免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4541 | 回复: 6
打印 上一主题 下一主题

难懂的fork(),盼解惑! [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-02-26 23:41 |只看该作者 |倒序浏览
1可用积分
不明白调用一次fork()是怎样实现返回两次的?

以下是我对fork()的理解:

比如有这么一个表达式u_pid=fork(),若此时CPU执行的是子进程;那么u_pid的值是0,若执行的是父进程,则u_pid的值为自进程的PID。
排出创建不成功的情况,fork()的返回值只能二者之一(难道同时返回子进程号和0?)而且在fork()调用完后已经确定。如果此时返回值为零,意味着CPU已经在执行子程序。那是不是说在fork()函数在其创建子进程之后,函数返回一个返回值之前,调度算法已经完成了一次从父进程到子进程的调度?
我猜想在fork()中会不会出现这样的情况:
fork()
{
   //此处为创建子进程的代码;
   //可能发生进程切换;
   if(...)//当前为父进程;
     return child_pid;
   else  //当前为子进程;
     return 0;
}

不知道疑惑说明白了没有,新手刚开始学,盼高手指点迷津啊!

[ 本帖最后由 teriusray 于 2007-2-26 23:49 编辑 ]

最佳答案

查看完整内容

>>如果此时返回值为零,意味着CPU已经在执行子程序。那是不是说在fork()函数在其创建子进程之后,函数返回一个返回值之前,调度算法已经完成了一次从父进程到子进程的调度?对于子进程,返回值为0,而和调度无关>>我猜想在fork()中会不会出现这样的情况:fork(){ //此处为创建子进程的代码; //可能发生进程切换; if(...)//当前为父进程; return child_pid; else //当前为子进程; return 0; }fork是系统调用, ...

论坛徽章:
0
2 [报告]
发表于 2007-02-26 23:41 |只看该作者
>>如果此时返回值为零,意味着CPU已经在执行子程序。那是不是说在fork()函数在其创建子进程之后,函数返回一个返回值之前,调度算法已经完成了一次从父进程到子进程的调度?
对于子进程,返回值为0,而和调度无关

>>我猜想在fork()中会不会出现这样的情况:
fork()
{
   //此处为创建子进程的代码;
   //可能发生进程切换;
   if(...)//当前为父进程;
     return child_pid;
   else  //当前为子进程;
     return 0;
}

fork是系统调用,不会发生进程切换
如果你看内核代码就知道秘密全在copy_thread函数中
fork为子进程创建一新的PCB(task_struct),该PCB中还有内核栈.fork通过对子进程的内核栈的一点改动来让子进程知道自己是子进程。
即childregs->eax = 0;//子进程的fork返回值为0
这样,子进程被调度时,eax寄存器从内核栈恢复,而eax就是fork的返回值,这样,子进程通过判断该值为0,代码的执行就出现分支。
if(fork())
{
父进程
}
else
{
子进程
}


//smpboot.c中的fork_by_hand,regs是栈上分配的,并没有初始化,不需要
int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
        unsigned long unused,
        struct task_struct * p, struct pt_regs * regs)
{
        struct pt_regs * childregs;
        //childregs指向子进程中的用户空间寄存器结构
        childregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) p)) - 1;
        struct_cpy(childregs, regs);//从父进程全部复制用户空间寄存器结构,regs对idle1...n无意义
        childregs->eax = 0;//子进程的fork返回值为0
        childregs->esp = esp;//子进程的用户空间堆栈指针为esp,如果调用do_fork时没有设,将等于父进程的

        p->thread.esp = (unsigned long) childregs;//子进程第一次被调度进来时的esp,idle1...idlen也象模象样地指向,其实永远也不会用到
        p->thread.esp0 = (unsigned long) (childregs+1);//进程进入系统0环的堆栈指针

        p->thread.eip = (unsigned long) ret_from_fork;//进程换入时的eip,从ret_from_fork返回

        savesegment(fs,p->thread.fs);
        savesegment(gs,p->thread.gs);

        unlazy_fpu(current);//保存当前进程的fpu现场
        struct_cpy(&p->thread.i387, &current->thread.i387);//现场复制到子进程中

        return 0;
}

论坛徽章:
0
3 [报告]
发表于 2007-02-27 08:48 |只看该作者
fork 的时候将复制父进程的那套文件描述符, 然后将这个描述符结构放入到运行队列中等待调度, 这个时候,子进程除了ID,其它的和父进程一样了

论坛徽章:
0
4 [报告]
发表于 2007-02-27 10:07 |只看该作者

fork实际上执行两次

虽然在代码上看着是执行一次,实际上执行两次,从此之后分成两个进程.靠返回值来确认到底是在哪个进程.

论坛徽章:
0
5 [报告]
发表于 2007-02-27 11:03 |只看该作者
父进程copy一份自己之后让他们继续执行,所以父子都在执行fork的结束部分。
两个进程会有一些数据用来区分自己,以便最后返回.

论坛徽章:
0
6 [报告]
发表于 2007-02-27 15:19 |只看该作者
原帖由 qtdszws 于 2007-2-26 23:41 发表于 2楼  
>>如果此时返回值为零,意味着CPU已经在执行子程序。那是不是说在fork()函数在其创建子进程之后,函数返回一个返回值之前,调度算法已经完成了一次从父进程到子进程的调度?
对于子进程,返回值为0,而和 ...

原来是这样。那是不是可以这样理解,对于子进程的堆栈段保存的fork()返回值为0,而对于父进程的来说堆栈段来说fork()返回值为自进程的PID?
这样的话说fork()对父子进程返回两个不同的值,是不是要比说fork()每次调用返回两次值更容易理解?
很多资料上都说返回两次,把我弄糊涂了,谢谢“异次元空间”的指点。

论坛徽章:
0
7 [报告]
发表于 2007-02-27 17:06 |只看该作者
完全正确!
其实许多使人困惑的问题,通过阅读内核代码就可以轻松解决!
内核源代码是解惑之本!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP