![已处理的 SIGINT 是否会影响与子进程的管道通信?](https://linux22.com/image/210426/%E5%B7%B2%E5%A4%84%E7%90%86%E7%9A%84%20SIGINT%20%E6%98%AF%E5%90%A6%E4%BC%9A%E5%BD%B1%E5%93%8D%E4%B8%8E%E5%AD%90%E8%BF%9B%E7%A8%8B%E7%9A%84%E7%AE%A1%E9%81%93%E9%80%9A%E4%BF%A1%EF%BC%9F.png)
我有一个交互式 C 程序,它从终端读取一个短语,并计算一系列字谜。我还有一个 Perl 程序,它从 STDIN 读取和缓冲行,直到收到保留的“输入结束”标记,对行进行排序,将它们组织成列并将它们发送到 STDOUT。 C 程序使用 BSD/MacOSpopen()
通过单个双向管道启动 Perl 程序,并fprintf()
在管道上进行 anagrams,直到生成所有 anagrams;发送“输入结束”标记,然后getline()
在管道上发出 来读回并打印格式化结果。
正常情况下工作正常。但我向 C 程序添加了一个 SIGINT 处理程序,这样我就可以中断长时间运行的字谜,打印迄今为止的任何结果,并循环获取更多终端输入。处理程序设置一个标志,主程序在写入管道后检查该标志。如果已设置,它将跳出字谜循环并继续进行,就像循环自然结束一样:它发送输入结束字符串,并读回格式化结果。
它爆发了,没问题;fprintf
输入结束得到正常的返回码;但第一个getline
返回一个指示 EOF 的空指针,所以我没有得到任何部分结果。 C 程序按预期正常继续。
我在文档中没有找到任何内容表明已处理的 SIGINT 会影响任何打开的管道,但我想不出还有什么会阻止我从 Perl 程序接收任何内容。
答案1
信号SIGINT
发送到前台进程组中的每个进程;前台进程组通常包括任何
popen
运行,
/* popen.c */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void poke(int unused) { fprintf(stderr, "sigint - C\n"); }
int main(void) {
FILE *fh;
signal(SIGINT, poke);
fh = popen(
"exec perl -e '$SIG{INT}=sub {warn q{ouch}};sleep 1 while 1'",
"r+");
if (!fh) abort();
while (1) sleep(1);
return 42;
}
可以通过以下方式验证:
$ make popen
$ ./popen
^Csigint - C
ouch at -e line 1.
^Csigint - C
ouch at -e line 1.
^\Quit
因此,所有程序都必须处理该信号,或者可以在终端中关闭该信号。详细信息因语言而异,但要忽略 Perl 中的信号,可以简单地忽略它:
$SIG{INT} = "IGNORE";
另一种选择是阻止父进程中的信号,尽管后续程序可能会安装自己的信号处理程序。
ISIG
另一种选择是在终端中禁用该标志。在这种情况下control+c不会导致向前台进程组发送信号;相反,某个字符将可供某些程序读取。有关详细信息,请参阅termios
手册页(手册页部分将因操作系统而异)。像curses这样的接口可能是这里的典型接口,尽管可以改为进行适当的
tcsetattr(3)
调用以disable ISIG
。
#!/usr/bin/env perl
# example script showing how control+c can be read as a key
use strict;
use warnings;
use Curses;
initscr;
noecho;
raw; # disables ISIG, among other things
while (1) {
my $ch = getchar() // die "getchar failed??";
last if $ch eq 'q';
move 0, 0;
clrtoeol;
addstring sprintf "key: %vx", $ch;
}
endwin;
key: 3
当按下control+时应该显示此内容;c信号不会发送到前台进程组。键入q
退出。