在Linux内核启动到最后时,内核会调用三个进程,PID分别为0,1,2,其中就包含了init进程(PID=1)。init进程可以说是之后所有用户进程的父进程了,它自从产生便一直存在在内存空间中直到关机。
在系统启动后,使用ps命令查看进程列表可以在最开始找到init进程,ps命令打印的是所有用户态下的进程,这是不是可以说明init进程就是一个用户进程呢?答案是否定的,init进程实际上最初是内核进程,在经过一段时间后才变为用户进程的。尽管这样,实际上init进程完成的绝大多数工作是在用户态下进行的。
init进程在内核态时,其本身实质上就是一个函数,内容如下:
static int __init kernel_init(void * unused)
{
/*
* Wait until kthreadd is all set-up.
*/
wait_for_completion(&kthreadd_done);
lock_kernel();
/*
* init can allocate pages on any node
*/
set_mems_allowed(node_states[N_HIGH_MEMORY]);
/*
* init can run on any cpu.
*/
set_cpus_allowed_ptr(current, cpu_all_mask);
/*
* Tell the world that we're going to be the grim
* reaper of innocent orphaned children.
*
* We don't want people to have to make incorrect
* assumptions about where in the task array this
* can be found.
*/
init_pid_ns.child_reaper = current;
cad_pid = task_pid(current);
smp_prepare_cpus(setup_max_cpus);
do_pre_smp_initcalls();
start_boot_trace();
smp_init();
sched_init_smp();
do_basic_setup();
/* Open the /dev/console on the rootfs, this should never fail */
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(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();
}
/*
* 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..
*/
init_post();
return 0;
}
分析这个函数,init进程在内核态主要完成了三件事情,其一是调用sys_open打开了控制台,同时也得到了内核的第一个文件描述符,然后进程又连续调用了两次sys_dup函数再次得到了两个文件描述符,这样系统就得到了三个文件描述符,意义分别是标准输入,标准输出,标准错误文件。之后所有的用户进程由于继承了init进程,所以最初就具有了这三个文件描述符。其二是调用prepare_namespace函数挂载根文件系统,关于根文件系统的描述(根文件系统存放位置,根文件格式)可以通过设置uboot的bootargs参数的方式给出,比如bootargs=console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3,根文件系统存放位置为第0个MMC设备的第二个分区中,根文件格式是ext3。如果挂载失败,内核会自动在5秒后进行重启。第三,内核寻找根文件系统中的init进程程序,并且执行,这个工作是在init_post函数中完成的。在init_post函数中,程序会首先尝试使用bootargs中的给出的init程序位置(/linuxrc),若发现根文件系统并未在此给出init程序,其会尝试另外四个位置寻找init程序,顺序为/sbin/init -> /etc/init -> /bin/init -> /bin/sh,如果还是未能找到init程序,系统默认启动失败。除此之外,init_post函数可以看做是init进程由内核态到用户态的过渡。
init进程过渡到用户态后,其便开始调度其他进程直到建立起我们最后看到的终端界面,调度的进程包括了login进程,命令行进程,shell进程等等。