【问题标题】:Why the tcsetpgrp() call does not work as expected?为什么 tcsetpgrp() 调用不能按预期工作?
【发布时间】:2021-02-27 02:37:36
【问题描述】:

寻找显示使用tcsetpgrp() 调用的sn-p 代码,我遇到了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() 调用中停止以及为什么它失败了吗?

【问题讨论】:

    标签: c unix terminal signals background-foreground


    【解决方案1】:

    这是因为 SIGTTOU 是通过在后台进程中尝试 tcsetpgrp 生成的,如手册​​中所述:

    如果从后台进程组对调用者的控制终端调用 tcsetpgrp(),则可能会生成 SIGTTOU 信号,具体取决于进程如何处理 SIGTTOU:
    您可以通过运行“strace -f ./a.out”并观察子进程的输出来看到这一点(“-f”表示跟随分叉):
    [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
    

    自从我不得不这样做已经有很多年了,所以我对基本原理很模糊,但我相信后台进程不应该写入终端并搞乱前台进程正在做的任何事情。很多时候,将自己置于后台的代码会重定向其输入/输出以将自己与前台终端分离。

    通过捕获(或忽略)信号,代码可以明确表达其意图,但我不确定随意的“忽略信号”是否会自动成为正确答案;我们需要了解这段代码如何适应更大的图景。

    【讨论】:

      猜你喜欢
      • 2021-05-30
      • 2020-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多