ターミナルでフレーズを読み、一連のスペルを計算する対話型Cプログラムがあります。また、保持された「入力の終わり」マークが受信されるまで、STDINから行を読み取ってバッファリングし、行をソートし、列に整理し、STDOUTに送信するPerlプログラムもあります。 BSD / MacOSを使用するCプログラムは、popen()
単一の双方向パイプを介してPerlプログラムを起動し、すべてのfprintf()
スペル置換が生成されるまでパイプでスペルを実行し、パイプgetline()
からエクスポートしてフォーマットされた結果を再読み込みして印刷します。
通常の状況ではうまく機能します。しかし、CプログラムにSIGINTハンドラを追加することで、長い間実行されている単語パズルをやめ、これまでの結果を印刷し、より多くの端末入力のためにループを回すことができました。ハンドラは、パイプに書き込んだ後に基本プログラムが確認するフラグを設定します。設定されると、charadeループから外れ、ループが自然に終わったかのように続行されます。入力終了文字列を送信し、書式設定された結果を再読み込みします。
問題は発生しません。fprintf
入力の終わりは通常の戻りコードを取得しますが、最初のコードはgetline
EOFを表すnullポインタを返すため、部分的な結果は得られません。 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)
無効にするための適切な呼び出しを行うこともできます
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
。