我有一个 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 忽略的陷阱将被忽略
华泰