os/unix/ngx_daemon.c源代码分析
本节我们主要介绍一下nginx中ngx_daemon.c将进程后台化,变成守护进程的实现。
1. os/unix/ngx_daemon.c源文件
源文件内容如下:
这里ngx_daemon()较为简单,主要有如下步骤:
-
调用fork()产生子进程,同时父进程退出
-
调用setsid()产生一个新的会话
-
调用umask()将文件模式创建屏蔽字设置为一个已知值
-
将标准输入、标准输出attach到/dev/null中
-
关闭打开的/dev/null文件句柄fd
说明: 1. 此处通过ngx_pid = ngx_getpid();将子进程的进程ID保存到了全局变量ngx_pid中 2. 这里并未显示的将标准错误attach到/dev/null中,因此在一般情况下标准错误输出仍可以输 出到控制台(除非打开的/dev/null句柄fd恰巧等于STDERR_FILENO)。
2. Unix高级环境编程–守护进程
在编写守护进程程序时需遵循一些基本规则,以防止产生不必要的交互作用。下面先说明这些规则,然后给出一个按照这些规则编写的函数daemonize。
(1) 首先要做的是调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0)。由继承得来的文件模式创建屏蔽字可能会被设置为拒绝某些权限。如果守护进程要创建文件,那么它可能要设置特定的权限。例如,守护进程要创建组可读、组可写的文件,继承的文件模式创建屏蔽字可能会屏蔽上述两种权限中的一种,而使其无法发挥作用。另一方面,如果守护进程调用的库函数创建了文件,那么将文件模式创建屏蔽字设置为一个限制性更强的值(如007)可能会更明智,因为库函数可能不允许调用者通过一个显示的函数参数来设置权限。
(2) 调用fork,然后使父进程exit。这样做实现了下面几点。第一,如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止会让shell认为这条命令已经执行完毕。 第二,虽然子进程继承了父进程的进程组ID,但获得了一个新的进程ID,这就保证了子进程不是一个进程组的组长进程。这是下面要进行的setsid调用的先决条件。
(3) 调用setsid创建一个新会话。使调用进程:
-
成为新会话的首进程
-
成为一个新进程组的组长进程
-
没有控制终端
在基于System V的系统中,有些人建议在此时再调用一次fork,终止父进程,继续使用子进程中的守护进程。 这就保证了该守护进程不是会话首进程,于是按照System V规则可以防止它取得控制终端。为了避免取得控制 终端的另一种方法是,无论何时打开一个终端设备,都一定要指定O_NOCTTY。
(4) 将当前工作目录更改为根目录。从父进程处继承过来的当前工作目录可能在一个挂载的文件系统中。因为守护进程通常在系统再引导之前是一直存在的,所以如果守护进程的当前工作目录在一个挂载文件系统中,那么该文件系统就不能被卸载。
或者,某些守护进程还可能会把当前工作目录更改到某个指定位置,并在此位置进行它们的全部工作。例如,行式打印机假脱机守护进程可能将其工作目录更改到它们的spool目录上。
(5) 关闭不再需要的文件描述符。这使守护进程不再持有从其父进程继承来的任何文件描述符(父进程可能是shell进程,或是某个其他进程)。可以使用open_max函数或getrlimit函数来判定最高文件描述符值,并关闭直到该值的所有描述符。
(6) 某些守护进程打开/dev/null使其具有文件描述符0,1和2,这样任何一个试图读标准输入、写标准输出或标准错误的库例程都不会产生任何效果。因为守护进程并不与终端设备相关联,所以其输出无处显示,也无处从交互式用户那里接收输入。即使守护进程是从交互式会话启动的,但是守护进程是在后台运行的,所以登录会话的终止并不影响守护进程。如果其他用户在同一终端设备上登录,我们不希望在该终端上见到守护进程的输出,用户也不期望他们在终端上的输入被守护进程读取。
如下给出一个例子(test.c):
编译运行:
[root@localhost test-src]# gcc -o test test.c [root@localhost test-src]# ./test [root@localhost test-src]# pgrep -lf test 101892 test [root@localhost test-src]# echo $? 0 [root@localhost test-src]# tail -f /var/log/messages Nov 14 18:42:22 localhost ./test: ./test running daemonized Nov 14 18:42:23 localhost ./test: ./test running daemonized Nov 14 18:42:24 localhost ./test: ./test running daemonized Nov 14 18:42:25 localhost ./test: ./test running daemonized Nov 14 18:42:26 localhost ./test: ./test running daemonized Nov 14 18:42:27 localhost ./test: ./test running daemonized Nov 14 18:42:28 localhost ./test: ./test running daemonized Nov 14 18:42:29 localhost ./test: ./test running daemonized Nov 14 18:42:30 localhost ./test: ./test running daemonized Nov 14 18:42:31 localhost ./test: ./test running daemonized Nov 14 18:42:32 localhost ./test: ./test running daemonized [root@localhost test-src]# pgrep -lf test | awk '{print $1}' | xargs kill -9