父子进程之间的信号传递
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信号发送 到层次结构中的所有进程。此外,否定符号确保在没有系统块的情况下中断子进程。