关于fork()

  1. #include <unistd.h>;
  2. #include <sys/types.h>;

  3. main ()
  4. {
  5.         pid_t pid;
  6.         pid=fork();

  7.         if (pid < 0)
  8.                 printf("error in fork!");
  9.         else if (pid == 0)
  10.                 printf("i am the child process, my process id is %d\n",getpid());
  11.         else
  12.                 printf("i am the parent process, my process id is %d\n",getpid());
  13. }
[root@localhost c]# ./a.out
i am the child process, my process id is 4286
i am the parent process, my process id is 4285


我就想不到为什么两行都打印出来了,在我想来,不管pid是多少,都应该只有一行才对




要搞清楚fork的执行过程,就必须先讲清楚操作系统中的“进程(process)”概念。一个进程,主要包含三个元素:

o. 一个可以执行的程序;
o. 和该进程相关联的全部数据(包括变量,内存空间,缓冲区等等);
o. 程序的执行上下文(execution context)。

不妨简单理解为,一个进程表示的,就是一个可执行程序的一次执行过程中的一个状态。操作系统对进程的管理,典型的情况,是通过进程表完成的。进程表中的每一个表项,记录的是当前操作系统中一个进程的情况。对于单 CPU的情况而言,每一特定时刻只有一个进程占用 CPU,但是系统中可能同时存在多个活动的(等待执行或继续执行的)进程。

一个称为“程序计数器(program counter, pc)”的寄存器,指出当前占用 CPU的进程要执行的下一条指令的位置。

当分给某个进程的 CPU时间已经用完,操作系统将该进程相关的寄存器的值,保存到该进程在进程表中对应的表项里面;把将要接替这个进程占用 CPU的那个进程的上下文,从进程表中读出,并更新相应的寄存器(这个过程称为“上下文交换(process context switch)”,实际的上下文交换需要涉及到更多的数据,那和fork无关,不再多说,主要要记住程序寄存器pc指出程序当前已经执行到哪里,是进程上下文的重要内容,换出 CPU的进程要保存这个寄存器的值,换入CPU的进程,也要根据进程表中保存的本进程执行上下文信息,更新这个寄存器)。

好了,有这些概念打底,可以说fork了。当你的程序执行到下面的语句:
pid=fork();
操作系统创建一个新的进程(子进程),并且在进程表中相应为它建立一个新的表项。新进程和原有进程的可执行程序是同一个程序;上下文和数据,绝大部分就是原进程(父进程)的拷贝,但它们是两个相互独立的进程!此时程序寄存器pc,在父、子进程的上下文中都声称,这个进程目前执行到fork调用即将返回(此时子进程不占有CPU,子进程的pc不是真正保存在寄存器中,而是作为进程上下文保存在进程表中的对应表项内)。问题是怎么返回,在父子进程中就分道扬镳。

父进程继续执行,操作系统对fork的实现,使这个调用在父进程中返回刚刚创建的子进程的pid(一个正整数),所以下面的if语句中pid<0, pid==0的两个分支都不会执行。所以输出i am the parent process...

子进程在之后的某个时候得到调度,它的上下文被换入,占据 CPU,操作系统对fork的实现,使得子进程中fork调用返回0。所以在这个进程(注意这不是父进程了哦,虽然是同一个程序,但是这是同一个程序的另外一次执行,在操作系统中这次执行是由另外一个进程表示的,从执行的角度说和父进程相互独立)中pid=0。这个进程继续执行的过程中,if语句中pid<0不满足,但是pid==0是true。所以输出i am the child process...

我想你比较困惑的就是,为什么看上去程序中互斥的两个分支都被执行了。在一个程序的一次执行中,这当然是不可能的;但是你看到的两行输出是来自两个进程,这两个进程来自同一个程序的两次执行。

我的天,不知道说明白了没……





pid这个变量是有两个的,
父进程一个,
子进程一个。





