守护进程
守护进程是一种后台运行并且独立于所有终端控制之外的进程。
为什么需要有独立于终端之外的进程呢?首先,处于安全性的考虑我们不希望这些进程在执行中的信息在任何一个终端上显示。其次,我们也不希望这些进程被终端所产生的中断信号所打断。最后,虽然我们可以通过&将程序转为后台执行,我们有时也会需要程序能够自动将其转入后台执行。因此,我们需要守护进程。
守护进程的启动
要启动一个守护进程,可以采取以下的几种方式:
1.在系统期间通过系统的初始化脚本启动守护进程。这些脚本通常在目录 etc/rc.d 下,通过它们所启动的守护进程具有超级用户的权限。系统的一些基本服务程序通常都是通过这种方式启动的。
2.很多网络服务程序是由 inetd 守护程序启动的。在后面的章节中我们还会讲到它。它监听各种网络请求,如 telnet、ftp 等,在请求到达时启动相应的服务器程序(telnet server、ftp server 等)
3.由 cron 定时启动的处理程序。这些程序在运行时实际上也是一个守护进程。
4.由 at 启动的处理程序。
5.守护程序也可以从终端启动,通常这种方式只用于守护进程的测试,或者是重起因某种原因而停止的进程。
6.在终端上用 nohup 启动的进程。用这种方法可以把所有的程序都变为守护进程
编写守护进程的步骤
1. fork
需要fork一个子进程并将父进程关闭。如果进程是作为一个 shell 命令在命令行上前台启动的,当父进程终止时,shell 就认为该命令已经结束。这样子进程就自动称为了后台进程。而且,子进程从父进程那里继承了组标识符同时又拥有了自己的进程标识符,这样保证了子进程不会是一个进程组的首进程。这一点是下一步 setsid 所必须的。
2. setsid
setsid调用创建了一个新的进程组,调用进程成为了该进程组的首进程。这样,就使该进程脱离了原来的终端,成为了独立于终端外的进程。
3. 忽略SIGHUP信号,重新fork
这样使进程不在是进程组的首进程,可以防止在某些情况下进程意外的打开终端而重新与终端发生联系。
4. 改变工作目录,清除文件掩码
改变工作目录主要是为了切断进程与原有文件系统的联系。并且保证无论从什么地方启动进程都能正常的工作。清除文件掩码是为了消除进程自身掩码对其创建文件的影响。
(file umask:当最初登录到系统中时,umask命令确定了你创建文件的缺省模式。这一命令实际上和chmod命令正好相反。你的系统管理员必须要为你设置一个合理的umask值,以确保你创建的文件具有所希望的缺省权限,防止其他非同组用户对你的文件具有写权限。)
5. 关闭全部已打开的文件句柄
这是为了防止子进程继承了在父进程中打开的文件而使这些文件始终保持打开从而产生某些冲突。
创建一个守护进程 mydaemon.c
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <syslog.h> #include <sys/types.h> #include <fcntl.h> #include <sys/param.h> #include <stdlib.h> void daemon_init() { int i, fd; pid_t pid; int maxfd; //fork ,disconnected with parent if((pid=fork())) { exit(0); } else if(pid<0) exit(1); //first child process, disconnected with process team //create a new session setsid(); //fork to end first child process, diconnected with console if((pid=fork())) { exit(0); } else if(pid<0) exit(1); //change directory, disconnected with source's file system chdir("/"); //remove file umask umask(0); //close all file description maxfd = sysconf(_SC_OPEN_MAX); for(i=0; i<maxfd; i++) { close(i); } //redirect stardard input, output, error if((fd=open("/dev/null", O_RDWR))==-1) exit(-1); dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); } int main(void) { FILE *fp=NULL; int t=0; //ignore SIGHUP signal(SIGHUP, SIG_IGN); daemon_init(); fp=fopen("/root/tmp.txt", "a"); fprintf(fp, "%s ", "test message"); fflush(fp); while(1) { fprintf(fp, "%d ", t); fflush(fp); sleep(2); t += 2; } fclose(fp); return 0; }
运行截图