I had once to run a script which continuously checks for some stuff during the whole system lifetime. Sometimes, inotify is not enough, so I wrote a basic python script to create a daemon doing the job.
1 2 3 4 5 6 7 8 9 10 11 12 13
At first, I didn’t pay too much attention to the double fork stuff. All I cared was running this function on background. And until I understand it, I was always wondering whether or not this script was efficient.
To provide some explication, I have first to explain what a zombie process is. Every process terminates with a return-code. And only the process responsible for its creation, its father-process, has access to this return-code when its child dies. A process becomes a zombie when it terminates, but init can’t get its return-code. Init can’t know for sure whether or not the process is dead, because, even if it looks like dead, well, it does not return anything.
Now, the father creates its child, and this creation is not blocking (well, I could say: per definition; this is the very purpose of forking). Which means, the father won’t wait for its child to die, but will perform its own tasks and so does its child. And when the child has finished with them, its returns its return-code to its father. But, remember? Only the father knows it, and init won’t. So, at this time, and until the father itself dies, the child turns into a zombie for init.
So, was my script valid? Well, once created, the child waits (and this wait is blocking) for 5 seconds. So, the child has at least 5 seconds of lifetime. And the father exits (os.fork() == 0 for the father), leaving its child orphean. As consequence, init rechild the child process and will then get its status-code once its destiny is complete. I don’t create any zombie here.
To conclude with, single-forking is enough when the father dies in short notice after child creation.
Writing a Zombie on purpose
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
- A first, everything goes well and daemon writes stuff into logfile.
- When i=15 daemon dies. But father remains and init can’t get child’s exist status.
- Output of ps aufx | grep python command:
1 2 3
RSS and VSZ turns to 0 (no memory is getting used)
When the father still has stuff to do
Indeed, things are getting more complicated when the father as other tasks to perform. Because the father won’t let init rechild its child until its dies. In this case, the solution is to fork twice: – First the father forks – Then the child forks and dies . Once again, forking is not blocking, it dies immediately afte forking – The grand-child is orphean as soon as it is born; So immediately rechild by init – Father and grand-child may now die safely.
Here is the code showing how to do this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Is there anything left?
- Well, first of all, it’s safer to check for the success or failure of the os.fork system call. Because it may simply throw exceptions (the maximum number of PID may be reached for instance).
- Then, we must ensure the child does not inherit its father environment, such as working directory (The working directory may be a mounted filesystem, and the running daemon inside may prevent the system to umount the filesystem at shutdown time), umask (A child process created via fork(2) inherits its parent’s umask man says) and create a new session with os.setsid().
- _exit instead of exit
This version is for most parts inspired from this blog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
Here are a few sources which provided great help understanding the principe and creating this article: