【问题标题】:what is tcgetpgrp() return value meaning?tcgetpgrp() 返回值是什么意思?
【发布时间】:2018-07-19 12:29:54
【问题描述】:

问题

tcgetpgrp 的手册页说

当 fd 引用调用进程的控制终端时,函数 tcgetpgrp() 将返回该终端的前台进程组 ID(如果有),并且 一些大于 1 的值,否则当前不是进程组 ID。

  1. some value是什么意思,好像有歧义
  2. 如何检查它是一个有效的组 id 还是其他值,这是否意味着我必须获取系统中所有进程组的列表才能确定返回值是否引用了一个有效的进程组?

上下文

当我尝试使用它时,我发现它似乎返回了当前会话 ID 我已经尝试了很多次,它总是返回当前会话 id

some value 是指当前会话 ID 吗?或者这是一个特例?还是我的代码有错误?

环境和代码

环境:libc 2.1.2,Linux 2.6.32

代码:

int main(int argc, char *argv[]) 
{
    return getgroup(argc, argv);
}

int getgroup(int argc, char *argv[])
{
    if (fork()) {
        return OK;
    }
    sleep(5);
    printf("process %d fork, ppid %d, pgid %d, psid %d \n", getpid(), getppid(), getpgid(getpid()), getsid(getpid()));
    pid_t gid = tcgetpgrp(STDIN_FILENO);
    printf("group id %d \n", gid);
    return OK;
}

【问题讨论】:

  • 手册说“进程的会话 ID 是会话领导者的进程组 ID。”它告诉你终端的进程组是会话领导进程组。即,会话 ID 始终也是有效的进程组 ID。
  • 您的代码示例不完整(main 在哪里?)并且不清楚您是如何运行它的(为什么输出会出现在 shell 提示符之后?)。后者尤其会影响输出。
  • 合理,当父进程退出时,没有前台进程,终端控制返回shell,是否有可能返回bash进程的pgid? @本
  • 某个大于 1 且当前不是进程组 ID 的值表示大于 1 的数字(可能的有效进程组 ID)但不是进程组 ID,因为系统没有具有该 ID 的进程组。这就是明显模棱两可的句子的明确含义。

标签: c linux system-calls


【解决方案1】:
  1. some value 的含义是什么,好像有歧义

这意味着它所说的,我认为:一些(任意)值不是进程组 ID。 (它实际返回的最有可能是在它终止之前作为前台组的进程组 ID;这在实践中不太可能可见,因为 shell 通常会在其子进程之前立即将另一个进程设置为前台进程在前台终止)。

  1. 如何检查它是一个有效的组 id 还是其他值,这是否意味着我必须获取系统中所有进程组的列表才能确定返回值是否引用了一个有效的进程组?

您可以使用带有负参数的kill 向进程组发出信号(发出信号的进程组将是参数的绝对值),并使用信号编号 0。这将返回 -1 并将 errno 设置为ESRCH 如果进程组不存在,如果存在则什么都不做(并返回 0)。

(也可以使用killpg,但手册页没有记录使用信号编号0的可能性,所以我不确定)。

但是,存在竞争条件:进程组可能在调用 tcgetgrp 时已经存在,但此后终止;相反,它可能不存在,但可能已经存在具有相同 ID 的新进程组。这使得它只对检查当前进程控制的进程组(即它可以防止收获)非常有用 - 特别是由当前进程的子进程领导的组(或由当前进程本身领导的组)。

如果这看起来很有限,请考虑:在什么情况下,您实际上需要知道哪个进程组在前台,为什么?

【讨论】:

  • 谢谢,我收获了很多。你说得对,不需要知道前台是哪个进程组,我用它是因为我学习进程,看到这个系统调用,我不明白some value是什么意思,所以我给它一个尝试。顺便说一下,如何解释图片的结果,我认为可能是bash进程的pgid:当父进程退出时,没有前台进程,终端控制返回shell,是否有可能返回bash进程的pgid ?
  • 我还有一个问题,当我们执行一个程序时,它fork一个子进程,它是前台进程组,当父进程退出时,子进程由init进程托管,它仍然保持进程组,子进程组是前台还是后台?
  • @lilin 我想你会发现一旦你通过它运行的进程返回,shell 就会将它自己的进程组作为前台进程组。在回答您的第二个问题时,这不会自动发生 - 外壳(或其他程序)必须这样做;在此之前,您的程序的进程组是前台组。
  • @davmac,系统会在创建有效进程组之前检查进程组是否不存在...它使用 pid 的分配,因为创建进程组的唯一方法是成为进程组负责人(根据定义 pgrpid == 进程组lider pid)
  • @LuisColorado 我知道。我不明白你在说什么?
【解决方案2】:

只关注tcgetpgrp() 返回值的含义是什么?的问题标题,即控制您请求号码的终端的进程组ID。

为了稍微解释一下事情的发生方式,有必要知道系统如何处理 pid 和进程组 ID,以及终端需要如何知道哪个进程组控制着该终端。

