linux进程初始化

1.在 boot/ 目录中引导程序把内核从磁盘上加载到内存中,并让系统进入保护模式下运行后,就开始执行系统初始化程序 init/main.c
2.该程序首先确定如何分配使用系统物理内存,然后调用内核各部分的初始化函数分别对内存管理、中断处理、块设备和字符设备、进程管理以及硬盘和软盘硬件进行初始化处理
3.在完成了这些操作之后,系统各部分已经处于可运行状态,此后程序把自己“手工”移动到任务 0(进程 0 )中运行,并使用 fork() 调用首次创建出进程 1
4.在进程 1 中程序将继续进行应用环境的初始化并执行 shell 登录程序
5.而原进程 0 则会在系统空闲时被调度执行,此时任务 0 仅执行 pause() 系统调用,并又会调用调度函数。
6.“移动到任务 0 中执行”这个过程由宏 move_to_user_mode ( include/asm/system.h )完成,它把 main.c程序执行流从内核态(特权级 0 )移动到了用户态(特权级 3 )的任务 0 中继续运行
7.在移动之前,系统在对调度程序的初始化过程( sched_init() )中,首先对任务 0 的运行环境进行了设置。这包括人工预先设置好任务 0 数据结构各字段的值( include/linux/sched.h )、在全局描述符表中添入任务 0 的任务状态段( TSS )描述符和局部描述符表( LDT )的段描述符,并把它们分别加载到任务寄存器 tr 和局部描述符表寄存器 ldtr 中。
8.内核初始化是一个特殊过程,内核初始化代码也即是任务 0 的代码
9.从任务 0数据结构中设置的初始数据可知,任务 0 的代码段和数据段的基址是 0 、段限长是 640KB 。而内核代码段和数据段的基址是 0 、段限长是 16MB ,因此任务 0 的代码段和数据段分别包含在内核代码段和数据段中
10.内核初始化程序 main.c 也即是任务 0 中的代码,只是在移动到任务 0 之前系统正以内核态特权级0 运行着 main.c 程序。宏 move_to_user_mode 的功能就是把运行特权级从内核态的 0 级变换到用户态的3 级,但是仍然继续执行原来的代码指令流。
11.在移动到任务 0 的过程中,宏 move_to_user_mode 使用了中断返回指令造成特权级改变的方法。该方法的主要思想是在堆栈中构筑中断返回指令需要的内容,把返回地址的段选择符设置成任务 0 代码段选择符,其特权级为 3 。此后执行中断返回指令 iret 时将导致系统 CPU 从特权级 0 跳转到外层的特权级3 上运行
12.宏 move_to_user_mode 首先往内核堆栈中压入任务 0 数据段选择符和内核堆栈指针。然后压入标志寄存器内容。最后压入任务 0 代码段选择符和执行中断返回后需要执行的下一条指令的偏移位置。该偏移位置是 iret 后的一条指令处。当执行 iret 指令时, CPU 把返回地址送入 CS:EIP 中,同时弹出堆栈中标志寄存器内容。
13.由于 CPU判断出目的代码段的特权级是 3 ,与当前内核态的 0 级不同。于是 CPU 会把堆栈中的堆栈段选择符和堆栈指针弹出到 SS:ESP 中,由于特权级发上了变化,段寄存器 DS 、 ES 、 FS 和 GS 的值变得无效,此时CPU 会把这些段寄存器清零,因此在执行了 iret 指令后需要重新加载这些段寄存器。此后,系统就开始以特权级 3 运行在任务 0 的代码上。所使用的用户态堆栈还是原来在移动之前使用的堆栈。而其内核态堆栈则被指定为其任务数据结构所在页面的顶端开始( PAGE_SIZE + (long)&init_task )
14.由于以后在创建新进程时,需要复制任务 0 的任务数据结构,包括其用户堆栈指针,因此要求任务 0 的用户态堆栈在创建任务 1 (进程 1 )之前保持“干净”状态。