5

我知道 os.setsid() 是将进程(分叉)组 ID 更改为自身,但为什么我们需要它呢?

我可以从谷歌看到一些答案是:在父进程退出时保持子进程运行。

但是根据我下面的测试,如果没有 os.setsid() ,即使父进程退出(或被杀死),子进程也不会退出。那么为什么我们需要添加 os.setsid() 呢?谢谢。

import os
import time
import sys

mainPid = os.getpid()
print("Main Pid: %s" % mainPid)

pid = os.fork()

if pid > 0:
    time.sleep(3)
    print("Main process quit")
    sys.exit(0)

#os.setsid()

for x in range(1, 10):
    print("spid: %s, ppid: %s pgid: %s" % (os.getpid(), os.getppid(), os.getpgid(0)))
    time.sleep(1)
4

3 回答 3

4

调用setsid通常是进程成为所谓的守护进程时所经历的步骤之一。(我们谈论的是 Linux/Unix 操作系统)。

setsid控制终端的关联中断。这意味着该过程不会受到注销的影响。

还有其他方法可以在注销后幸存下来,但是这种“守护进程”的目的是创建一个尽可能独立于外部世界的后台进程。

这就是为什么所有继承的描述符都是关闭的;cwd 设置为适当的目录,通常是根目录;并且该过程离开它开始的会话。

fork通常建议使用双重方法。每次fork父母退出,孩子继续。实际上除了 PID 没有任何变化,但这正是这里所需要的。

首先forksetsid确保流程不是流程组负责人之前。这是成功所必需的setsid

fork之后的第二个setsid确保不会仅通过打开终端设备来启动与控制终端的新关联。


注意:从 启动守护进程时systemdsystemd可以安排上述所有内容,因此该进程不必这样做。

于 2017-08-28T06:33:46.680 回答
1

好吧,双叉守护进程就是一个很好的例子。但是,最好了解什么是进程组和会话

  • 会话 ID (SID)

这只是会话负责人的 PID。如果 PID == SID,则此进程是会话领导者。

会话和进程组只是将许多相关进程视为一个单元的方法。一个进程组的所有成员总是属于同一个会话,但一个会话可能有多个进程组。

通常,shell 将是会话领导者,并且由该 shell 执行的每个管道都将是一个进程组。这是为了在 shell 退出时更容易杀死 shell 的子代。(有关血腥细节,请参见出口(3)。)

基本上,如果你登录到一台机器,你的 shell 就会启动一个会话。如果您想在注销时保持进程运行,您应该为孩子启动一个新会话。

double forked 进程的区别在于您仍然可以将控制终端附加到该进程,因为它是会话领导者,而由创建的守护进程double fork不能再附加到终端。

于 2020-07-20T16:46:51.430 回答
0

在某些情况下,子进程即使在父进程退出后也能继续运行,但这并不是万无一失的。在某些情况下,当父母退出时,孩子也会退出。

从 Python 3.2 开始,您可以使用 subprocess.Popen() 并传递 start_new_session=True 来完成子进程与父进程的完全分离。

文档状态:

如果 start_new_session 为真,则将在子进程执行之前在子进程中进行 setsid() 系统调用。(仅限 POSIX)

https://docs.python.org/3/library/subprocess.html#subprocess.Popen

于 2020-02-06T21:38:46.547 回答