[linux 0.11]fork()--子进程的返回
fork()是linux下创建进程的函数,这里通过linux 0.11分析下fork()创建进程后,子进程是如何返回的,但并不打算分析完整的fork()。 fork()是1个系统调用(int 0x80),主要由find_empty_process和copy_process两个内核函数组成。 当调用fork()时(int 0x80),cpu会自动将调用fork()时的代码段c
fork()是linux下创建进程的函数,这里通过linux 0.11分析下fork()创建进程后,子进程是如何返回的,但并不打算分析完整的fork()。
fork()是1个系统调用(int 0x80),主要由find_empty_process和copy_process两个内核函数组成。
当调用fork()时(int 0x80),cpu会自动将调用fork()时的代码段cs和fork()指令的下一句指令地址eip压栈,在执行copy_process时,将此cs,eip作为copy_process的部分参数,而copy_process内将复制父进程的TSS给子进程,也即将cs,eip做为子进程的入口地址。
但是copy_process并不是将父进程的TSS完全复制给子进程,而是将子进程TSS段内的eax字段设置为0,而eax正是函数fork()的返回值。
copy_process中最后将子进程设置为就绪态(TASK_RUNNING),父进程就从fork()中返回。
NOTE!子进程实际上并没有执行fork() ,fork()只不过是创建了一个新进程,而它被调度执行时候的eax,cs,eip早已被精心设计过了。
看个简单的关于fork()的小示例:
//fork.c:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
pid_t pid;
if((pid=fork())==0) //注意这里!对应下面的反汇编
{
printf("child/n");
}
else if(pid>0)
{
printf("parent/n");
}
else
{
printf("error/n");
}
exit(0);
}
将其编译:gcc fork.c -o fork.out
对fork.out进行反汇编:objdump -d fork.out后,截取关键代码段如下:
08048414 <main>:
8048414: 55 push %ebp
8048415: 89 e5 mov %esp,%ebp
8048417: 83 e4 f0 and $0xfffffff0,%esp
804841a: 83 ec 20 sub $0x20,%esp
804841d: e8 1e ff ff ff call 8048340 <fork@plt>
8048422: 89 44 24 1c mov %eax,0x1c(%esp) #eax值传给0x1c(%esp)
8048426: 83 7c 24 1c 00 cmpl $0x0,0x1c(%esp) #0x1c(%esp)与0比较,实际就是eax与0比较
804842b: 75 0e jne 804843b <main+0x27>
804842d: c7 04 24 34 85 04 08 movl $0x8048534,(%esp)
8048434: e8 f7 fe ff ff call 8048330 <puts@plt> # printf("child/n");
8048439: eb 21 jmp 804845c <main+0x48>
804843b: 83 7c 24 1c 00 cmpl $0x0,0x1c(%esp)
8048440: 7e 0e jle 8048450 <main+0x3c>
8048442: c7 04 24 3a 85 04 08 movl $0x804853a,(%esp)
8048449: e8 e2 fe ff ff call 8048330 <puts@plt> # printf("parent/n");
804844e: eb 0c jmp 804845c <main+0x48>
8048450: c7 04 24 41 85 04 08 movl $0x8048541,(%esp)
8048457: e8 d4 fe ff ff call 8048330 <puts@plt> # printf("error/n");
804845c: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048463: e8 e8 fe ff ff call 8048350 <exit@plt>
我们调用fork()后,cpu自动压栈的eip 就是 指令:call 8048340 <fork@plt>下一句指令 mov %eax,0x1c(%esp)的地址0x8048422。
从反汇编代码中我们可以看到eax正是fork()的返回值,当子进程被调度执行时候,它的第一条指令便是 0x8048422处的 mov %eax,0x1c(%esp),而此时子进程的所处的环境下eax已经被设置为0了--这正是fork()后子进程为什么返回0的原因。
---------------------附上copy_process()的代码,关于完整的fork()调用代码这里不给出了---------------
int 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 ei p ,long cs,l ong eflags,long esp,long ss)
{
struct task_struct *p;
int i;
struct file *f;
p = (struct task_struct *) get_free_page();
if (!p)
return -EAGAIN;
task[nr] = p;
*p = *current; /* NOTE! this doesn't copy the supervisor stack */
p->state = TASK_UNINTERRUPTIBLE;
p->pid = last_pid;
p->father = current->pid;
p->counter = p->priority;
p->signal = 0;
p->alarm = 0;
p->leader = 0; /* process leadership doesn't inherit */
p->utime = p->stime = 0;
p->cutime = p->cstime = 0;
p->start_time = jiffies;
p->tss.back_link = 0;
p->tss.esp0 = PAGE_SIZE + (long) p;
p->tss.ss0 = 0x10;
p->tss.eip = eip;
p->tss.eflags = eflags;
p->tss.eax = 0; //子进程中fork()的返回值是0
p->tss.ecx = ecx;
p->tss.edx = edx;
p->tss.ebx = ebx;
p->tss.esp = esp;
p->tss.ebp = ebp;
p->tss.esi = esi;
p->tss.edi = edi;
p->tss.es = es & 0xffff;
p->tss.cs = cs & 0xffff;
p->tss.ss = ss & 0xffff;
p->tss.ds = ds & 0xffff;
p->tss.fs = fs & 0xffff;
p->tss.gs = gs & 0xffff;
p->tss.ldt = _LDT(nr);
p->tss.trace_bitmap = 0x80000000;
if (last_task_used_math == current)
__asm__("clts ; fnsave %0"::"m" (p->tss.i387));
if (copy_mem(nr,p)) {
task[nr] = NULL;
free_page((long) p);
return -EAGAIN;
}
for (i=0; i<NR_OPEN;i++)
if (f=p->filp[i])
f->f_count++;
if (current->pwd)
current->pwd->i_count++;
if (current->root)
current->root->i_count++;
if (current->executable)
current->executable->i_count++;
set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));
set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));
p->state = TASK_RUNNING; /* do this last, just in case */
return last_pid; //父进程中fork()的返回值是子进程id
}
更多推荐
所有评论(0)