Linux 如何“杀死”一个进程?

ser*_*ant 94 process kill process-management

我常常感到困惑的是,尽管我从事计算机专业工作几十年,Linux 工作十年,但我实际上将操作系统的大部分功能视为黑匣子,与魔术没什么两样。

今天我想到了这个kill命令,虽然我每天多次使用它(无论是“正常”还是-9风格),我必须承认我完全不知道它在幕后是如何工作的。

在我看来,如果一个正在运行的进程被“挂起”,我会调用kill它的 PID,然后它突然不再运行了。魔法!

那里到底发生了什么?联机帮助页谈论“信号”,但这肯定只是一种抽象。发送kill -9到进程不需要进程的合作(如处理信号),它只是将其杀死。

  • Linux 如何阻止进程继续占用 CPU 时间?
  • 是否从调度中删除?
  • 它是否断开进程与其打开的文件句柄的连接?
  • 进程的虚拟内存是如何释放的?
  • 内存中是否有一个全局表之类的东西,Linux 保留对进程占用的所有资源的引用,当我“杀死”一个进程时,Linux 只是简单地遍历该表并一个一个地释放资源?

我真的很想知道这一切!

gol*_*cks 75

向进程发送 kill -9 不需要进程的合作(如处理信号),它只是将其杀死。

您假设因为某些信号可以被捕获和忽略,所以它们都涉及合作。但根据man 2 signal,“信号SIGKILL 和 SIGSTOP 不能被捕获或忽略”。SIGTERM 可以被捕获,这就是为什么plainkill并不总是有效的原因——通常这意味着进程处理程序中的某些东西出错了。1

如果进程没有(或不能)为给定信号定义处理程序,则内核执行默认操作。 在 SIGTERM 和 SIGKILL 的情况下,这是终止进程(除非它的 PID 为 1;内核不会终止init2 意味着它的文件句柄被关闭,它的内存返回到系统池,它的父进程收到 SIGCHILD,它的孤儿子代由 init 等继承,就像它已经调用过一样exit(参见 参考资料man 2 exit)。该进程不再存在——除非它最终成为僵尸,在这种情况下,它仍然与一些信息一起列在内核的进程表中;当它的父级没有时会发生这种情况wait并妥善处理这些信息。然而,僵尸进程不再有任何内存分配给它们,因此无法继续执行。

内存中是否有一个全局表之类的东西,Linux 保留对进程占用的所有资源的引用,当我“杀死”一个进程时,Linux 只是简单地遍历该表并一个一个地释放资源?

我认为这已经足够准确了。物理内存按页(一页通常等于 4 KB 块)进行跟踪,这些页从全局池中取出并返回到全局池。它有点复杂,因为一些释放的页面会被缓存,以防再次需要它们包含的数据(即从仍然存在的文件中读取的数据)。

联机帮助页谈论“信号”,但这肯定只是一种抽象。

当然,所有信号都是抽象的。它们是概念性的,就像“过程”一样。我在玩语义,但如果你的意思是 SIGKILL 与 SIGTERM 在性质上不同,那么是和否。是的,因为它无法被捕获,但不是,因为它们都是信号。以此类推,苹果不是橙子,但根据先入为主的定义,苹果和橙子都是水果。SIGKILL 看起来抽象,因为你无法捕捉它,但它仍然是一个信号。这是 SIGTERM 处理的一个例子,我相信你以前见过这些:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

void sighandler (int signum, siginfo_t *info, void *context) {
    fprintf (
        stderr,
        "Received %d from pid %u, uid %u.\n",
        info->si_signo,
        info->si_pid,
        info->si_uid
    );
}

int main (void) {
    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_sigaction = sighandler;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGTERM, &sa, NULL);
    while (1) sleep(10);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这个过程将永远休眠。您可以在终端中运行它并使用 SIGTERM 发送它kill。它会吐出类似的东西:

Received 15 from pid 25331, uid 1066.
Run Code Online (Sandbox Code Playgroud)

1066 是我的 UID。PID 将是从中kill执行的 shell的 PID,或者如果您 fork 它 ( kill 25309 & echo $?) ,则是 kill 的 PID 。

同样,为 SIGKILL 设置处理程序没有意义,因为它不起作用。3如果 Ikill -9 25309进程将终止。但这仍然是一个信号;内核有关于谁发送信号,它是什么类型的信号等的信息。


1. 如果您还没有查看可能的信号列表,请参阅kill -l

2. 另一个例外,正如 Tim Post 在下面提到的,适用于处于不间断睡眠状态的进程。在根本问题得到解决之前,这些无法被唤醒,因此所有信号(包括 SIGKILL)都会在此期间延迟。然而,流程不能故意创造这种情况。

3. 这并不意味着kill -9在实践中使用更好。我的示例处理程序是一个糟糕的处理程序,因为它不会导致exit(). SIGTERM 处理程序的真正目的是让进程有机会做一些事情,比如清理临时文件,然后自动退出。如果你使用kill -9,它就没有这个机会,所以只有在“自愿退出”部分似乎失败时才这样做。

  • 值得一提的是,当进程处于该状态时,磁盘睡眠 (D) 会抢占所有信号。因此,试图“kill -9”某些 I/O 绑定进程不会起作用,至少不会立即起作用。 (12认同)
  • 我要补充一点,因为无法捕获`kill -9`,接收它的进程在退出之前不能执行任何清理(例如删除临时文件、释放共享内存等)。因此,仅将 `kill -9`(又名 `kill -kill`)用作最后的手段。首先以 `kill -hup` 和/或 `kill -term` 开始,然后使用 `kill -kill` 作为最后一击。 (7认同)