Contents

父子进程之间的信号传递

1. 概述

在本教程中,我们将讨论 Linux 进程中的信号、父子进程层次结构以及进程间的信号行为。

我们将理解为什么SIGINT信号 没有传播到子进程。为此,我们需要先了解 Linux 是如何处理进程和信号的。

2. 信号

我们可以使用kill 命令从终端向进程发送信号。要使用此命令,我们只需 以这种方式调用kill

kill [-signal|-s signal|-p] [-q value] [-a] [--timeout milliseconds signal] [--] pid|name... 

尽管它看起来很复杂,但如果我们只想中断一个进程,它使用起来非常简单明了:

$ kill -SIGINT 1234 

简而言之,kill命令向特定进程发送信号,唯一需要的参数是信号和进程 ID。

3. 父子进程层次结构中的信号

在我们深入讨论之前,让我们关注流程层次结构和信号的基础知识。

Linux 不支持从头开始创建进程,但是它提供了一个方法fork() 来帮助在编程过程中创建子进程。通常,当我们想要创建一个子进程时,我们会在代码中做这样的事情:

int pid = fork();
if (pid == 0) {
    // child process
    exec();
}
else {
    // parent process
}
// block parent until the child is finished
waitpid(pid); 

当我们调用*fork()*时,它会创建一个子进程,该子进程具有唯一的进程 ID 和来自其父进程的信号处置副本。但是,当我们调用exec() 系列方法时,它会替换过程映像并将信号配置重置为默认值。

*这意味着*exec()之后子进程中的信号行为不再与父进程共享。因此,如果我们在调用exec()后将信号发送给父进程,它自然不会传播。

有时,当向父进程发送SIGINT时,我们可能会注意到我们也可以“中断”一些子进程。在这种情况下,我们不会通过SIGINT本身来中断子进程。相反,是操作系统“阻止”了它们。当子进程正在等待父进程持有的某些资源时,就会发生这种情况。子进程被阻塞并等待父进程释放资源。当子进程独立于父进程时,不会发生这种阻塞行为。

4. 如何中断层次结构中的所有进程?

**幸运的是,当我们在层次结构中创建子进程时,层次结构中的每个进程都会获得一个名为Process Group ID (PGID) **的新属性。

要查找进程的 PGID,我们可以使用 ps 命令:

$ ps ax -O pgid 
PID       PGID      S      TTY      TIME      COMMAND
  1          1      S      ?    00:00:00      /usr/lib/...
...

标有 PGID 的第二列是我们要查找的内容。

此命令为我们提供了所有进程的列表以及附加的组 ID。我们可以在这个列表中找到我们的目标进程。如果我们知道进程的名称(例如a.out),我们可以简单地将输出通过管道传输到grep 命令中:

$ ps ax -O pgid | grep a.out 
1234    1234    S    pts/0    00:00:00    ./a.out

同样,第二个数字是我们正在寻找的 PGID。

获得此 ID 后,我们可以使用kill命令向组发送SIGINT信号。为此,我们需要在信号和进程组 ID 中添加否定符号“-”。这告诉kill命令将给定的 ID 视为一个组。此外,我们建议在信号和进程组之间添加双破折号“- -”,以便kill可以将信号发送到其中的所有进程:

$ kill -SIGINT -- -1234 

此命令保证将SIGINT信号发送 到层次结构中的所有进程。此外,否定符号确保在没有系统块的情况下中断子进程。