I used to confuse daemon processes with background tasks, but after reading the article later, I realized the difference between the two. I wrote this article to express my understanding of daemon processes.
1: What is a daemon?
The so-called daemon process is a long-running background service process of Linux. httpd, named, sshd and other services are run in the daemon process Daemon. Usually the service name ends with the letter d, which is the first letter of Daemon.
- No control terminal required (no interaction with users)
- Run in the background
- The life cycle is relatively long, usually with the system startup and shutdown
2: Necessity of daemon
Usually when we execute tasks, we execute in the foreground, occupying the current terminal, and we cannot operate at this time. Even if we add the & symbol and put the program in the background, the program is interrupted due to problems such as the terminal's network disconnection.
What you need to know is: on the current Linux, with the systemd service, this service management tool can facilitate programs we write in the background to run, and can even replace this daemon process. By writing the configuration file of the service, systemd can monitor our programs, which can run as the system starts, set startup conditions, and make them convenient.
3: Process Group
$ ps -o pid,pgid,ppid,comm | cat PID PGID PPID COMMAND 10179 10179 10177 bash 10263 10263 10179 ps 10264 10263 10179 cat
- bash: both process and process group IDs are 10179, and the parent process is actually sshd(10177)
- ps: Both process and process group IDs are 10263, and the parent process is bash(10179), because it is a command executed on the shell
- cat: The process group ID is the same as the process group ID of ps, and the parent process is also bash(10179)
4: Session Group
Multiple processes form a process group, while session groups are built by multiple process groups. The process group is also called a job. The session has a foreground job and a background job. A session can have a control terminal, which will be passed to the foreground process group when the control terminal has input and output, such as Ctrl + Z. The meaning of a session is that it can control multiple jobs through one terminal, operate one foreground, and run other backgrounds.
So how to write a daemon?
In fact, writing a daemon process is very simple, you only need to follow a few points.
1: Create a child process, and the parent process exits
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 0 49 49 49 pts/2 70 Ss 0 0:00 /bin/bash 49 70 70 49 pts/2 70 R+ 0 0:00 \_ ps axjf 0 17 17 17 pts/1 68 Ss 0 0:00 /bin/bash 17 68 68 17 pts/1 68 S+ 0 0:00 \_ python 68 69 68 17 pts/1 68 S+ 0 0:00 \_ python 0 1 1 1 pts/0 1 Ss+ 0 0:00 /bin/bash
After the process is fork, the parent process exits. There are 2 reasons for doing this:
If the daemon is started through the shell and the parent process exits, the shell will think that the task has been executed, and the child process is adopted by init
The child process inherits the process group ID of the parent process, ensuring that the child process is not the leader of the process group, because the subsequent call to setsid() requires that it is not the leader of the process group.
PGID is the PID of the Leader of the Group to which the process belongs. If PGID=PID, then the process is the Group Leader
2. Subprocess creates a new session
Call setsid() to create a new session and become the leader of the new session. This step is mainly to be separated from the sessions, process groups, and terminals that inherit the parent process.
So the question is, why can't the process group leader call setsid()?
For the process leader, the process group ID is already the same as the PID. If it is allowed to call setsid(), its process group ID will remain the same and will appear:
1: The process leader belongs to a new session;
2: The old process group members belong to the old session.
This situation becomes that members of a process group belong to different sessions, and Linux wants to prohibit this situation from happening.
3. Forbid the child process to reopen the terminal
At this moment, the child process is the session group leader. In order to prevent the child process from reopening the terminal, it forks again and exits the parent process, which is the child process. At this time, child process 2 is no longer the session group leader and the terminal cannot be opened. In fact, this step is not necessary, but adding this step will make it more rigorous.
4. Set the current directory as the root directory
If the daemon's current working directory is the /usr/home directory, the administrator will report an error when uninstalling the /usr partition. To avoid this problem, you can call the chdir() function to set the working directory to the root directory /.
5. Set file permission mask
File permission mask refers to blocking the corresponding bits in file permissions. Since the child process newly created using the fork() function inherits the file permission mask of the parent process, this brings a lot of trouble to the child process using files. Therefore, setting the file permission mask to 0 can greatly enhance the flexibility of the daemon. The usual method is umask(0).
6. Close the file descriptor
The child processes inherit the opened files, which occupy system resources and may cause the file system to be uninstalled. At this time, the daemon process is separated from the terminal, and the commonly-called input, output, and error descriptors should also be closed. After all, the terminal will not be used at this time.
Daemon error handling
Since the daemon process leaves the terminal, the error message cannot be output to the control terminal, and even gdb cannot be debugged normally. A common method is to use the syslog service to enter error messages into /var/log/messages.
syslog is a system log management service in Linux and is maintained through the daemon syslogd. The daemon will read a configuration file /etc/ when it starts. This file determines where different kinds of messages will be sent.
Code display
import os import sys def daemonize(pid_file=None): pid = () if pid: (0) () _pid = () if _pid: (0) (0) ('/') () () with open('/dev/null') as read_null, open('/dev/null','w') as write_null: os.dup2(read_null.fileno(), ()) os.dup2(write_null.fileno(), ()) os.dup2(write_null.fileno(), ()) if pid_file: with open(pid_file,'w+') as f: (str(())) if __name__ == "__main__": daemonize('')
About the function os.dup2
The os.dup2() method is used to copy one file descriptor fd to another fd2.
Available on Unix, Windows.
>>> import os >>> f = open("","a") >>> os.dup2((),1) >>> () >>> print("hello world") >>> print("changed") cat 1 hello world changed
Additional topics
Why do server-side often fork twice?
Because this is to avoid zombie processes.
When we only fork() once, the parent and child processes exist. There are two ways to avoid zombie processes:
- The parent process calls functions such as waitpid() to receive the child process exit status.
- The parent process ends first, and the child process is automatically hosted to the Init process (pid = 1).
Currently, consider the situation where the child process ends before the parent process:
- If the parent process does not handle the child process exit status, the child process is in the zombie process state before the parent process exits.
- If the parent process calls waitpid() (the blocking call is used here to ensure that the child process ends before the parent process) to wait for the child process to end, the parent process will enter a sleep state after calling waitpid(), and the waitpid() of the child process will return only. If there is a situation where the child process ends but the parent process has not yet executed waitpid(), then the child process will also be in the zombie process state during this period.
From this, it can be seen that the parent process has a parent-son relationship with the child process. Unless the parent process is guaranteed to end before the child process or the parent process executes waitpid() before the child process ends, the child process has the chance to become a zombie process. So how can we make the parent process more convenient to create child processes that will not become zombie processes? This requires two times fork().
After the parent process is fork() once, a child process is generated, and then immediately executes waitpid (child process pid, NULL, 0) to wait for the child process to end, and then the child process is fork() and then exit(0) immediately. In this way, the child process terminates successfully (the parent process only collects the child process's body and does not require the child process's return value), and the parent process continues to execute. At this time, the grandson process will be handed over to the Init process for hosting because it loses its parent process (that is, the child process of the parent process). Therefore, the parent process has no inheritance relationship with the grandson process. Their parent processes are both Init. The Init process will automatically collect the corpse when its child process ends, so that no zombie process will be generated.
The above is the detailed content of how to write python daemon programs. For more information about python daemon programs, please follow my other related articles!