本文共 6303 字,大约阅读时间需要 21 分钟。
//上层//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; ifilp[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不一样,所以返回值不一样.
//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/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); ...}