Linux内核分析-6/进程fork

来源:互联网 发布:淘宝开店要交押金吗? 编辑:程序博客网 时间:2024/06/07 00:38

进程fork

  • 前一段我们看了系统调用,那么fork不出意外,也是个系统调用,在x86下面也是int 80,那么既然这样子的话,我们分析一下fork下的sys_fork

linux0.11中的fork

//上层//init/main.cstatic inline _syscall0(int,fork)//inlcude/unistd.h#define _syscall0(type,name) \type name(void) \{ \long __res; \__asm__ volatile ("int $0x80" \    : "=a" (__res) \    : "0" (__NR_##name)); \if (__res >= 0) \    return (type) __res; \errno = -__res; \return -1; \}//综上得到了int fork(void) {     long __res;     __asm__ volatile             (            "int $0x80"             : "=a" (__res) //=只读的值 a 将输入变量放入eax            : "0" (__res_NR_fork)//引用了 %0 操作数,就是将调用号放入 %eax            );     if (__res >= 0)         return (int) __res;     errno = -__res;     return -1; }
//下层_system_call:    cmpl $nr_system_calls-1,%eax    ja bad_sys_call    push %ds    push %es    push %fs    pushl %edx    pushl %ecx      # push %ebx,%ecx,%edx as parameters    pushl %ebx      # to the system call    movl $0x10,%edx        # set up ds,es to kernel space    mov %dx,%ds    mov %dx,%es    movl $0x17,%edx        # fs points to local data space    mov %dx,%fs    call _sys_call_table(,%eax,4)//这里面会调用sys_fork,C中没有这个函数,该文件内有个函数_sys_fork    pushl %eax    movl _current,%eax    cmpl $0,state(%eax)        # state    jne reschedule    cmpl $0,counter(%eax)      # counter    je rescheduleret_from_sys_call:    movl _current,%eax      # task[0] cannot have signals    cmpl _task,%eax    je 3f    cmpw $0x0f,CS(%esp)        # was old code segment supervisor ?    jne 3f    cmpw $0x17,OLDSS(%esp)     # was stack segment = 0x17 ?    jne 3f    movl signal(%eax),%ebx    movl blocked(%eax),%ecx    notl %ecx    andl %ebx,%ecx    bsfl %ecx,%ecx    je 3f    btrl %ecx,%ebx    movl %ebx,signal(%eax)    incl %ecx    pushl %ecx    call _do_signal    popl %eax3:  popl %eax    popl %ebx    popl %ecx    popl %edx    pop %fs    pop %es    pop %ds    iret  //sys_fork;//// sys_fork()调用,用于创建子进程,是system_call 功能2。原形在include/linux/sys.h 中。;// 首先调用C 函数find_empty_process(),取得一个进程号pid。若返回负数则说明目前任务数组;// 已满。然后调用copy_process()复制进程。align 4_sys_fork:    call _find_empty_process ;// 调用 find_empty_process()(kernel/fork.c,135)。 //得到一个进程id    test eax,eax    js l2// SF .JS(Jump if sign)   结果为负则转移 格式:   JS   OPR 测试条件:SF=1    push gs    push esi    push edi    push ebp    push eax    call _copy_process ;// 调用C 函数 copy_process ()(kernel/fork.c,68)。    add esp,20 ;// 丢弃这里所有压栈内容。l2: ret//copy_process(kernel/fork.c)/* *  Ok, this is the main fork-routine. It copies the system process * information (task[nr]) and sets up the necessary registers. It * also copies the data segment in it's entirety. */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 eip,long cs,long eflags,long esp,long ss){    struct task_struct *p;    int i;    struct file *f;    //1/得到一个指针    p = (struct task_struct *) get_free_page();    if (!p)        return -EAGAIN;    task[nr] = p;    //2/复制全部内容    *p = *current;  /* NOTE! this doesn't copy the supervisor stack */    //3/修改当前结构体    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的一个返回值    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;    //4/修改其他    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));    //5/设置运行态    p->state = TASK_RUNNING;    /* do this last, just in case */    return last_pid;}
为什么fork一次,返回两次?//其实不是返回两次,而是造成了返回两次的假象在A进程fork的时候,创建了一个进程块B,并设置了B的ebp esp eip ,然后A进程返回 B进程块的id这是A做的事情B被创建之后,什么东西都被设置好了,下一步自然是被调度了.但是我们不知道,什么时候被调度到占用cpu,不过只要占用cpu了,那么返回一个为0 的值,看起来是A进程中的fork的返回值,看起来也是走的 A进程的 指令序列.但是B进程,1/被调度的第一句就是返回.并没有执行fork2/走的是A进程的指令序列,因为A进程返回时的eip和B进程返回时的eip一样3/和A进程的返回值不一样,是因为AB进程的eax不一样,所以返回值不一样.

下面的说的是glibc-2.25和linux-3.10中的调用路径


glibc路径

//http://www.cnblogs.com/openix/p/3274260.html//posix/fork.c//sysdeps/unix/sysv/linux/i386/arch-fork.h//glibc的最上层从fork()没找到通向ARCH_FORK的转换#define ARCH_FORK() \  INLINE_SYSCALL (clone, 5,                           \          CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD, 0,     \          NULL, NULL, &THREAD_SELF->tid)# define INLINE_SYSCALL(name, nr, args...) \  ({                                          \    unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args);         \    __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, ))             \    ? __syscall_error (-INTERNAL_SYSCALL_ERRNO (resultvar, ))             \    : (int) resultvar; })

kernel路径

//kernel/fork.c#ifdef __ARCH_WANT_SYS_FORK  SYSCALL_DEFINE0(fork)//这个最后被预处理成了sys_fork  {    #ifdef CONFIG_MMU        return do_fork(SIGCHLD, 0, 0, NULL, NULL);    #else        /* can not support in nommu mode */        return(-EINVAL);    #endif  }#endif//可见 sys_fork 调用了 do_fork /* *  Ok, this is the main fork-routine. * * It copies the process, and if successful kick-starts * it and waits for it to finish using the VM if required. */long do_fork(unsigned long clone_flags,          unsigned long stack_start,          unsigned long stack_size,          int __user *parent_tidptr,          int __user *child_tidptr){  ...  p = copy_process(clone_flags, stack_start, stack_size,child_tidptr, NULL, trace);  ...}
阅读全文
0 0
原创粉丝点击