kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); // 参数CLONE_FS | CLONE_SIGHAND表示0号线程和1号线程分别共享文件系统(CLONE_FS)、打开的文件(CLONE_FILES)和信号处理程序 (CLONE_SIGHAND)。
这个进程就是著名的pid为1的init进程(内核态的),它会继续完成剩下的初始化工作比且创建若干个用于高速缓存和虚拟主存管理的内核线程,如kswapd和bdflush等,然后execve(/sbin/init)(生成用户态的init进程pid=1,因为没有调用fork(),所以pid还是1!), 成为系统中的其他所有进程的祖先。回过头来看pid=0的进程,在创建了init进程后(内核态的),pid=0的进程调用 cpu_idle()在主cpu中演变成了idle进程。而内核态pid=1的init进程同样会在各个从cpu上生成idel进程。 init在演变成/sbin/init之前,会执行一部分初始化工作,其中一个就是smp_prepare_cpus(),初始化SMP处理器,在这过程中会在处理每个从处理器时调用
task =copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL, NULL, 0); init_idle(task, cpu);
即从init中复制出一个进程,并把它初始化为idle进程(pid仍然为0)。从处理器上的idle进程会进行一Activate工作,然后执行cpu_idle()。
执行/sbin/init,这样从内核太过度到用户态,按照配置文件/etc/inittab 要求完成启动的工作,并且创建若干个不编号为1,2,3...号的终端注册进程getty,其作用就是设置其进程组的标识号,监视配置到系统终端的接口电路,当有信号来到的时候,getty会执行execve()生成注册进程login,用户可以注册登录了,如果登录成功,则login会演化为shell进程,若login不成功则关闭打开的终端线路,用户1号进程会创建新的getty。到这里init的流程基本完成了。奉上大图一张!
init的过程:
init 的流程讲完了,这里粗略说说init还做了什么事。先来认识下运行级别。
运行级别: linux可以在不同的场合启动不同的开机启动程序,这就叫做运行级别。根据不同的运行级别启动不同的程序。例如在用作服务器的时候要开启Apache,而桌面就不需要。
linux预先设置了7种运行级别(0--6)。ubuntu有8种(0--6、S),这里主要以ubuntu来说。0:关闭系统,1:系统进入单用户模式,S:单用户恢复模式,文本登录界面,只运行少数的系统服务。2:多用户模式(系统默认的级别),图形登录界面,运行所有预定的系统服务。3--5:多用户模式,图形登录界面,运行所有预定的系统服务(对于系统定制而言,运行级别2-5的作用等同),6:重启系统。
对于每个运行级别,在/etc/都有对应的子录目---/etc/rcN.d 用来指定要加载的程序。
细心看的话可以发现,除README外其他的文件都是“S开头+两位数字+程序名”的形式。这代表什么呢?S代表Start启动。如果是K的话则代表关闭kill,如果从其他的运行级别切换过来的话则要关闭程序。之后的数字为处理的顺序,越小则越早执行,如果数字相同,按字母的顺序启动。
上图可以看出这里的文件都是链接文件,为什么呢?上面说过各种运行级别有各自的一个录目用来存放各自的开机程序,如果有多个运行级别要启动同一个程序,那么这个程序的脚本会被拷贝到每一个录目里,这样做的话,如果要修改启动脚本就要修改每一个录目,这样不科学啊!!!!所以这些文件都是链接文件指向/etc/init.d。启动时就是运行这些脚本的。
子系统的初始化:
内核选项:linux 允许用户传递内核配置选项给内核,内核在初始化的过程中调用parse_args()函数对这些选项进行解析,之后调用相应的函数进行处理。对于parse_args()函数,其能够解析形如“变量名=值”的字符串,在模块加载的时候也会被调用来解析模块的参数。
子系统的初始化:在完成内核选项的解析后,就进入初始化的函数调用。在kernel_init()函数中调用do_basic_setup()函数再去调用do_initcalls()函数来完成。各个函数具体实现请查阅源代码!
登录:登录有三种方式。
1) 命令行登录