gpt4 book ai didi

c - 哪个 Linux 内核函数创建了 'process 0' ?

转载 作者:行者123 更新时间:2023-12-03 13:39:01 26 4
gpt4 key购买 nike

我想了解更多关于 process 0例如,它是否有内存描述符(非NULL task_struct->mm 字段),以及它与交换或空闲进程有什么关系。在我看来,在引导 cpu 上创建了一个“进程 0”,然后 idle_threads_init 为每个其他 cpu 创建了一个空闲线程。 ,但我没有找到第一个(我假设是 process 0 )的创建位置。

更新

鉴于live book tychen 引用的,这是我对 process 0 的最新理解(对于 x86_64),有人可以确认/反驳以下项目吗?

  • init_task 输入 task_struct是静态定义的,具有任务的内核堆栈 init_task.stack = init_stack , 内存描述符 init_task.mm=NULLinit_task.active_mm=&init_mm ,其中堆栈区 init_stack mm_struct init_mm 都是静态定义的。
  • 事实只有active_mm非 NULL 意味着 process 0是一个内核进程。另外, init_task.flags=PF_KTHREAD .
  • 未压缩的内核镜像开始执行后不久,启动 cpu starts使用 init_stack作为内核堆栈。这使得 current宏有意义(自机器启动以来第一次),这使得 fork()可能的。在这一点之后,内核实际上运行在 process 0 中。的上下文。
  • start_kernel -> arch_call_rest_init -> rest_init ,在这个函数中,process 1&2 fork 。内 kernel_init 预定的功能 process 1 ,创建一个新线程(带有 CLONE_VM )并挂接到 CPU 的运行队列的 rq->idle ,对于每个其他逻辑 CPU。
  • 有趣的是,所有空闲线程共享相同的 tid 0 (不仅 tgid )。通常线程共享tgid但有明显的tid , 这真的是 Linux 的 process id .我想它不会破坏任何东西,因为空闲线程被锁定到它们自己的 CPU 上。
  • kernel_init加载 init可执行文件(通常为 /sbin/init ),并同时切换 current -> mmactive_mm到非 NULL mm_struct ,并清除 PF_KTHREAD标志,这使得 process 1一个合法的用户空间进程。虽然 process 2不调整 mm ,意味着它仍然是一个内核进程,与 process 0 相同.
  • rest_init , do_idle接管,这意味着所有 CPU 都有一个空闲进程。
  • 之前有些东西让我困惑,但现在变得清楚了:init_*对象/标签,例如 init_task/init_mm/init_stack都被process 0使用了,而不是 init process , 即 process 1 .
  • 最佳答案

    我们真的从 start_kernel 开始Linux内核, 进程 0/idle 也从这里开始。

    开头start_kernel ,我们调用set_task_stack_end_magic(&init_stack) .该函数将设置 init_task 的堆栈边界,即进程 0/idle。

    void set_task_stack_end_magic(struct task_struct *tsk)
    {
    unsigned long *stackend;

    stackend = end_of_stack(tsk);
    *stackend = STACK_END_MAGIC; /* for overflow detection */
    }

    很容易理解,这个函数获取限制地址并将底部设置为STACK_END_MAGIC作为堆栈溢出标志。这是结构图。

    enter image description here

    进程 0 是静态定义的。这是唯一一个不是由 kernel_thread 创建的进程也不是 fork .

    /*
    * Set up the first task table, touch at your own risk!. Base=0,
    * limit=0x1fffff (=2MB)
    */
    struct task_struct init_task
    #ifdef CONFIG_ARCH_TASK_STRUCT_ON_STACK
    __init_task_data
    #endif
    = {
    #ifdef CONFIG_THREAD_INFO_IN_TASK
    .thread_info = INIT_THREAD_INFO(init_task),
    .stack_refcount = REFCOUNT_INIT(1),
    #endif
    .state = 0,
    .stack = init_stack,
    .usage = REFCOUNT_INIT(2),
    .flags = PF_KTHREAD,
    .prio = MAX_PRIO - 20,
    .static_prio = MAX_PRIO - 20,
    .normal_prio = MAX_PRIO - 20,
    .policy = SCHED_NORMAL,
    .cpus_ptr = &init_task.cpus_mask,
    .cpus_mask = CPU_MASK_ALL,
    .nr_cpus_allowed= NR_CPUS,
    .mm = NULL,
    .active_mm = &init_mm,
    ......
    .thread_pid = &init_struct_pid,
    .thread_group = LIST_HEAD_INIT(init_task.thread_group),
    .thread_node = LIST_HEAD_INIT(init_signals.thread_head),
    ......
    };
    EXPORT_SYMBOL(init_task);


    以下是我们需要明确说明的一些重要事项。
  • INIT_THREAD_INFO(init_task)设置 thread_info如上图。
  • init_stack定义如下

  • extern unsigned long init_stack[THREAD_SIZE / sizeof(unsigned long)];

    其中 THREAD_SIZE 等于

    #ifdef CONFIG_KASAN
    #define KASAN_STACK_ORDER 1
    #else
    #define KASAN_STACK_ORDER 0
    #endif
    #define THREAD_SIZE_ORDER (2 + KASAN_STACK_ORDER)
    #define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)

    所以定义了默认大小。
  • 进程 0 只会在内核空间运行,但在我上面提到的某些情况下它需要一个虚拟内存空间,所以我们设置以下

  •     .mm     = NULL,
    .active_mm = &init_mm,

    让我们回顾一下 start_kernel , rest_init将初始化 kernel_initkthreadd .

    noinline void __ref rest_init(void)
    {
    ......
    pid = kernel_thread(kernel_init, NULL, CLONE_FS);
    ......
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    ......
    }

    kernel_init将运行 execve然后进入用户空间,改为 init通过运行处理,即进程 1。

    if (!try_to_run_init_process("/sbin/init") || 
    !try_to_run_init_process("/etc/init") ||
    !try_to_run_init_process("/bin/init") ||
    !try_to_run_init_process("/bin/sh"))
    return 0;
    kthread成为管理和调度其他内核的守护进程 task_struts ,这是过程2。

    做完这一切,进程0就会变成空闲进程并跳出 rq这意味着它只会在 rq 时运行是空的。

    noinline void __ref rest_init(void)
    {
    ......
    /*
    * The boot idle thread must execute schedule()
    * at least once to get things moving:
    */
    schedule_preempt_disabled();
    /* Call into cpu_idle with preempt disabled */
    cpu_startup_entry(CPUHP_ONLINE);
    }


    void cpu_startup_entry(enum cpuhp_state state)
    {
    arch_cpu_idle_prepare();
    cpuhp_online_idle(state);
    while (1)
    do_idle();
    }

    最后,这里有个好消息 gitbook如果您想更多地了解 Linux 内核,那么为您提供帮助。

    关于c - 哪个 Linux 内核函数创建了 'process 0' ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62204047/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com