由 forkpty() 启动的 Bash shell 会生成忽略 SIGINT 的子进程。为什么以及如何确保 SIGINT 不被忽略?

由 forkpty() 启动的 Bash shell 会生成忽略 SIGINT 的子进程。为什么以及如何确保 SIGINT 不被忽略?

我有一个 C 程序,它使用 forkpty 来执行 bash shell。我发现此 shell 启动的程序在启动时忽略了 SIGINT,因此当我向 shell 发送 Ctrl-C 时,它们永远不会关闭。

例子:

int masterFd;
char* args[] = {"/bin/bash", "-i", NULL };
int procId = forkpty(&masterFd, NULL, NULL,  NULL);
if( procId == 0 ){
  execve( args[0], args, NULL);
}
else {
   // Simple code that reads from standard in and writes to masterFd.  
   // I also register for Ctrl-C and write it to masterFd if caught
}

其他控制字符似乎可以通过,ctrl-D,ctrl-?然而,每当我查看新 bash shell 启动的进程的状态时,它看起来好像 SIGINT 被屏蔽了。

MyShell:# sleep 1000

StandardTerm:#  ps -ef | grep sleep
root    26611  19278  0  17:44  pts/1   00:00:00 sleep 1000
root    26613  32376  0  17:44  pts/1   00:00:00 grep sleep

StandardTerm:# grep Sig proc/26611/status
SigQ:    0/256428
SigPnd:  0000000000000000
SigBlk:  0000000000000000
SigIgn:  0000000000010006   <- THE 6 IS THE PROBLEM
SigCgt:  0000000180000000

SigIgn 设置了 2 位,这意味着 2 (SIGINT) 被忽略。如果我做完全相同的事情,但在标准终端中运行 sleep (或 cat 一个巨大的文件或其他什么),则该位被清除。当我启动 pty bash 并导致它创建忽略 SIGINT 的孙子程序时,我在做什么?

此外,如果我向进程发送 SIGINT 信号

StandardTerm:# kill -2 26611

什么都没发生。奇怪的是,当我向 bash shell 发送相同的命令时,我 forkpty 的它可以工作,因为 bash shell 不会忽略 SIGINT。

答案1

也许你只需要这样做:

stty sane

我注意到forkpty() man页面显示它将复制 termios 设置*termp到新打开的 pty,但它没有具体说明它如何处理这些,并且您手上唯一的非 NULL 参数forkpty()是用于 pty master fd。我猜你最终会得到一个完全为空的 termios 结构,这不是很有用。它不会bash太麻烦,它必须readline()处理所有自己的终端内容,因此无论如何它都会默认解释所有默认字符。

这是来自一个信息丰富的块引用文章就此主题而言:

stty-F选项非常适合查看其他程序正在对其终端执行的操作。如果您tty在 shell 中运行,它将打印该 shell 终端设备的路径(通常采用 的形式/dev/pts/N,至少在 Linux 下)。现在,您可以从不同的 shell 运行stty -a -F /dev/pts/N以查看第一个 shell 的终端是如何配置的。然后,您可以在第一个 shell 中运行程序,并stty在第二个 shell 中重复该咒语以查看设置了哪些设置。例如,如果我stty -F /dev/pts/10现在跑步(当我通过那个 ptybash与某人交谈时)gnome-terminal, 我懂了:

    $ stty -F /dev/pts/10
      speed 38400 baud; line = 0;
      eol = M-^?; eol2 = M-^?; swtch = M-^?; lnext = <undef>; min = 1; time = 0;
     -icrnl iutf8
     -icanon -echo

所以我们可以看到已经bash/readline禁用了CR→LF输入翻译( icrnl), 禁用canonical模式和echo,但开启了UTF-8模式(因为bash检测到 utf-8 语言环境)。请注意,如果我stty直接在该 shell 中运行,我会看到一些略有不同的内容:

     $ stty
       speed 38400 baud; line = 0;
       eol = M-^?; eol2 = M-^?; swtch = M-^?;
       iutf8

这是因为bash维护了自己的一组termios设置(为了readline,并保存和恢复正在运行的程序的设置,以便运行程序时的设置与您在提示符处键入时的设置不同bash

答案2

此结果不可能是由于您在问题中显示的代码块造成的。你的整体设置的其他部分一定还有其他东西。

仅考虑到您描述的使用场景,最可能的原因是您实际上在代码中的某处设置了忽略 SIGINT叉子,或forkpty 子代中的 execve。

这将产生您所描述的结果,因为您的 forkpty'edbash -i将继承此类 SIGINT-ignored 设置,并且虽然它会出于自己的内部目的(因此不会忽略 SIGINT)而将其设置为其他状态,但它也会将其重置为继承的忽略状态它会产生的每一个命令。

这是 Bash 行为的记录,请参阅 Bash 手册页的“命令执行环境”一章,特别是以下段落:

当要执行除内置函数或 shell 函数之外的简单命令时,将在由以下内容组成的单独执行环境中调用它。 [...]

[...]

o shell 捕获的陷阱将重置为从 shell 的父级继承的值,并且 shell 忽略的陷阱将被忽略

华泰

相关内容