Linux process/thread 可以在不通过 do_exit() 的情况下终止吗?

Can a Linux process/thread terminate without pass through do_exit()?

为了验证我想使用的第三方二进制分布式软件的行为,我正在实施一个内核模块,其 objective 用于跟踪该软件生成和终止的每个子项。

目标二进制文件是 Golang 生成的,它是大量多线程的。 我编写的内核模块在内核函数 _do_fork()do_exit() 上安装挂钩以跟踪每个 process/thread 这个二进制文件产生并终止。

LKM 或多或少起作用了。

然而,在某些情况下,我有一个我无法解释的场景。 似乎 process/thread 可以在不通过 do_exit().

的情况下终止

我通过放置 printk() 收集的证据显示进程创建但不表示进程终止。

我知道 printk() 可能会很慢,而且我也知道在这种情况下消息可能会丢失。

试图防止由于控制台速度慢而导致的消息丢失(对于这个特定的应用程序,使用了串行 tty 115200),我尝试实现一个更快的控制台,并且已经使用 netconsole[=57 收集了消息=].

所描述的设置似乎确认进程可以在不通过 do_exit() 函数的情况下终止。

但是因为我不确定我的消息不会在 printk() 基础设施上丢失,所以我决定重复相同的测试但替换 printk()ftrace_printk(),这应该是 printk().[= 的更精简替代方案13=]

仍然是同样的结果,偶尔我看到进程没有通过do_exit(),并验证PID当前是否为运行,我不得不面对不是运行.

的事实

另请注意,我将钩子放在 do_exit() 内核函数中作为第一条指令,以确保函数流不会在被调用函数内终止。

那么我的问题是:

Linux 进程可以在其流不通过 do_exit() 函数的情况下终止吗?

如果是这样,谁能告诉我这个场景可能是什么?

Can a Linux process terminate without its flow pass through the do_exit() function?

可能不是,但您应该研究 Linux kernel to be sure. Ask on KernelNewbies. Kernel threads and udev or systemd 相关事物的源代码(或者 modprobe 或更早的 hotplug)可能是例外。当您的 pid 1 的 /sbin/init 终止时(这不应该发生),奇怪的事情就会发生。

The LKM works, more or less.

这是什么意思?内核模块怎么会半工作?


在现实生活中,有时您的 Linux 内核是 panicking or crashes (and it could happen with your LKM, if it has not been peer-reviewed by the Linux kernel community). In such a case, there is no more any notion of processes,因为它们是由一个活跃的 Linux 内核提供的抽象。

另见 dmesg(1), strace(1), proc(5), syscalls(2), ptrace(2), clone(2), fork(2), execve(2), waitpid(2), elf(5), credentials(7), pthreads(7)

另请查看您的 libc 的源代码,例如GNU libc or musl-libc

当然,见Linux From Scratch and Advanced Linux Programming

And verifying if the PID is currently running,

这可以通过 /proc/ 或使用 kill(2) with a 0 signal (and maybe also pidfd_send_signal(2)...)

的用户空间来完成

PS。我仍然不明白为什么你需要编写内核模块或更改内核代码。我的直觉是尽可能避免这样做。

经过长时间的调试,我终于可以回答我自己的问题了。

这还不是全部;我还能够解释为什么我会看到我在场景中描述的奇怪行为。

让我们从头开始:监控一个高度多线程的应用程序。我观察到极少数情况下,突然停止的 PID 存在而没有观察其流通过 Linux 内核 do_exit() 函数。

因为这是我原来的问题:

Linux 进程是否可以在不通过 do_exit() 函数的情况下终止?

至于我目前的知识,我现在认为这是相当广泛的,Linux 进程不能在不通过 do_exit()函数。

但是这个答案与我的观察相反,导致我提出这个问题的问题仍然存在。

这里有人提出我看到的奇怪行为是因为我的观察有问题,暗示我的方法不准确,至于我的结论。

我的观察是正确的,我看到的过程没有通过do_exit()而是终止了.

为了解释这种现象,我想提出 table 另一个我认为互联网搜索者可能会觉得有用的问题:

两个进程可以共享同一个PID吗?

如果你在一个月前问我这个问题,我肯定会这样回答这个问题:“绝对不能,两个进程不能共享同一个 PID。” 不过,Linux 更复杂。

有一种情况,在Linux系统中,两个不同的进程可以共享同一个PID!

https://elixir.bootlin.com/linux/v4.19.20/source/fs/exec.c#L1141

令人惊讶的是,这种行为并没有伤害任何人;发生这种情况时,这两个进程之一就是僵尸。

如果线程领导者在其 child 线程之前终止,预期的行为是用领导者的 PID 替换 child 之一。这也意味着child的PID突然停止存在。

那是我在看的;我正在监视的 PID 没有通过 do_exit() 因为当相应的线程终止时,它不再有它启动时的 PID,但它有它的领导者的。

对于非常了解 Linux 内核机制的人来说,这不足为奇;此行为是有意为之的,自 2.6.17 以来一直没有改变。 现在5.10.3,还是这样。

希望这对互联网搜索者有用;我还想补充一点,这也回答了以下问题:

  • 问题:Linux process/thread 可以不通过 do_exit() 就终止吗? 答案:不,do_exit() 是进程必须结束其执行的唯一方式——既有意也无意。
  • 问题: 两个进程可以共享同一个PID吗? 回答:通常不会。在极少数情况下,两个可调度实体具有相同的 PID。
  • 问题:Linux 内核是否存在进程更改其 PID 的情况? 回答:是的,至少有一种情况是进程更改其 PID。