守护进程Daemon
http://blog.kurukurumi.com原创,转载请注明出处。
E-Mail : hubenchang0515@outlook.com
守护进程是一种后台运行的进程,通常在操作系统启动时启动,操作系统退出时退出。
让进程在后台运行进仅需要fork一个子进程,并正常退出父进程(如果父进程没有退出,终端关闭等情况下父进程会向其所有子进程发送SIGHUP信号之后再退出,如果没有捕获或忽略SIGHUP,它通常会杀死子进程)。
但是这样的后台进程仍然能够收到同一进程组下其他进程(父进程fork出的其他进程或fork出父进程的进程等)的信号,并且后台进程仍然与控制终端关联。
守护进程通常不希望受到其他进程和控制终端的干扰,因此通常调用setsid来为子进程创建一个新的session,这样便会切断与原控制终端的关联并创建新的进程组。如此一来,就没有同组进程和控制终端能够干扰后台进程了。
但是调用setsid后,后台进程成为新session的首进程,具有打开控制终端的权限,为了阻止守护进程打开控制终端,通常再次fork一个子进程,并正常退出父进程,这样一来新的子进程不是会话的首进程,无法打开控制终端。
综上所述,创建一个守护进程通常需要以下步骤:
1、fork一个子进程,并正常退出父进程
2、调用setsid
3、再次fork一个子进程,并正常退出父进程
完成以上操作后,后台进程从最初的父进程继承来了工作目录及文件描述符等资源。这会导致即使不需要该工作目录,该目录也无法unmount;即使不需要这些文件描述符,它们也会一直占用资源。因此,守护进程通常还会进程以下操作:
4、调用chdir将工作目录设为"/"或其他合适的目录
5、关闭所有(不需要的)文件描述符
由于关闭了所有文件描述符,因此当守护进程打开文件时,会从文件描述法0开始使用,这会导致对STDIN_FILENO、STDOUT_FILENO和STDERR_FILENO读写数据时操作错误的目标,因此守护进程通常需要在关闭所有文件描述符后:
6、将0、1、2号文件描述符定向到"/dev/null"
/* Let code after invoke this function run as daemon * Return porcess id of daemon * Return 0 while failed before fork * Return -1 while failed after fork, now cannot print message in tty */ pid_t makeDaemon() { umask(0); /* Change working directory to '/' So file system can unmount. */ if(chdir("/") < 0) { return 0; } /* Get file descriptor limit */ struct rlimit rl; if(getrlimit(RLIMIT_NOFILE, &rl) < 0) { return 0; } /* Fork and exit parent process */ pid_t pid = fork(); if(pid < 0) { return 0; } else if(pid > 0) { exit(0); } /* son process now */ /* Become a session leader to lose controlling terminal */ if(setsid() < 0) { return -1; } /* Fork again to ban reopen controlling terminal */ pid = fork(); if(pid < 0) { return -1; } else if(pid > 0) { exit(0); } /* grandson process now */ /* close all file descriptor */ if(rl.rlim_max == RLIM_INFINITY) { rl.rlim_max = 1024; } for(unsigned int i = 0; i < rl.rlim_max; i++) { close(i); } /* reopen 0 1 2*/ int fd0 = open("/dev/null"); int fd1 = dup(fd0); int fd2 = dup(fd0); return getpid(); }
发表评论: