unix 为什么tcsetpgrp()调用不能按预期工作?

mspsb9vt  于 2023-02-04  发布在  Unix
关注(0)|答案(1)|浏览(165)

在寻找显示tcsetpgrp()调用用法的代码片段时,我遇到了https://www.ibm.com/support/knowledgecenter/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxbd00/rttcsp.htm,其中显示了CELEBT10.c的代码。
当执行我得到的代码

original foreground process group id of stdout was 59741
now setting to 59742

则程序停止。
对于ps -aj,我看到组更改(setpgid())工作正常,实际上,当我向子进程发送SIGCONT信号时,子进程执行剩余部分并退出(与等待的父进程一起)。
tcsetpgrp()之后添加一个sleep()ps -aj也显示父组仍然是前台组,即tcsetpgrp()调用失败。
有人能解释一下为什么子进程在tcsetpgrp()调用中停止以及为什么失败吗?

ax6ht2ek

ax6ht2ek1#

这是因为从后台进程 * 尝试tcsetpgrp * 生成了SIGTTOU,如手册中所述:
如果tcsetpgrp()从后台进程组针对调用者的控制终端被调用,则SIGTTOU信号可以根据进程如何处理SIGTTOU而被生成:通过运行'strace -f ./a.out'并观察子进程的输出('-f'表示跟随fork),您可以看到这一点:

[pid  4062] setpgid(4062, 0)            = 0
[pid  4062] write(1, "now setting to 4062\n", 20now setting to 4062
) = 20
[pid  4062] ioctl(1, TIOCSPGRP, [4062]) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
[pid  4062] --- SIGTTOU {si_signo=SIGTTOU, si_code=SI_KERNEL} ---

tcsetpgrp()被库转换为ioctl,我们可以看到发生了什么。
将指向的代码复制到此处:

/* CELEBT10
 *
 *    This example changes the PGID.
 *
 *     */
#define _POSIX_SOURCE
#include <termios.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>

int main() {
  pid_t pid;
  int status;

  if (fork() == 0)
  {
    // signal(SIGTTOU, SIG_IGN);  // UNCOMMENT ME
    if ((pid = tcgetpgrp(STDOUT_FILENO)) < 0)
      perror("tcgetpgrp() error");
    else {
      printf("original foreground process group id of stdout was %d\n",
             (int) pid);
      if (setpgid(getpid(), 0) != 0)
        perror("setpgid() error");
      else {
        printf("now setting to %d\n", (int) getpid());
        if (tcsetpgrp(STDOUT_FILENO, getpid()) != 0)
          perror("tcsetpgrp() error");
        else if ((pid = tcgetpgrp(STDOUT_FILENO)) < 0)
          perror("tcgetpgrp() error");
        else
          printf("new foreground process group id of stdout was %d\n", (int) pid);
        fflush(stdout);
      }
    }
  }
  else wait(&status);
}

参见注解“未注解我”,它将允许功能继续:

$ ./a.out
original foreground process group id of stdout was 4070
now setting to 4071
new foreground process group id of stdout was 4071

我已经很久没有这么做了,所以我对基本原理有点模糊,但我相信这个想法是后台进程不应该写终端,也不应该把前台进程正在做的事情搞砸。很多时候,把自己放在后台的代码会重定向它的输入/输出,把自己从前台终端分离出来。
通过捕捉(或忽略)信号,代码使其意图明确,但我不确定随意的“忽略信号”是否自动是正确的答案;我们需要了解这些代码如何融入大局。

相关问题