init 进程:所有进程的祖先?

init 进程:所有进程的祖先?

我一直都知道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.modprobesysctl值中的程序来触发模块加载事件(例如,当发现新硬件时,或者当首次使用某些网络协议时)。当程序转储核心时,内核会调用由kernel.core_pattern如果有的话。这些进程是用户进程,但是他们的父母注册为kthreadd

答案2

  1. 内核的结构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 },  \
        }, }                                                 \
    }
    
  2. 在内核启动过程中产生两个线程: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。

相关内容