POSIX 进程组
我目前正在将进程组实现到我的操作系统项目的 POSIX 子系统中。 但是,我对 POSIX 规范 (setsid)(以及维基百科有关进程组的页面)。
我们的终端层向前台进程(组,其 id 应等于组领导者的 PID)发送 SIGINT。 在这种情况下,前台进程(我们的“登录”应用程序)通过调用 setsid
成为组领导者。 当用户登录时,程序会分叉并执行用户的 shell。 在这个阶段,我的理解是,在调用 exec*
之前,我会从分叉的子进程中调用 setpgid
。 这意味着执行的程序从一开始就是进程组的一部分。
如果我想在进程组之外运行新分叉的子进程,我只需在调用 exec*
之前在分叉的子进程中调用 setsid
即可。
它是否正确? 有什么我应该检查或做的真正晦涩的事情吗?
作为一个后续问题,我相信我已经知道了,分叉是否需要转移组成员资格? 或者是否必须在每次 fork
调用后使用 setpgid
来完成? 我收集到进程组是通过 fork
从 fork
的 POSIX 定义传输的。
提前致谢。
I'm currently implementing process groups into my operating system project's POSIX subsystem. However, I've become a little confused at the POSIX specification (setsid) (along by Wikipedia's page on Process groups).
Our terminal layer sends SIGINT to the foreground process (group, whose id should equal the group leader's PID). In this case, that foreground process (our "login" application) becomes a group leader by calling setsid
. When the user logs in, the program forks and executes the user's shell. At this stage, my understanding is that I call setpgid
from the forked child before calling exec*
. This means the executed program will be a part of the process group from the outset.
If I wanted to run the newly forked child outside the process group I would merely call setsid
in the forked child before calling exec*
.
Is this correct? Are there any really obscure things I should be checking or doing?
As a follow-on question, which I believe I already know, is it a requirement for fork
to transfer group membership? Or is it something that must be done using setpgid
after every fork
call? I gather process groups are transferred by fork
from the POSIX definition of fork
.
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
有趣的问题——尤其是因为它在很长一段时间内都没有得到部分答案。
POSIX 基本定义
POSIX 定义部分的一些引用:
POSIX 系统接口
和:
解释
正如定义所明确的,一个会话可能由多个进程组组成。 在广泛的限制内,进程可以更改进程组(尽管它在任何时候只属于一个进程组)。 会话处理的选项更加有限; 基本上,进程要么仍然是其原始会话的成员,要么可以使自己成为新会话的领导者。
复制问题的部分内容:
我怀疑括号应该是“前台进程组(其 id 应等于组长的 PID)”。 根据定义(3.292),进程组领导者是 PID 与进程组 ID 相同的进程。 我没有引用相关资料,但我相信将信号分派给进程组领导者是正确的
注意,前台进程通过调用
setsid()
成为会话领导者,同时也成为进程组领导也是。 我希望登录程序在分叉之后但在执行 shell 之前将用户的 shell 设置为进程组领导者(也可能是会话领导者)。 所有子进程自动继承父进程的进程组和会话; 如果你希望它有所不同,你必须覆盖它。您可以这样做,但它也会创建一个新会话。 您可能想要使用
setpgid()
(现代标准;可能是setpgrp()
这是 SVID 的旧标准)而不是setsid()
。是的,这基本上是正确的。 是的,可能还有一些晦涩的事情需要跟踪。 例如,您可能需要考虑控制 TTY。
fork()
之后的子进程属于同一组组(如/etc/group
中),并且也属于同一会话和同一进程组 - 但是它不是会话领导者,也不是进程组领导者。Interesting question - not least because it stayed without even a partial answer for so long.
POSIX Base Definitions
Some quotes from the definitions part of POSIX:
POSIX System Interfaces
And:
Interpretation
As the definitions make clear, a session may consist of multiple process groups. Within broad limits, a process may change process groups (though it belongs to just one process group at any time). The options for session handling are more limited; basically, a process either remains a member of its original session, or it can make itself the leader of a new session.
Copying parts of the question:
I suspect the parentheses should be 'the foreground process group (whose id should equal the group leader's PID)'. By definition (3.292), the process group leader is the process whose PID is the same as the process group ID. I haven't quote the relevant material, but I believe that dispatching the signal to the process group leader is correct
Note that the foreground process becomes a session leader by calling
setsid()
and also becomes the process group leader too. I would expect that the login program would set up the user's shell as a process group leader (and probably a session leader) after forking but before executing the shell. All child processes inherit process group and session from their parent processes automatically; you have to override that if you want it to be different.You could do that, but it would also create a new session. You probably want to use
setpgid()
(modern standard; possiblysetpgrp()
which is an older standard from SVID) rather thansetsid()
.Yes, this is mostly correct. Yes, there probably are some obscure things to keep track of too. For example, you might need to think about the controlling TTY.
The child process after a
fork()
belongs to the same set of groups (as in/etc/group
), and also to the same session and same process group - but it is not a session leader nor is it a process group leader.setpgid
POSIX C 进程组最小示例我相信使用基本 API 通常是学习新概念的最佳方法,所以让我们尝试一下。
这说明了如果子进程没有使用
setpgid
更改其进程组,信号如何发送到子进程。main.c
GitHub 上游。
编译:
不使用
setpgid
运行如果没有任何 CLI 参数,
setpgid
未完成:可能的结果:
程序挂起。
正如我们所看到的,两个进程的 pgid 是相同的,因为它是通过
fork
继承的。然后,每当您点击:
它再次输出:
这显示了如何:
kill(-pgid, SIGINT)
向整个进程组发送信号通过向两个进程发送不同的信号来退出程序,例如使用
Ctrl + \
发送 SIGQUIT。使用
setpgid 运行
如果您使用参数运行,例如:
那么子级会更改其 pgid,现在每次仅从父级打印一个 sigint:
现在,每当你点击:
只有父进程也接收到信号:
你仍然可以像以前一样用 SIGQUIT 杀死父进程:
但是子进程现在有一个不同的 PGID,并且不会接收到该信号! 这可以从以下内容中看出:
您必须使用以下命令显式终止它:
这清楚地表明了信号组存在的原因:否则我们将一直留下一堆需要手动清理的进程。
在 Ubuntu 18.04 上测试。
setpgid
POSIX C process group minimal exampleI believe that playing around with the base APIs is often the best way to learn new concepts, so let's give that a try.
This illustrates how the signal does get sent to the child, if the child didn't change its process group with
setpgid
.main.c
GitHub upstream.
Compile with:
Run without
setpgid
Without any CLI arguments,
setpgid
is not done:Possible outcome:
and the program hangs.
As we can see, the pgid of both processes is the same, as it gets inherited across
fork
.Then whenever you hit:
It outputs again:
This shows how:
kill(-pgid, SIGINT)
Quit the program by sending a different signal to both processes, e.g. SIGQUIT with
Ctrl + \
.Run with
setpgid
If you run with an argument, e.g.:
then the child changes its pgid, and now only a single sigint gets printed every time from the parent only:
And now, whenever you hit:
only the parent receives the signal as well:
You can still kill the parent as before with a SIGQUIT:
however the child now has a different PGID, and does not receive that signal! This can seen from:
You will have to kill it explicitly with:
This makes it clear why signal groups exist: otherwise we would get a bunch of processes left over to be cleaned manually all the time.
Tested on Ubuntu 18.04.