守护进程Daemon

2017-9-19 Plan C Linux

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_FILENOSTDOUT_FILENOSTDERR_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();
}


发表评论:

Powered by emlog
鄂ICP备16003833号