忽略父进程中的信号

我正在尝试实现一个shell程序,我希望shell程序忽略SIG_INT(ctrl + c)。 但是在我的程序中,孩子也忽略了SIG_INT信号,因为exec应该将子进程带到另一个程序,并且该程序应该默认处理SIG_INT信号。 我应该怎么做才能在按下ctrl + c时终止子进程。

新编辑:在我的子进程块中放置信号(Certain_signal,SIG_DFL)后,我的代码工作正常。 但我仍然对这项工作的方式感到困惑。 这是否意味着信号和信号处理都可以通过执行命令传播?

int main(void){ signal(SIG_INT, SIG_IGN); int result = fork(); if(result == 0){ //child: //exec some programs } else{ waitpid(result); //do something } 

}

我相信你已经误解了exec如何修改信号处理。 在Linux exec手册页中,例如(1) ,它表明(我的重点):

execve()期间保留所有进程属性,但以下内容除外:

  • 正被捕获的任何信号的配置被重置为默认值( signal(7) )。

  • <在这个问题的背景下,很多其他不相关的东西>

正在捕获的信号与被忽略的signal ,如signal手册页所示:

使用这些系统调用,进程可以选择在传递信号时发生以下行为之一:

  • 执行默认操作;
  • 忽略信号; 要么
  • 使用信号处理程序捕获信号,信号处理程序是在传递信号时自动调用的程序员定义的函数。

这实际上是有道理的,因为虽然忽略信号可以通过exec调用传播,但信号处理程序不能 – 用于处理信号的函数已被exec调用替换,因此尝试调用它很可能是灾难性的。

您可以通过编译以下两个程序qqp.c来查看inheritance“ignore”处置的这种行为:

 #include  #include  #include  #include  int main (void) { signal (SIGINT, SIG_IGN); puts("Parent start"); if (fork() == 0) execl ("./qqc", 0); wait(0); sleep (1); puts("Parent end"); return 0; } 

qqc.c

 #include  #include  #include  int main (void) { //signal (SIGINT, SIG_DFL); puts("Child start"); sleep (60); puts("Child end"); return 0; } 

请注意,您还可以在forkexec之间更改第一个代码示例中的处置。 如果您实际上没有控制第二个代码示例将执行的操作(例如,如果您正在调用未编译的可执行文件),那么这将是更可取的。

运行qqp ,无论你按CTRL-C多少次,父母和孩子都不会过早退出。 但是,取消注释恢复默认行为的行,您可以轻松地突破孩子。

因此,如果您希望您的孩子恢复默认行为,您需要在孩子本身中执行此操作,例如:

 signal (SIG_INT, SIG_DFL); 

(1) POSIX对发生的事情有更详细的了解:

设置为调用过程映像中的默认操作( SIG_DFL )的信号应设置为新过程映像中的默认操作。 SIGCHLD外,调用过程映像设置为忽略的信号( SIG_IGN )应设置为被新过程映像忽略。 设置为由调用过程映像捕获的信号应设置为新过程映像中的默认操作(请参阅 )。 如果调用过程映像将SIGCHLD信号设置为忽略, SIGCHLD指定SIGCHLD信号是设置为忽略还是设置为新过程映像中的默认操作。


而且,只是在您的编辑中我提出的解决方案有效,但它为您提出了另一个问题:

这是否意味着信号和信号处理都可以通过执行命令传播?

信号本身不通过exec调用传播,信号实际上是生成的“中断”。 这与信号处理程序(处理信号的代码)或信号处理(信号发生时该怎么做)不同。 如上所示,处置可以在exec调用中存活,但处理程序不能。 信号也没有。

当您按下CTRL-C并且多个进程受到影响时,您所看到的与在exec边界上inheritance信号无关,它更多地与终端内容有关。

传递给单个进程的信号不会影响其任何子进程。 但是,按CTRL-C不会向单个进程发送信号。 POSIX终端接口具有控制终端和进程组的概念:

每个流程也是流程组的成员。 每个终端设备记录一个称为其前台进程组的进程组。 进程组控制终端访问和信号传递。 在终端处生成的信号被发送到作为终端的前台进程组的成员的所有进程。