在分析进程1如何开始执行之前,先回顾一下进程0创建进程1的过程。在3.1.3节中讲解调用copy_process函数时曾强调过,当时为进程1设置的tss.eip就是进程0调用fork( )创建进程1时int 0x80中断导致的CPU硬件自动压栈的ss、esp、eflags、cs、eip中的EIP值,这个值指向的是int 0x80的下一行代码的位置,即if (__res >= 0)。前面讲述的ljmp 通过CPU的任务门机制自动将进程1的TSS的值恢复给CPU,自然也将其中的tss.eip恢复给CPU。现在CPU中的EIP指向的就是fork中的if (__res >= 0)这一行,所以,进程1就要从这一行开始执行。执行代码如下:
//代码路径:include/unistd.h: #define _syscall0(type,name) \ int fork(void) { long __res; __asm__ volatile ("int $0x80" : "=a" (__res) : "0" (__NR_ fork)); if (__res >= 0) //现在从这行开始执行,copy_process为进程1做的tss.eip就是指向这一行 return (int) __res; errno= -__res; return -1; }回顾前面3.1.3节中的介绍可知,此时的__res值,就是进程1的TSS中eax的值,这个值在3.1.3节中被写死为0,即p->tss.eax = 0,因此,当执行到return (type) __res这一行时,返回值是0,如图3-15所示。
返回后,执行到main()函数中if (!fork( ))这一行,! 0为“真”,调用init()函数!执行代码如下:
//代码路径:init/main.c: void main(void) { … if (!fork()) { //!0为真, init(); //这次要执行这一行!代码跨度比较大,请参看3.1.3节 } }进入init()函数后,先调用setup()函数,执行代码如下:
//代码路径:init/main.c: void init(void) { … setup((void *) &drive_info); … }本章后续的内容都是setup( )函数实现的。这个函数的调用与fork( )、pause( )函数的调用类似;略有区别的是setup( )函数不是通过_syscall0( )而是通过_syscall1( )实现的;具体的实现过程基本类似,也是通过int 0x80、_system_call、call _sys_call_table(,