为什么流程不是预期流程组的一部分?

Sto*_*row 5 linux process process-groups

我正在学习 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;
}
Run Code Online (Sandbox Code Playgroud)

...a.out作为后台进程运行它,就像这样......

a.out &
Run Code Online (Sandbox Code Playgroud)

这个网站说以下...

每个进程都是唯一进程组的成员,由其进程组 ID 标识。(创建进程时,它成为其父进程组的成员。)按照惯例,进程组的进程组 ID 等于进程组的第一个成员的进程 ID,称为进程组组长。

根据我的阅读,第一句与括号内的内容冲突:进程是唯一进程组的成员,还是其父进程组成员?

我试图调查ps...

ps xao pid,ppid,pgid,sid,command | grep "PGID\|a.out"
  PID  PPID  PGID   SID COMMAND
24714 23890 24714 23890 ./a.out
Run Code Online (Sandbox Code Playgroud)

这告诉我我的a.out进程是 pid 24714,从父 pid23890和程序组的一部分产生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
Run Code Online (Sandbox Code Playgroud)

我的父进程a.outbash. 起初我认为“ bash 的 pid 与其 pgid 匹配 - 那一定是因为它是进程组的领导者。也许这是有道理的,因为 bash 是运行我的进程的“第一件事”。 “但是那个推理没有意义,因为a.out的 pgid 也匹配它自己的 pid。

为什么a.outpgid 不等于bashpgid?根据我对报价的理解,这就是我所期望的。

有人可以澄清pids和pgids之间的关系吗?

der*_*ert 5

Bash 将您的程序放在其自己的进程组中,作为其作业控制处理的一部分。例如,来自 bash 联机帮助页:

为了便于作业控制的用户界面的实现,操作系统维护了当前终端进程组 ID 的概念。此进程组的成员(进程组 ID 等于当前终端进程组 ID 的进程)接收键盘生成的信号,例如 SIG? 情报局。据说这些进程在前台。后台进程是那些进程组 ID 与终端不同的进程;此类进程不受键盘生成信号的影响。只允许前台进程读取,或者,如果用户使用 stty tostop 指定,则写入终端。

还有关于set -m

监控模式。作业控制已启用。对于支持它的系统上的交互式 shell,默认情况下启用此选项(请参阅上面的 JOB CONTROL)。所有进程都在一个单独的进程组中运行。当后台作业完成时,shell 会打印一行包含其退出状态的行。

所以,如果你想要你期望的行为,你可以:

  1. fork在您的代码中使用,在没有 bash 的情况下创建一个进程。
  2. 用于set +m关闭 bash 中的监控模式。不过,这会破坏诸如 之类的东西fg


thr*_*rig 5

没有冲突;默认情况下,一个进程将位于一个唯一的进程组中,该进程组是其父进程组:

$ 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
$ 
Run Code Online (Sandbox Code Playgroud)

fork分裂我们的工艺为母公司(12495)和孩子(12496),和孩子属于父的独特的工艺组(12495)。bash与此不同,因为它发出额外的系统调用:

$ echo $$
12366
$
Run Code Online (Sandbox Code Playgroud)

然后在另一个终端中我们运行:

$ strace -f -o blah -p 12366
Run Code Online (Sandbox Code Playgroud)

然后回到第一个终端:

$ ./pg
pid=12676 pgid=12676
pid=12677 pgid=12676
$
Run Code Online (Sandbox Code Playgroud)

然后我们control+cstrace,并检查系统调用:

$ 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 ...>
Run Code Online (Sandbox Code Playgroud)

bash已经使用setpgid调用来设置进程组,从而将我们的pg进程放入与shell无关的进程组中。(setsid(2)如果您正在寻找系统调用,这将是调整进程组的另一种方法。)