linux操作系统中第一个进程是0号进程是为道,其主要任务是不断进行进程调度。
第一个cpu上的IDLE进程
内核在启动过程中第一个cpu进入start_kernel逻辑,这个逻辑是内核初始化入口。
内核初始化的过程逻辑比较复杂,......略。
在执行该内核初始化逻辑前,0号进程已经绑定到当前的cpu上了。
// init/main.c
// 在第一个cpu上执行 该进程是0号进程
void start_kernel(void) {
...
// 初始化操作
...
arch_call_rest_init();
}
0号进程的调度逻辑:
// init/main.c
void arch_call_rest_init(void) {
rest_init();
}
// init/main.c
void rest_init(void) {
int pid;
// ...
// 0号进程创建1号进程init
pid = kernel_thread(kernel_init,...);
// ...
// 0号进程创建2号进程kthreadd
pid = kernel_thread(kthreadd,...);
...
// 调度逻辑
cpu_startup_entry(CPUHP_ONLINE);
}
1号和2号进程
PPID是指父进程的ID,通过执行ps -ef可以发现,用户态进程的祖先都是1号init进程。
僵尸/孤儿进程最后都会被init进程清理和托管。
僵尸进程管理
父子进程两者运行是相互独立的,父进程不清楚子进程什么时候结束。
子进程退出后,父进程会使用wait或waitpid进行回收子进程资源,获得子进程终止状态。
子进程先于父进程退出,父进没有使用使用wait或waitpid回收子进程资源,子进程会残留资源 存放于内核中,变成僵尸进程。
子进程调用exit结束时,内核释放了该进程所有资源,包括打开的文件、占用的内存等。
子进程信有些息直到父进程通过 wait()/waitpid()才会被释放,进程号、退出状态、运行时间。
孤儿进程管理
父进程退出,它的子进程还在运行变成孤儿进程。
内核会把孤儿进程的父进程设置为init,完成状态收集工作。
init进程还给其它cpu创建IDLE进程。
IDLE进程while循环尝试进程调度,保证cpu一直有机器指令在执行。
道生一二0号进程创建了1号和2号进程,然后调用cpu_startup_entry:
// kernel/sched/idle.c
void cpu_startup_entry(...) {
// 0号是优先级最低的进程 当没有进程被调度时 就选择0号进程
// 0号进程while循环 不断地执行调度操作
// 由此0号进程被叫做IDLE进程
while (1)
do_idle();
}
// kernel/sched/idle.c
static void do_idle(void) {
...
schedule_idle();
...
}
// kernel/sched/core.c
void schedule_idle(void) {
...
__schedule(false);
...
}
其它cpu上的IDLE进程
上述可知第一个cpu有个IDLE进程不断的执行while循环。
其它cpu也有IDLE进程不断的执行,道生万物这些IDLE进程都是第一个cpu的IDLE进程创建出来的。
在rest_init方法中第一个cpu的IDLE进程调用kernel_thread创建了1号进程。
入口函数为kernel_init()也叫INIT进程:
// init/main.c
static int kernel_init(void *unused) {
...
kernel_init_freeable();
...
}
// init/main.c
static void kernel_init_freeable(void) {
...
smp_init();
...
}
// kernel/smp.c
void smp_init(void) {
// ...
// 创建其他IDLE进程
idle_threads_init();
pr_info("Bringing up secondary CPUs ...\n");
// ...
// 启动其他cpu
for_each_present_cpu(cpu) {
...
cpu_up(cpu);
}
}
在smp_inits中通过idle_threads_init过方法复制出IDLE进程们。
如操作系统有8个cpu,除第一个cpu当前进程外,要复制出7个IDLE进程。
如某cpu没有绑定IDLE进程,则调用fork_idle创建调用per_cpu绑定。
// kernel/smpboot.c
void idle_threads_init(void) {
unsigned int cpu, boot_cpu;
boot_cpu = smp_processor_id();
for_each_possible_cpu(cpu) {
if (cpu != boot_cpu)
idle_init(cpu);
}
}
// kernel/smpboot.c
static void idle_init(unsigned int cpu) {
struct task_struct *tsk = per_cpu(idle_threads, cpu);
if (!tsk) {
// 复制进程
tsk = fork_idle(cpu);
per_cpu(idle_threads, cpu) = tsk;
}
}
IDLE进程们初始化完成后加载其余cpu。
入口函数是secondary_start_kernel:
// arch/arm64/kernel/smp.c
void secondary_start_kernel(void) {
...
cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
}
// kernel/sched/idle.c
void cpu_startup_entry(...) {
while (1)
do_idle();
}