我一直都知道init进程是所有进程的祖先。为什么进程 2 的 PPID 为 0?
$ ps -ef | head -n 3
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 May14 ? 00:00:01 /sbin/init
root 2 0 0 May14 ? 00:00:00 [kthreadd]
答案1
首先,“祖先”与“父母”不同。祖先可以是父级的父级……父级的父级,并且内核仅跟踪一级。然而,当一个进程死亡时,它的子进程会被 init 收养,因此在典型的系统上您会看到很多父进程为 1 的进程。
现代 Linux 系统另外还有一些执行内核代码的进程,但就调度而言,它们作为用户进程进行管理。 (它们不遵守通常的内存管理规则,因为它们运行内核代码。)这些进程都是由kthreadd
(它是内核线程的初始化)产生的。您可以通过/proc/2/exe
无法读取(通常是进程可执行文件的符号链接)这一事实来识别它们。另外,ps
用方括号之间的名称列出它们(这对于正常用户进程是可能的,但不常见)。大多数父进程 ID 为 2 的进程都是内核进程,但也有一些 PPID 为 2 的内核辅助进程(见下文)。
进程 1 ( init
) 和 2 ( kthreadd
) 是内核在启动时直接创建的,因此它们没有父进程。在其 ppid 字段中使用值 0 来指示这一点。此处 0 的意思是“内核本身”。
Linux 还具有一些让内核启动用户进程的工具,这些进程的位置通过系统控制参数在某些情况下。例如,内核可以通过调用kernel.modprobe
sysctl值中的程序来触发模块加载事件(例如,当发现新硬件时,或者当首次使用某些网络协议时)。当程序转储核心时,内核会调用由kernel.core_pattern
如果有的话。这些进程是用户进程,但是他们的父母注册为kthreadd
。
答案2
- 内核的结构
task_struct
是由宏代码静态生成的。
这struct task_struct init_task = INIT_TASK(init_task); EXPORT_SYMBOL(init_task);
nr
是全局 pid 编号。它从 0 开始。#define INIT_STRUCT_PID { \ .numbers = { { \ .nr = 0, \ .ns = &init_pid_ns, \ .pid_chain = { .next = NULL, .pprev = NULL }, \ }, } \ }
- 在内核启动过程中产生两个线程:init、kthreadd。
作为内核的自然结果, 它static noinline void __init_refok rest_init(void) { /* spawn init thread */ kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); /* spawn kthreadd thread */ pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); cpu_idle(); }
init process
具有 pid 1 和pid 2。kthreadd process
所以他们的 ppid 是 0。