Pipe

已處理的 SIGINT 是否會影響與子程序的管道通信?

  • September 8, 2022

我有一個互動式 C 程序,它從終端讀取一個片語,併計算一系列字謎。我還有一個 Perl 程序,它從 STDIN 讀取和緩衝行,直到接收到保留的“輸入結束”標記,對行進行排序,將它們組織成列並將它們發送到 STDOUT。C 程序使用 BSD/MacOSpopen()以單個雙向管道啟動 Perl 程序,並fprintf()在管道上生成 anagrams,直到生成所有 anagrams;發送“輸入結束”標記,然後getline()在管道上發出 ’s 以讀取並列印格式化的結果。

它工作正常。但是我在 C 程序中添加了一個 SIGINT 處理程序,這樣我就可以中斷一個長時間執行的字謎,列印它到目前為止的任何結果,並循環獲取更多終端輸入。處理程序設置一個標誌,主程序在寫入管道後檢查該標誌。如果它被設置,它會跳出字謎循環並繼續進行,就像循環自然結束一樣:它發送輸入結束字元串,並讀回格式化的結果。

它爆發了。輸入fprintf結束得到一個正常的返回碼;但第一個getline返回一個指示 EOF 的空指針,所以我沒有得到任何部分結果。C 程序按預期正常繼續。

我在文件中找不到任何內容表明處理的 SIGINT 會影響任何打開的管道,但我想不出還有什麼會阻止我從 Perl 程序接收任何內容。

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 這樣的介面在這裡可能是典型的,儘管可以改為進行適當的 tcsetaddr(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;    # enables 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

引用自:https://unix.stackexchange.com/questions/716530