为什么在 setsid() 之前要调用 fork()?

42

为什么在使用 setsid() 将进程变为守护进程之前需要使用 fork()

基本上,如果我想将一个进程与其控制终端分离并使其成为进程组领导者:我会使用 setsid()

如果在没有使用 fork() 的情况下执行此操作,则无法正常工作。

为什么呢?

3个回答

57

首先: setsid()将使您的进程成为进程组领导者,但也将使您成为新会话的领导者。如果您只对获取自己的进程组感兴趣,则使用setpgid(0,0)。

现在,要理解为什么如果您已经是进程组领导者或会话领导者,则setsid()会返回EPERM,您必须了解进程组和会话ID是从创建它们(因此领导它们)的进程的进程ID初始化的(即对于会话领导者pid == sid,而对于进程组领导者pid == pgid)。此外,进程组无法在会话之间移动。

这意味着,如果您是进程组领导者,并且允许创建新会话,则sid和pgid将设置为您的pid,使旧进程组中的其他进程处于奇怪的状态:他们的进程组领导者突然处于不同的会话中,他们自己可能也是如此。这是不允许的,因此内核会返回EPERM错误。

现在,如果您fork()一次,则既不是会话领导者也不是进程组领导者,因此将您的sid和pgid设置为您的pid是安全的,因为在这样的组中没有其他进程。

所以,是的,想一想,这都有道理。


1
请问您能否提供一些参考文档,解释什么是进程组组长,并且为什么这很重要。 - valentt
1
如果允许进程组领导调用 setsid() 函数创建一个新会话和新进程组,则可能导致进程组 ID 冲突。 - zeekvfu

20

必须要使用 fork() 并且让子进程调用 setsid() 来确保调用 setsid() 的进程不是一个已经是进程组的领导者(setsid() 想要将其调用者设为一个新进程组的进程组领导者,因此在那种情况下会失败)。


2
当你使用man 2 setsid命令时,你会得到以下描述:
setsid()函数会在调用进程不是进程组长的情况下创建一个新的会话。调用进程将成为新会话的领导者、新进程组的进程组领导者并且没有控制终端。调用进程的进程组ID和会话ID被设置为调用进程的PID。该调用进程将是这个新进程组与新会话中唯一的进程。
如果进程组长可以调用setsid()函数,创建一个新的会话并一个新的进程组(带有相同的进程组ID),这将导致进程组ID冲突。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接