sys_fork()

来源:互联网 发布:知乎 重庆鸳鸯锅 编辑:程序博客网 时间:2024/06/03 23:43

eax作为返回值应该是gcc的约定吧,所以你先要把握这个约定才行~~之后才可能理解其他的代码~~
大概的变化过程是这个样子:
1、在 调用 fork 的时候,触发了系统调用 sys_fork (这个你要理解_syscall0这个宏才行)
2、接着进入了系统调用sys_fork(因为之前有注册系统调用0x80,为system_call,而system_call里头有根据系统调用编号来处理sys_fork的语句)
sys_fork:
    call find_empty_process
    testl %eax,%eax
    js 1f
    push %gs
    pushl %esi
    pushl %edi
    pushl %ebp
    pushl %eax
    call copy_process
    addl $20,%esp
1:  ret
3、之后就是执行上面的代码,
先找出一个空的进程控制块结构和一个新的pid,如果发现没有找到,直接退出中断,否则把重要信息入栈,最后一个入栈的即为find_empty_process的返回值,即eax,里头刚好是那个空的进程控制块的编号。
这里的eax是进程控制块的编号~~
接着进入copy_process,在里头开始复制进程控制块。在分析copy_process之前,必须理解copy_process的参数传递问题。
copy_process有这么一大串的参数:
       int nr,long ebp,long edi,long esi,long gs,long none,
        long ebx,long ecx,long edx,
        long fs,long es,long ds,
        long eip,long cs,long eflags,long esp,long ss
都必须明确它们的来源才行,它们来自栈?自上而下分别是:
ss   #这里是发生中断后cpu自动压入栈中的
esp
eflags
cs
eip  #这个是cpu自动存入的,在发生中断时的下一条指令,即_syscall0中int 0x80之后的代码
      # if (__res >= 0) return (type) __res;要注意,这里的eax跟__res是绑定的,你看看: "=a" (__res)就知道了,所以__res即eax
ds  #这里开始到none都是从system_call那里开始压入栈中的
es
fs
edx
ecx
ebx
none  # 这个是什么呢,我们根本没有找到对应的push指令,但是你有没有发现在call *sys_call_table(,%eax,4)跳转到sys_fork时,
         # 等同于push eip; jmp *sys_call_table(,%eax,4),所以none就是call *sys_call_table(,%eax,4)的下一条指令
         #不过这个东西仅仅是用来占位了,在copy_process中根本没有用到
gs   #这里一直到nr都是从sys_fork那里亚入栈中的
esi
edi
ebp
nr   --> eax
"addl $20, %esp"的地址

okay,参数理解以后,我们接着看下面的关键代码:
    p->tss.eip = eip;    ...
    p->tss.eax = 0;
注意这两句,第一句是说进程1的TSS里头的eip即cpu压入到栈里头的eip,即if (__res >= 0) 的地址,而eax则是0
执行完copy_process以后,进程1就可以参与调度了,因为它的状态被设置成了TASK_RUNNING,见p->state = TASK_RUNNING

4、接着执行sys_fork的最后两条指令
addl $20,%esp
1:  ret
第一条是把esp增加20,刚好是gs, esi,edi,ebp,eax,以及ip这几个寄存器所占的栈的空间,即4*5。另外,注意一下"addl $20, %esp"的地址已经被copy_process的返回指令ret弹出来了,所以这里不用管。
指定完 addl $20,%esp,下面就是ret,它接着从栈中弹出一个地址,即 none,也即是 call *sys_call_table(,%eax,4)的下一条指令,我们看到,刚好是 pushl %eax,我们发现这个 eax,刚好是 copy_process 返回之前 return 的 last_pid,

接着,将执行如下指令

    movl current,%eax
    cmpl $0,state(%eax)     # state
    jne reschedule
    cmpl $0,counter(%eax)       # counter
    je reschedule
后续的就是比较当前current,即子进程的状态是否可以运行,如果当前进程不再就绪状态就去执行调度程序,如果该任务在就绪状态但时间片用完了,也就执行调度程序。所有后续的情况是,我们无法确定进程0或者进程1先执行,但是返回值已经明显确定了。

即进程0的返回值为last_pid,即子进程的pid;而子进程,即进程1的返回值呢,则为0,因为在copy_proces的时候已经设置为了0。