![](/img/trans.png)
[英]Sending a variable changed by a signal to a process created by exec in the child process.[C]
[英]C shell, signal caught by parent still goes to child process.
我正在用C語言編寫一個基本的unix外殼程序,我想在外殼程序中捕獲Cntrl-C信號並將其僅傳遞給前台進程,而不傳遞給后台進程。 外殼程序本身應繼續運行(並且確實如此),並且后台進程應忽略Cntrl-C,並且只能通過專門發送給它們的終止信號(可能是通過命令行“ kill pid ”) 終止 。 但是,前台和后台進程都應使用SIGCHLD觸發處理程序。 但是,現在,外殼捕獲了Cntrl-C信號, 似乎可以正確地識別出沒有將信號傳遞到的前台進程,但是后台進程仍然消失了。
我嘗試將后台進程的組ID設置為其他名稱,這可以解決問題,但會產生一個新問題。 當我這樣做時,后台進程完成時,我的信號處理程序將不再捕獲信號。
到目前為止,我已經查看了SIGINT的手冊頁,已經閱讀了約20個SO答案,我嘗試將孩子的組ID設置為與父組不同的名稱(解決了問題,但是現在孩子不能再將SIGCHLD發送給父進程),而在運行后台進程時,我已經檢查了childid!=前台進程和frontendProcess == 0。 但是,后台進程仍然被殺死。 有任何想法嗎?
我認為我的問題出在我的信號處理程序中,但是不確定:
在主要方面:
struct sigaction sa;
sa.sa_handler = &handleSignal; /*passing function ref. to handler */
sa.sa_flags = SA_RESTART;
sigfillset(&sa.sa_mask); /*block all other signals while handling sigs */
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
handleSignal看起來像這樣:
void handleSignal(int signal){
int childid;
switch (signal) {
/*if the signal came from a child*/
case SIGCHLD:
/*get the child's id and status*/
childid = waitpid(-1,&childStatus,0);
/*No action for foreground processes that exit w/status 0 */
/*otherwise show pid & showStatus */
if ((childid != foregroundProcess)){
printf("pid %i:",childid);
showStatus(childStatus);
fflush(stdout);
}
break;
/* if signal came from somewhere else, pass it to foreground child */
/* if one exists. */
default:
printf("Caught signal: %i and passing it", signal);
printf(" to child w/pid: %i\n\n:", foregroundProcess);
fflush(stdout);
/*If there is a child, send signal to it. */
if (foregroundProcess){
printf("trying to kill foreground.\n");
fflush(stdout);
kill(foregroundProcess, signal);
}
}
}
找到了我自己問題的答案。 我已經嘗試過使用setpid(0,0);
更改后台子進程的組ID setpid(0,0);
這行得通,但產生了另一個問題。 通話之后,我不再從父級的子級捕獲SIGCHLD信號。 這是因為一旦更改了子進程組,出於信令目的,它實際上不再連接到父進程組。 這解決了后台進程從父進程捕獲Cntrl-C(SIGINT)信號的問題(不良行為),但阻止了后台進程在完成時向父進程發信號。 解決了一個問題,只解決了另一個問題。
相反,解決方案是檢測將要創建一個子進程作為前台進程還是后台進程,如果是后台進程,則告訴它忽略SIGINT信號: signal(SIGINT, SIG_IGN);
由於這可能是tty上的第一個進程,因此內核在發送內核啟動的SIGINT,SIGHUP或SIGQUIT時,會將信號發送給其所有子進程。 有關終端的更多詳細信息,請參閱在終端關閉時終止sudo python腳本 ,以及跟蹤/調試正在發生的事情以確保正確使用的想法。
啟動bg子進程忽略SIGINT(和SIGQUIT,也許還有SIGHUP?)的一種替代方法是避免讓內核將這些信號傳遞給Shell。
我忘記了,但是我認為內核僅將SIGINT傳遞給當前的前台進程。 如果cat
或其他物體在前台運行,則外殼將無法獲得它。 因此,您只需要擔心外殼在前台時會發生什么情況。 (不過,我對此只有80%的把握,如果外殼(及其所有子容器)始終收到SIGINT,則此想法毫無用處。)
如果在用戶編輯命令提示符時禁用tty的信號發送,則可以避免讓外殼接收SIGINT。 最好的方法可能是使用tcsetattr(3)做與stty -isig
等效的操作
// disable interrupts
struct termios term_settings;
tcgetattr(fd, &term_settings); // TODO: check errors
term_settings.c_lflag &= ~ISIG; // clear the interactive signals bit
tcsetattr(fd, TCSANOW, &term_settings);
// do the reverse (settings |= ISIG) after forking, before exec
如果您strace
,你會只看到ioctl
系統調用,因為這是系統調用的termios庫函數的基礎上實現的。
我猜這在子進程結束與wait()
返回與外殼禁用終端中斷之間留出了很小的時間窗口。
IIRC,我讀到一些有關bash
難以跟蹤何時在raw(用於行編輯)和Cooked(用於運行命令)之間交換終端的技巧,但是我認為這僅是由於作業控制。 (^ z / fg)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.