我想了解更多关于 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=NULL
和 init_task.active_mm=&init_mm
,其中堆栈区 init_stack
和 mm_struct
init_mm
都是静态定义的。 active_mm
非 NULL 意味着 process 0
是一个内核进程。另外, init_task.flags=PF_KTHREAD
. 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
-> mm
和 active_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作为堆栈溢出标志。这是结构图。
进程 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)
所以定义了默认大小。
.mm = NULL,
.active_mm = &init_mm,
让我们回顾一下
start_kernel
, rest_init
将初始化 kernel_init
和 kthreadd
.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/