Linux(十二):守护进程为什么fork两次?

守护进程也称精灵进程( Daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。

Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直在运行着。这种进程有一个名称叫守护进程(Daemon)。例如:udevd负责维护/dev目录下的设备文件,acpid负责电源管理,syslogd负责维护/var/log下的日志文件,可以看出守护进程通常采用以d结尾的名字,表示Daemon。

精灵进程特点:自成进程组,自成会话,和终端无关(无数据交流),即tty为?。不能进行前后台控制,因为前后台是相对于终端来说,会话由前台进程和后台进程组成,而精灵进程自成会话。

而后台进程受用户登录终端影响。

精灵进程作用:提供服务。eg:内核线程:完成操作系统级别服务.

注销时:kill当前所有进程,精灵进程不受影响。

可以使用 ps axj查看系统中的进程:

Linux(十二):守护进程为什么fork两次?_第1张图片

其中凡是TPGID标号为-1均为守护进程。

如何创建守护进程?


创建守护进程最关键的一步是调用setsid函数创建一个新的session,并成为session leader

调用这个函数之前,当前进程不允许是进程组的Leader,否则该函数返回-1。要保证当前进程不是进 程组的Leader也很容易,只要先

fork再调用setsid就行了。 fork创建的父进程和子进程在同一个进程组中,进程组的Leader必然是该组的第一个进程,所以子进程不可

能是该组的第一个进程,在子进程中调用setsid就不会有问题了。

成功调用该函数的结果是:
1. 创建一个新的Session,当前进程成为Session Leader,当前进程的id就是Session的id。

2. 创建一个新的进程组,当前进程成为进程组的Leader,当前进程的id就是进程组的id。
3. 如果当前进程原本有一个控制终端,则它失去这个控制终端,成为一个没有控制终端的进程。所谓失去控制终端是指,原来的控制终

端仍然是打开的,仍然可以读写,但只是一个普通的打开文件而不是控制终端了。

具体实现步骤:

1. 调用umask将文件模式创建屏蔽字设置为0.
2. 调用fork,父进程退出( exit) 。

原因:1.如果该守护进程是作为一条简单的shell命令启动的,那么父进程终止使得shell认为该命令已经执行完毕。2.保证子进程

不是一个进程组的组长进程。
3. 调用setsid创建一个新会话。 
4. 将当前工作目录更改为根目录。

5.关闭文件描述符

6.忽略SIGCHLD信号

更改工作目录函数调用:


实现代码:

#include
#include
#include
#include
#include
#include
void my_daemon()
{
	//1.首先设置权限
	umask(0);

	//2.创建父子进程
	pid_t id = fork();

	if(id < 0)
	{//father
		exit(0);
	}
	else
	{//child
	}

	//setsid
	setsid();
	//更改目录为工作目录
	if(chdir("/") < 0)
	{	
		printf("chdir");
		return;
	}

	//关闭文件描述符
	close(0);
	close(1);
	close(2);
	//屏蔽SIGCHLD信号
	signal(SIGCHLD, SIG_IGN);
}
int main()
{
	my_daemon();

	while(1)
		;
}


当然也有很多人选择fork两次创建子进程

第一次fork的作用是让shell 认为本条命令 已经终止,不用挂在终端输入上。还有一个作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader). 此时父进程是进程组组长。

第2次fork不是必须的。也看到很多开源服务没有fork第二次。fork第二次主要目的是。防止进程再次打开一个控制终端。因为打开一个控制终端的前台条件是该进程必须是会话组长。再fork一次,子进程ID != sid(sid是进程父进程的sid)。所以也无法打开新的控制终端。

2次fork实现代码:

#include
#include
#include
#include
#include
#include
void my_daemon()
{
	//1.首先设置权限
	umask(0);

	//2.创建父子进程
	pid_t id = fork();

	if(id < 0)
	{//father
		exit(0);
	}
	else
	{//child
	}

	//setsid
	setsid();

	if((id = fork()) < 0)
	{//father
		exit(0);
	}
	else
	{//child
	}
	//更改目录为工作目录
	if(chdir("/") < 0)
	{	
		printf("chdir");
		return;
	}

	close(0);
	close(1);
	close(2);

	signal(SIGCHLD, SIG_IGN);
}
int main()
{
	my_daemon();

	while(1)
		;
}



你可能感兴趣的:(Linux)