我正在学习 Linux 中进程、进程组(和会话)之间的关系。
我编译了以下程序...
#include <iostream>
#include <ctime>
#include <unistd.h>
int main( int argc, char* argv[] )
{
char buf[128];
time_t now;
struct tm* tm_now;
while ( true )
{
time( &now );
tm_now = localtime( &now );
strftime( buf, sizeof(buf), "%a, %d %b %Y %T %z", tm_now );
std::cout << buf << std::endl;
sleep(5);
}
return 0;
}
...并将a.out
其作为后台进程运行,就像这样...
a.out &
这个网站说以下...
每个进程都是唯一进程组的成员,由其进程组 ID 标识。 (进程创建后,它就成为其父进程组的成员。)按照约定,进程组的进程组 ID 等于进程组的第一个成员(称为进程组领导者)的进程 ID。
根据我的阅读,第一句话与括号内的内容相冲突:is a process a member of a独特的进程组,或者是进程组的成员其父级的?
我试图调查ps
...
ps xao pid,ppid,pgid,sid,command | grep "PGID\|a.out"
PID PPID PGID SID COMMAND
24714 23890 24714 23890 ./a.out
这告诉我我的a.out
进程是 pid 24714
,从父 pid 产生23890
,并且是程序组的一部分24714
。首先,我不明白为什么这个pgid与pid相匹配。
接下来,我尝试调查父进程......
ps xao pid,ppid,pgid,sid,command | grep "PGID\|23890"
PID PPID PGID SID COMMAND
23890 11892 23890 23890 bash
24714 23890 24714 23890 ./a.out
我的父进程a.out
是,这对我来说是有意义的bash
。起初我以为“bash 的 pid 与其 pgid 相匹配 - 这一定是因为它是进程组领导者。也许这是有道理的,因为 bash 是运行的“第一件事”,我从中运行了我的流程。“但是这种推理没有意义,因为a.out
的 pgid 也与它自己的 pid 匹配。
a.out
为什么's pgid不等于bash
's pgid?根据我对这句话的理解,这就是我所期望的。
有人可以澄清 pids 和 pgids 之间的关系吗?
答案1
没有冲突;默认情况下,进程将位于唯一的进程组中,该进程组是其父进程组:
$ cat pg.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
fork();
printf("pid=%d pgid=%d\n", getpid(), getpgrp());
}
$ make pg
cc pg.c -o pg
$ ./pg
pid=12495 pgid=12495
pid=12496 pgid=12495
$
将fork
我们的进程分为父进程 ( 12495
) 和子进程 ( 12496
) ,子进程属于父进程 ( 12495
) 的唯一进程组。bash
与此不同,因为它发出额外的系统调用:
$ echo $$
12366
$
然后在另一个终端中运行:
$ strace -f -o blah -p 12366
然后回到第一个终端:
$ ./pg
pid=12676 pgid=12676
pid=12677 pgid=12676
$
然后我们检查系统调用control+c:strace
$ egrep 'exec|pgid' blah
12366 setpgid(12676, 12676) = 0
12676 setpgid(12676, 12676 <unfinished ...>
12676 <... setpgid resumed> ) = 0
12676 execve("./pg", ["./pg"], [/* 23 vars */]) = 0
12676 write(1, "pid=12676 pgid=12676\n", 21 <unfinished ...>
12677 write(1, "pid=12677 pgid=12676\n", 21 <unfinished ...>
bash
使用该setpgid
调用来设置进程组,从而将我们的pg
进程放入与 shell 无关的进程组中。 (setsid(2)
如果您正在寻找系统调用,这将是调整进程组的另一种方法。)
答案2
Bash 将您的程序放入其自己的进程组中,作为其作业控制处理的一部分。例如,来自 bash 联机帮助页:
为了便于实现作业控制的用户界面,操作系统维护当前终端进程组ID的概念。该进程组的成员(进程组 ID 等于当前终端进程组 ID 的进程)接收键盘生成的信号,例如 SIGINT。据说这些进程位于前台。后台进程是指进程组ID与终端进程组ID不同的进程;这些进程不受键盘生成的信号的影响。仅允许前台进程读取或写入终端(如果用户使用 stty tostop 指定)。内核的终端驱动程序会向尝试从终端读取(当 stty tostop 有效时写入)的后台进程发送 SIGTTIN (SIGTTOU) 信号,除非被捕获,否则该进程将挂起。
还有关于set -m
:
监控模式。作业控制已启用。对于支持它的系统上的交互式 shell,此选项默认处于启用状态(请参阅上面的作业控制)。所有进程都在单独的进程组中运行。当后台作业完成时,shell 会打印一行包含其退出状态的行。
因此,如果您想要您期望的行为,您可以:
fork
在代码中使用,无需 bash 即可创建进程。- 用于
set +m
关闭 bash 中的监视模式。fg
不过,这会破坏诸如 之类的东西。