1.* idle进程 PID = 0
idle进程 由系统自动创建, 运行在内核态
idle进程其pid=0,其前身是系统创建的第一个进程,也是唯一一个没有通过fork或者kernel_thread产生的进程。完成加载系统后,演变为进程调度、交换
2. init进程
init进程由idle通过kernel_thread创建,在内核空间完成初始化后, 加载init程序, 并最终用户空间
由0进程创建,完成系统的初始化. 是系统中所有其它用户进程的祖先进程
Linux中的所有进程都是有init进程创建并运行的。首先Linux内核启动,然后在用户空间中启动init进程,再启动其他系统进程。在系统启动完成完成后,init将变为守护进程监视系统其他进程。
2.1 (kernel_init 函数)
static int __ref kernel_init(void *unused)
{
int ret;
kernel_init_freeable(); /* init 进程的一些其他初始化工作 */
/* need to finish all async __init code before freeing the memory */
async_synchronize_full(); /* 等待所有的异步调用执行完成 */
free_initmem(); /* 释放 init 段内存 */
mark_rodata_ro();
system_state = SYSTEM_RUNNING; /* 标记系统正在运行 */
numa_default_policy();
flush_delayed_fput();
/* ramdisk_execute_command 是一个全局的 char 指针变量,此变量值为“/init”,
* 也就是根目录下的 init 程序。 ramdisk_execute_command 也可以通过 uboot 传递,
* 在 bootargs 中 使用“rdinit=xxx”即可, xxx 为具体的 init 程序名字。*/
if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
if (!ret)
return 0;
pr_err("Failed to execute %s (error %d)\n",
ramdisk_execute_command, ret);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
/* 如果 ramdisk_execute_command 为空的话就看 execute_command 是否为空,反
* 正不管如何一定要在根文件系统中找到一个可运行的 init 程序。 execute_command 的值是通过
* uboot 传递,在 bootargs 中使用“init=xxxx”就可以了,比如“init=/linuxrc”表示根文件系统中
* 的 linuxrc 就是要执行的用户空间 init 程序
*/
if (execute_command) {
ret = run_init_process(execute_command);
if (!ret)
return 0;
panic("Requested init %s failed (error %d).",
execute_command, ret);
}
/* ramdisk_execute_command 和 execute_command 都为空 */
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;
/* 如果以上步骤都没有找到用户空间的 init 程序,那么就提示错误发生! */
panic("No working init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
}
2.2 kernel_init_freeable 函数
static noinline void __init kernel_init_freeable(void)
{
/*
* Wait until kthreadd is all set-up.
*/
wait_for_completion(&kthreadd_done); /* 等待 kthreadd 进程准备就绪 */
/* Now the scheduler is fully set up and can do blocking allocations */
gfp_allowed_mask = __GFP_BITS_MASK;
/*
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_MEMORY]);
/*
* init can run on any cpu.
*/
set_cpus_allowed_ptr(current, cpu_all_mask);
cad_pid = task_pid(current);
smp_prepare_cpus(setup_max_cpus);
do_pre_smp_initcalls();
lockup_detector_init();
smp_init(); /* SMP 初始化 */
sched_init_smp(); /* 多核(SMP)调度初始化 */
do_basic_setup(); /* 设备初始化都在此函数中完成 */
/*
打开设备“/dev/console”,在 Linux 中一切皆为文件!因此“/dev/console”也
是一个文件,此文件为控制台设备。每个文件都有一个文件描述符,此处打开的“/dev/console”
文件描述符为 0,作为标准输入(0)。
*/
/* Open the /dev/console on the rootfs, this should never fail */
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
pr_err("Warning: unable to open an initial console.\n");
/*
sys_dup 函数将标准输入(0)的文件描述符复制了 2 次,一个作为标准
输出(1),一个作为标准错误(2)。
*/
(void) sys_dup(0);
(void) sys_dup(0);
/*
* check if there is an early userspace init. If yes, let it do all
* the work
*/
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
/* 调用函数 prepare_namespace 来挂载根文件系统。跟文件系统也是由命令行参
数指定的,也就是 uboot 的 bootargs 环境变量*/
prepare_namespace();
}
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*
* rootfs is available now, try loading the public keys
* and default modules
*/
integrity_load_keys();
load_default_modules();
}
do_basic_setup 函数用于完成 Linux 下设备驱动初始化工作!非常重要。
do_basic_setup 会调用 driver_init 函数完成 Linux 下驱动模型子系统的初始化。
3. kthreadd进程
由idle通过kernel_thread创建,并始终运行在内核空间, 负责所有内核线程的调度和管理
它的任务就是管理和调度其他内核线程kernel_thread, 会循环执行一个kthreadd的函数,该函数的作用就是运行kthread_create_list全局链表中维护的kthread, 当我们调用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程