Double fork and setsid

There are a couple of methods to daemonize a process, maybe less popular but really interesting ones; and these are the double fork and setsid.

Double fork is the way a process is usually daemonized and implies a fork, a duplication of the parent process to create a child one. In the case of double forking applied to daemonization, the parent process forks off a child process, then terminates it. Then, the child process forks its own child process and terminates. So, at the end of the chain, the two parent processes die and only the grandchild is alive and running but as a daemon. The reason for this resides in how a controlling terminal for a session is allocated since the child processes that are forked inherit the controlling terminal from their parent process.

In an interactive session, the shell is the first processed to be executed, so it is the controlling process for the terminal and the session leader from which all forked processes in the session inherit their controlling terminal. Forking and killing the parent processes gives us an orphan process, which is automatically reparented to init, so it becomes the child of the main process of the system. All of this is to prevent the child process from being a session leader and acquiring a controlling terminal; and this is the reason why we double fork and kill the parent twice: we want to make the child process an orphan so that the system, to prevent it from becoming a zombie process, will reparent it to init. Since it is is not the first process in its pipeline, it cannot become a session leader and acquire the controlling terminal. So, the child process is then moved to a different session and has no hold on the controlling terminal, going effectively daemon.

Let's have a look and start our script in the background:

zarrelli:~$ ./while.sh &
[1] 17460

And have a look at the IDs:

zarrelli:~$ ps -Ho pid,ppid,pgid,tpgid,sess,args
PID PPID PGID TPGID SESS COMMAND
10355 1401 10355 17515 10355 /bin/bash
17460 10355 17460 17515 10355 /bin/bash ./while.sh
17515 10355 17515 17515 10355 ps -Ho pid,ppid,pgid,tpgid,sess,args

The session ID is the same as the shell from which it forked, but it has its own process group ID and the Parent Process ID (PPID) equal to its parent process ID. Let's see where the script places itself in the process tree:

zarrelli:~$ pstree | grep -B3 while
| `-{gmain}
|-login---bash-+-grep
| |-pstree
| `-while.sh

As expected, it is nested inside the login session, so it is part of this session. Now, let's double fork:

zarrelli:~$ (./while.sh &) &
[1] 17846

Have a look at the process:

zarrelli:~$ ps -Ho pid,ppid,pgid,tpgid,sess,args
PID PPID PGID TPGID SESS COMMAND
10355 1401 10355 17970 10355 /bin/bash
17970 10355 17970 17970 10355 ps -Ho pid,ppid,pgid,tpgid,sess,args
17847 1 17846 17970 10355 /bin/bash ./while.sh

The PPID of the shell executing while is now really interesting; it took the value of 1. This means that his parent process is no longer the shell spawned at the login session but the init process. But notice, it still shares the same session ID and the same terminal. We can double-check with pstree:

zarrelli:~$ pstree | grep -B3 while
| `-{probing-thread}
|-upowerd-+-{gdbus}
| `-{gmain}
|-while.sh

We do not have any nesting since we are directly reparented at the first level to init.

With setsid, we get a slightly different outcome. Whenever a process which is not the process group leader calls setsid , this creates a new session and makes the calling process the session leader, the process group leader of a newly created process group, and deprives it of a controlling terminal. So, we essentially come up with a new session that holds a new process group and only one process, the calling process. Both the session and process group ID are set to the calling process ID. We want to daemonize a process but there is a drawback, we do not have any output unless we redirect to a file:

setsid command > file.log

Let's demonize our script:

zarrelli:~$ setsid ./while.sh
zarrelli:~$ ps -e -Ho pid,ppid,pgid,tpgid,sess,args | grep while
22853 10355 22852 22852 10355 grep while
22572 1 22572 -1 22572 /bin/bash ./while.sh

This time, we had to use the -e option of ps to show all the processes and the grep to while, because ps, by default, shows only the processes with the same effect user ID as the current user and with the same terminal. In this case, we changed the terminal, so it would not show up. Finally, let's have a look at pstree:

zarrelli:~$ pstree | grep -B3 while
| `-{probing-thread}
|-upowerd-+-{gdbus}
| `-{gmain}
|-while.sh

As we would expect, since the PPID is 1, we see a nesting on the first level. The process, in our case the shell, executing the script is reparented to init without any controlling terminal.

Now that we have examined a few methods on how to effectively put a process in the background and shield it from a session closure, we can proceed further, having a look at how we can actually write scripts that demonize themselves, going in the background and working without user interaction. Well, there would be some workaround such as using utilities: a screen and a terminal multiplexer, which allow you to detach a session from a terminal so that the process can keep running even if user logs out. Anyway, this is not our goal, we are not reviewing external tools but trying to sort out the best from our Bash, so the next paragraph will dwell a bit on the different methods to have Bash to demonize our scripts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.63.136