fork之后,操作系统会复制一个与父进程完全相同的子进程,虽说是父子关系,但是在操作系统看来,他们更像兄弟关系,这2个进程共享代码空间,但是数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同,但只有一点不同,如果fork成功,子进程中fork的返回值是0,父进程中fork的返回值是子进程的进程号,如果fork不成功,父进程会返回错误。
可以这样想象,2个进程一直同时运行,而且步调一致,在fork之后,他们分别作不同的工作,也就是分岔了。这也是fork为什么叫fork的原因。
至于那一个最先运行,可能与操作系统有关,而且这个问题在实际应用中并不重要,如果需要父子进程协同,可以通过原语的办法解决。




>;>;派生子进程的pid变量并没有被改变是什么意思?对于子进程来讲pid不就是0吗?

1,派生子进程的进程,即父进程,其pid不变;
2,对子进程来说,fork返回给它0,但它的pid绝对不会是0;之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid;
3,楼上的楼上的你的观点是对的,fork之后夫子进程除非采用了同步手段,否则不能确定谁先运行,也不能确定谁先结束。认为子进程结束后父进程才从fork返回的,这是不对的,fork不是这样的,vfork才这样。



注意:
子进程和父进程的pid 是完全不同的两个变量(虽然名字相同),父子进程要分别占用不同的资源的。
比如:
int com=0;
main()
{
int pid;
pid=fork();

if(pid<0)
then printf("error";

if(pid=0)
    then
          {com=1};
    else
          {com=com+1};
printf{"com=%d\n",com};

}
最后的结果是什么呢?
应该是:
com=1
com=1
.也就是说在父子进程里,全局变量com是两个不同的变量,占用的计算机资源是不同的。





子进程的PID不是0
根据fork的实现,fork的返回值可能为>0 =0 <0,3种情况
如果是>0说明是父进程,返回值是子进程的PID;如果是=0说明是子进程,子进程可以通过getpid()得到自己的PID,通过getppid得到父进程的PID;<0就说明fork失败





fork()调用将会复制一个与当前进程几乎完全相同(除了fork的返回值不同)的新进程,这两个进程各有各的空间,各有各的局部变量,而且两个进程的局部变量的值在fork这个点处具有相等的值,只有fork返回的值不同,在该例子中,就是局部变量pid的值不同,新进程的pid变量为0,原来进程的pid为新进程的进程ID值。这样,两个并行的进程运行,才会出现你看到的结果。



操作系统通过复制父进程的相关信息创建子进程,父子共用一个代码段,但各有各的数据段。




说说我的理解:

1,子进程会继承父进程的所有资源,包括各种变量,缓冲区等等,这其中,变量的值除了那个pid之外,父和子都是一样的。

2,pid不是进程号,写这个pid出来是为了让用户控制两个进程,和进程自身没有半点关系(即pid这里起到一个标识作用),真正的进程号是需要通过系统调用getpid来返回的。

3,为何在父中的pid返回它儿子的进程号,这是有原因的,因为,父进程没法通过其他的系统调用返回它儿子的进程号,而儿子进程可以通过getppid返回它父亲的进程号。

4,fork后的两个进程的代码段是一样的(除非子进程exec另外的程序),重点是儿子进程从哪里开始执行这段代码,答案是从fork语句之后。(你想要是把代码从头到尾执行一篇,那不是陷入了无限死循环,儿子再参生儿子,再参生儿子。。)。 至于为何在没有加\n的情况下,儿子进程也会打印出“fork!”,这是因为:
前面说的,儿子继承了父亲的缓冲区总的数据,没有加\n的父进程,这时候的“fork"字段还在缓冲区中,这样就被儿子copy了,后来,儿子在它的输出缓冲区中放入了\n,
即"i am the child \n",这里的字符和前面的fork!一起存入了缓冲区,遇到\n后,就输出了 。
这造成一种错觉,好像是儿子也把fork语句之前的代码执行了,注意,没有。你可以验证,在儿子进程后来输出任何语句的情况下,它是不会输出前面的fork的(因为没有\n)


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值