当用户登录时,登录shell,交互并成为进程组负责人,使用户终端知道进程组是登录shell。

对于作业控制,对于外壳构建和执行的每个管道,在派生第一个进程之后,它就成为进程组领导(通过对 setpgrp() 的调用)并在该管道下生成所有命令进程组。然后,它使该进程组 id 成为终端控制的进程组,并且终端知道哪个进程组是该终端的控制进程组。

这意味着两件事:

  • 终端一次只能处理一个进程组。将其作为控制终端的其他进程组未链接到它(从某种意义上说,它们确实有一个指向 tty 的指针,但由于 tty 不指向该进程组,read(2)ing 对于这些进程不可能)。他们将能够再次read(2),一旦他们再次成为前台组(通过tcsetpgrp termios 调用使tty 指向该进程组)

  • shell对交互式作业执行此操作。更改交互式作业意味着将进程组设置为将要进入前台的进程组。它必须改变等待,等待该组的进程负责人。这不包括在后台生成的所有作业(或使用命令bgfg 在后台作业中转换的作业),因为 tty 现在指向不同的进程组。 shell 只维护一个前台进程组和许多后台进程,输入只能用于前台进程组。

终端需要知道它链接到哪个进程组,原因有两个:

  • 信号SIGINT(Ctrl-C)、SIGHUP(Ctrl-D)和SIGQUIT(Ctrl-])和SIGSTOP(Ctrl-X)的发送必须发送到进程组在前台,所以它被发送到控制进程组。信号 SIGHUP 被发送到更广泛的进程组,即会话组。

  • tty 读取函数需要知道读取终端的进程是否在它指向的进程组中,因此它检查执行读取调用的进程是否具有与存储的进程组相同的进程组 ID在终端驱动结构中。如果不匹配,则返回错误(errno 等于ENOTTY

另一方面,成为进程组组长(创建进程组的唯一方法)会创建一个进程组,其 id 等于发出调用的进程的 pid,因此进程组组长具有相同的编号它的 pid 和进程组 id。

曾经说过...控制进程组ID不需要在终端中更新。当进程exit(2)s 时必须搜索终端,并且搜索 tty 设备的完整列表以删除任何可以保留在那里的控制终端 ID 应该是乏味的。

但是当一个进程组负责人打开一个 tty 时,该 tty 的进程组将更改为该领导的进程组.....所以当一个进程死亡时,不需要导航系统中的所有终端,只是为了使他们的控制进程组 id 无效。

正如您在@davmac 的回答中所说的那样,有其他方法可以知道返回的数字是否是有效的进程组 ID,只需发送一个 0 信号(这样就可以知道一个进程 --- 或进程组 --- 是否存在,你会得到它)。

当然,关于@davmac 所指的竞态条件,要考虑重用 pid 的可能性,但由于内核以最近最少使用(或接近它)的方式管理 pid,因此负载非常高有必要进行这样的 pid 冲突。

您总是获得当前进程的进程组 ID 的原因是您总是以交互方式启动程序。尝试在后台启动它,你会看到一个与请求进程的进程组不匹配的进程组(它很可能是 shell 的进程组)

【讨论】:

  • “但是当一个进程组长打开一个tty时,那个tty的进程组就变成了那个leader的进程组”——这是不正确的,一个进程组长打开一个tty并没有自动将 tty 的前台进程组设置为该进程组。这必须使用tcsetpgrp 或等效项来完成。
  • 你有没有测试过,因为它是古老的使用,我最近没有这样做。但是在 UNIX 中,当你打开一个 tty 时,该 tty 没有附加进程组(这很难实现)并且该进程是进程组组长(这意味着它的 pgid 匹配它的 pid),并且该进程还没有打开另一个tty,则该 tty 已将其进程组设置为调用进程的进程组。我很久以前测试过这个(当时没有 tcsetpgrp 调用)并且它有效。
  • 来自tcsetpgrp(3)库调用的手册页:...与fd关联的终端设备必须(已经)是调用进程的控制终端引用括号是我的。这意味着 tcsetpgrp(3) 调用 不使用 将终端设置为进程组控制终端。 (继续挖掘文档...)
  • @davmac,我没有说相反,许多组可以拥有与控制终端相同的 tty。一个进程不能有多个控制终端,一个终端只能有一个控制进程组。我所说的是,为了能够从 tty 读取,从进程到终端以及从终端到进程组的指针必须相互指向。 Other process groups are not linked with that terminal 这句话是错误的错字。我的意思是我上面说的。终端总是向进程组发送键盘信号(SIGHUP 除外,它被发送到进程会话组)
  • 进程组被发明来管理作业控制......和进程组会话来管理交互式登录会话或守护进程组上的会话。
猜你喜欢
  • 2013-04-25
  • 2014-05-16
  • 1970-01-01
  • 1970-01-01
  • 2017-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-09
相关资源
最近更新 更多