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
}

Logo

更多推荐