tcgetpgrp()返回值是什么意思?

时间:2018-07-19 12:29:54

标签: c linux system-calls

问题

tcgetpgrp说的手册页

  

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

  1. some value是什么意思,似乎模棱两可
  2. 我怎样才能将其检查为有效的组ID或其他值,这是否意味着我必须获取系统中所有过程组的列表才能确定返回值是否引用了有效的过程组?

上下文

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

enter image description here

some value是当前会话ID的平均值吗?还是特殊情况?或我的代码有错误?

环境和代码

env: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;
}

2 个答案:

答案 0 :(得分:5)

  
      
  1. 某些值的含义是什么,似乎模棱两可
  2.   

我认为它表示的意思是:一些(任意)值,不是进程组ID。 (它实际上返回的最有可能是终止前的进程组ID,它是前台组;在实践中这不太可能看到,因为外壳程序通常会在其子进程之前立即将另一个进程设置为前台进程。在前台终止)。

  
      
  1. 我怎样才能将其检查为有效的组ID或其他值,这是否意味着我必须获取系统中所有过程组的列表才能确定返回值是否引用了有效的过程组?
  2.   

您可以使用带有负参数的kill来向进程组发信号(发出信号的进程组将是参数的绝对值),并使用信号号0。这将返回-1,并带有errno如果进程组不存在,则设置为ESRCH;如果进程组不存在,则设置为不执行任何操作(并返回0)。

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

但是,存在竞争条件:进程组可能在调用tcgetgrp时就已经存在,但是此后终止。相反,它可能不存在,但是可能已存在具有相同ID的新进程组。这实际上仅对检查当前流程控制的流程组(即可以防止收割的流程组)(特别是由当前流程的子流程领导(或由当前流程本身领导)的组)有用。

如果这似乎有局限性,请考虑:在什么情况下,您实际上需要知道哪个进程组位于前台,为什么?

答案 1 :(得分:1)

仅将问题标题放在 tcgetpgrp()返回值的含义是什么?上,该进程组ID控制着您请求编号的终端。

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

用户登录时,交互登录并成为过程组领导者的登录shell使用户终端知道该进程组是登录shell。

对于作业控制,在分叉第一个进程之后,shell会为每个构建和执行的管道构建并执行该外壳,它成为进程组负责人(通过使用setpgrp()的cal),并在该管道下生成所有命令流程组。然后,它使该进程组标识为终端的控制中的进程组,并且终端知道哪个进程组是该终端的控制进程组。

这意味着两件事:

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

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

由于两个原因,终端需要知道其链接到哪个进程组:

  • 发送信号SIGINTCtrl-C),SIGHUPCtrl-D)和SIGQUITCtrl-])和{ {1}}(SIGSTOP)必须发送到前台的进程组,因此必须发送到控制进程组。信号Ctrl-X发送到更广泛的进程组,即会话组。

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

另一方面,成为流程组负责人(创建流程组的唯一方法)将创建一个ID等于发出呼叫的流程的pid的流程组,因此该流程组负责人的编号与其pid和进程组ID。

一旦这样说...控制进程组ID不需要在终端中更新。当进程ENOCTTY出现时,必须对终端进行搜索,并且搜索tty设备的完整列表以删除可以保留在该终端上的任何控制终端ID都是很麻烦的。

但是,当进程组负责人打开tty时,该tty的进程组将更改为该负责人的进程组.....因此,当进程终止时,无需导航系统中的所有终端,只是为了使他们的控制进程组ID无效。

正如您在@davmac的答案中告诉您的那样,有其他方法可以知道返回的数字是否是有效的进程组ID,只需发送一个exit(2)信号(可以通过它来进行设置)可能知道某个流程-或流程组-是否存在,您会知道的。

当然,对于@davmac指出的竞争条件,请考虑重用pid的可能性,但是由于内核以最近最少使用(或接近使用)的方式管理pid,因此负载非常高有必要发生这种短暂的冲突。

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