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

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

我正在学习 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+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 ...>

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 会打印一行包含其退出状态的行。

因此,如果您想要您期望的行为,您可以:

  1. fork在代码中使用,无需 bash 即可创建进程。
  2. 用于set +m关闭 bash 中的监视模式。fg不过,这会破坏诸如 之类的东西。

相关内容