ChinaUnix首页 > 精华文章 > C/C++ > 正文

[精彩] fork的一个例子,好像人家是讲得很详细了,我还是不明白


http://www.chinaunix.net 作者:ccf  发表于:2009-05-26 17:48:54
发表评论】 【查看原文】 【C/C++讨论区】【关闭

#include <unistd.h>;

#include <sys/types.h>;

main ()
{
        pid_t pid;
        pid=fork();

        if (pid < 0)
                printf("error in fork!");
        else if (pid == 0)
                printf("i am the child process, my process id is %d\n",getpid());
        else
                printf("i am the parent process, my process id is %d\n",getpid());
}


结果是
[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是多少,都应该只有一行才对



 naiza 回复于:2004-04-23 11:38:11

这里的if和else不是以前理解的选择分支。fork后产生的子进程和父进程并行运行的


 sashow 回复于:2004-04-23 13:41:16

引用:原帖由 "naiza"]这里的if和else不是以前理解的选择分支。fork后产生的子进程和父进程并行运行的
 发表:



这种理解是不正确的。if 和 else 还是选择分支。

主要的原因是,[color=red]fork() 函数调用一次,返回两次。[/color]两次返回的区别是:子进程的返回值是0,父进程返回值为新子进程的进程ID。


 ccf 回复于:2004-04-23 14:38:59

但是只有一个pid=fork(); 呀,fork()返回的第二次值在什么时候赋给pid呢


 victory7 回复于:2004-04-27 18:06:29

这是由系统来控制fork地返回的


 davidtian 回复于:2004-04-27 18:11:20

fork后,父子进程共用程序段


 birdielu 回复于:2004-04-27 18:56:54

恩, 分析的很精辟


 lenovo 回复于:2004-04-27 20:09:20

引用:原帖由 "ccf"]但是只有一个pid=fork(); 呀,fork()返回的第二次值在什么时候赋给pid呢
 发表:


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


 chg.s 回复于:2004-04-27 21:09:30

要搞清楚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...

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

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


 UNIX大瓜 回复于:2004-04-27 23:14:44

精辟


 Zalophus 回复于:2004-04-28 03:30:44

问题是,结果显示是子进程先打印出自己的pid的,是不是子进程先于父进程执行了?


 chg.s 回复于:2004-04-28 09:16:31

到底哪个进程执行在先,这个和操作系统的调度算法等等很多因素相关。我觉得理解上的困难,关键在于为什么会有两个输出,而不是谁先谁后。


 zhaojinbo 回复于:2004-04-28 12:35:50

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


 luoting 回复于:2004-04-28 15:45:08

清晰!感谢!


 ccf 回复于:2004-04-28 17:06:59

明白了,thanks a lot


 carol1980 回复于:2004-04-28 17:15:21

:D 获益匪浅


 xhl 回复于:2004-04-28 17:21:44

我在父进程里定义的变量,子进程在创建的时候回自动创建一个副本,那如果我在子进程里,就是pid==0里创建的变量,父进程是不是看不到呢,

就是两个进程不共享数据段的情况下,父进程在创建子进程的之前的变量自进程都能继承,但要是父进程在fork后创建的变量,子进程能继承吗???


 lenovo 回复于:2004-04-28 17:25:43

它们已经是两个独立的进程了,
子进程怎么继承?


 xhl 回复于:2004-04-28 17:42:06

引用:原帖由 "lenovo" 发表:
它们已经是两个独立的进程了,
子进程怎么继承?




谢谢,就是说在fork前父进程的东西子进程可以继承,而在fork后子进程没有任何和父进程的继承关系了。在子进程里创建的东西是子进程的,在父进程创建的东西是父进程的。可以完全看成两个进程。


 lenovo 回复于:2004-04-28 19:46:49

对。


 sniper 回复于:2004-04-28 22:11:15

哦,偶明白了,在程序段里用了fork();之后程序出了分岔,派生出了两个进程。具体哪个先运行就看该系统的调度算法了。
在这里,我们可以这么认为,在运行到"pid=fork();"时系统派生出一个跟主程序一模一样的子进程。该进程的"pid=fork();"一句中pid得到的就是子进程本身的pid;子进程结束后,父进程的"pid=fork();"中pid得到的就是父进程本身的pid。因此改程序有两行输出。


 henngy 回复于:2004-04-29 10:13:32

fock关键要控制的是谁先返回!是子进程还是父进程,,用wait控制!


 xangyu 回复于:2004-04-29 11:58:03

真的很不错!谢谢!!


 adccpeng 回复于:2004-04-30 14:12:13

获益非浅!!
辛苦!


 playmud 回复于:2004-05-06 14:08:29

有这么麻烦吗?一个进程打印一句话。


 bierdaci 回复于:2004-05-06 21:14:51

引用:原帖由 "xhl" 发表:
我在父进程里定义的变量,子进程在创建的时候回自动创建一个副本,那如果我在子进程里,就是pid==0里创建的变量,父进程是不是看不到呢,

就是两个进程不共享数据段的情况下,父进程在创建子进程的之前的变量自进..........



你说的创建变量是什么意思?变量只能是静态的定义,fork是复制进程只要原进程里有的东西都会给复制出来(这里先不管共享不共享,对用户来说这是透明的你看不到哪里是复制的)。


 hbczjzc 回复于:2004-05-07 13:20:32

编写进程条时很有用的哦.


 corand 回复于:2004-05-09 13:03:25

fork后子进程跟父进程并行,公用程序段,若用vfork,则父进程被阻塞,等子进程执行完之后才会被执行


 flw 回复于:2004-05-09 14:09:16

引用:原帖由 "sniper" 发表:
哦,偶明白了,在程序段里用了fork();之后程序出了分岔,派生出了两个进程。具体哪个先运行就看该系统的调度算法了。
在这里,我们可以这么认为,在运行到"pid=fork();"时系统派生出一个跟主程序一模一样的子进程。..........


完全正确。


 sniper 回复于:2004-05-10 10:53:44

补充一下,fork()函数复制了当前进程的PCB,并向父进程返回了派生子进程的pid。而且根据上面“corand”兄的提示,父子进程并行,打印语句的先后完全看系统的调度算法。打印的内容控制则靠pid变量来控制。因为我们知道fork()向父进程返回了派生子进程的pid,是个正整数;而派生子进程的pid变量并没有被改变。这一区别使得我们看到了他们的不同输出。


 jjl3 回复于:2004-07-14 11:43:20

我做如下修改

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

main () 

        pid_t pid; 
        printf("fork!");    // printf("fork!\n");
        pid=fork(); 

        if (pid < 0) 
                printf("error in fork!"); 
        else if (pid == 0) 
                printf("i am the child process, my process id is %d\n",getpid()); 
        else 
                printf("i am the parent process, my process id is %d\n",getpid()); 


结果是 
[root@localhost c]# ./a.out 
fork!i am the child process, my process id is 4286 
fork!i am the parent process, my process id is 4285

但我改成printf("fork!\n");后,结果是
[root@localhost c]# ./a.out
fork! 
i am the child process, my process id is 4286 
i am the parent process, my process id is 4285

为什么只有一个fork!打印出来了?上一个为什么有2个?


 lenovo 回复于:2004-07-14 13:48:33

看这个:
http://bbs.chinaunix.net/forum/viewtopic.php?t=244249&highlight=蓝色键盘


 RedMarquis 回复于:2004-07-14 16:03:04

搭车问一个弱问题:

 if(fork()==0)
    system("cat test.c");
 
这样是不是会产生2个进程?分别是子进程和cat?


 lenovo 回复于:2004-07-14 16:32:16

man system


 RedMarquis 回复于:2004-07-14 17:10:48

man了下,感觉还是做为个进程,另发现他的说明里推荐用exec,说什么会产生参数影响,不甚明白,盼楼上的能继续解释一下,谢谢


 默难 回复于:2004-07-14 20:41:12

引用:原帖由 "ccf"]alhost 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返回值是两个,一个返回给父进程(子进程的ID)一个返回给子进程(0)


 wujiajia 回复于:2004-07-14 21:30:59

main()
{
int a;
int pid;
printf("AAAAAAAA");//print 一次;
pid=fork();//重这里开始分为两个
if(pid==0){//在这里定义的变量父进程是不会有的:int b;
printf("ok");}
else if(pid>;0){
printf("is ok\n");//if you want print b;error!but you can print a;
}
printf("BBBBBBB");//父子进程都会打印;
}

不知道我的回答是否正确!


 bashfulboy 回复于:2004-07-14 22:10:52

我也来一下:
wujiajia 的理解有些错误,
printf("AAAAAAAA");//print 一次;   这里会print 2次
如果你将 printf("AAAAAA") 换成 printf("AAAAAA\n")   那么就是只打印一次了.
主要的区别是因为有了一个 \n  回车符号
这就跟Printf的缓冲机制有关了,printf某些内容时,操作系统仅仅是把该内容放到了stdout的缓冲队列里了,并没有实际的写到屏幕上
但是,只要看到有 \n 则会立即刷新stdout,因此就马上能够打印了.
运行了printf("AAAAAA") 后, AAAAAA 仅仅被放到了缓冲里,再运行到fork时,缓冲里面的 AAAAAA 被子进程继承了
因此在子进程度stdout缓冲里面就也有了 AAAAAA.
所以,你最终看到的会是 AAAAAA 被printf了2次!!!!
而运行 printf("AAAAAA\n")后, AAAAAA 被立即打印到了屏幕上,之后fork到的子进程里的stdout缓冲里不会有 AAAAAA 内容
因此你看到的结果会是 AAAAAA 被printf了1次!!!!


 ohwww 回复于:2004-07-17 10:19:22

真是佩服各位,很受用


 eagerly1 回复于:2004-07-17 21:51:18

引用:原帖由 "ohwww"]真是佩服各位,很受用
 发表:


是呀


 jjl3 回复于:2004-07-19 15:19:30

:)


 mingjwan 回复于:2004-08-19 10:46:50

根据 chg.s的解释,请问,子进程在进行fork()操作的时候,是怎么知道自己就子进程,需要返回0,而不是继续产生一个子进程呢?


 bjldlee 回复于:2004-09-05 21:26:45

to bashfulboy:


main() 

int a; 
int pid; 
printf("AAAAAAAA");//print 两次; 
pid=fork();//重这里开始分为两个,但是子进程也会执行这个语句!!!这就产生了悖论!!! 
if(pid==0){//在这里定义的变量父进程是不会有的:int b; 
printf("ok");} 
else if(pid>;0){ 
printf("is ok\n");//if you want print b;error!but you can print a; 

printf("BBBBBBB");//父子进程都会打印; 
}


 temin 回复于:2004-10-15 09:20:29

收益,顶


 xhl0902 回复于:2004-10-15 09:51:24

讲的够清楚,够明了。佩服佩服


 zerglot 回复于:2004-10-15 15:57:59

了解了!haha


 cnufo 回复于:2005-01-10 00:56:01

3x,perfect!


 xujunxp 回复于:2005-01-10 19:57:31

顶,收获颇多


 superroy 回复于:2005-01-11 14:53:33

引用:原帖由 "mingjwan"]根据 chg.s的解释,请问,子进程在进行fork()操作的时候,是怎么知道自己就子进程,需要返回0,而不是继续产生一个子进程呢?
 发表:


fork()产生子进程后,父子进程都执行fork()之后的语句,即子进程不再执行fork()语句。


 郭子耳 回复于:2005-03-05 21:32:10

引用:原帖由 "chg.s"]操作系统创建一个新的进程(子进程),并且在进程表中相应为它建立一个新的表项。新进程和原有进程的可执行程序是同一个程序;上下文和数据,绝大部分就是原进程(父进程)的拷贝,但它们是两个相互独立的进程!
 发表:




 zlrll 回复于:2005-03-05 22:37:10

其实可以理解为fork就是生成了当前进程的一个副本,与原来进程不同的是原来进程中返回的是>;0,副本中返回==0,两个进程各执行一次if(),所以会打印2个了


 hmilyhacker 回复于:2005-03-06 07:49:16

谢谢各位大大,受益匪浅


 lss888 回复于:2005-03-06 20:53:53

[color=red]#include <unistd.h>;
#include <sys/types.h>;

main ()
{       int i=5;
        pid_t pid;
        pid=fork();
        for(;i>;0;i--){
        if (pid < 0)
                printf("error in fork!");
        else if (pid == 0)
                printf("i am the child process, my process id is %d and i=%d\n",getpid(),i);
        else
                printf("i am the parent process, my process id is %d and i=%d\n",getpid(),i);
          }
}[/color]
[color=blue]i am the child process, my process id is 11879 and i=5
i am the child process, my process id is 11879 and i=4
i am the child process, my process id is 11879 and i=3
i am the child process, my process id is 11879 and i=2
i am the child process, my process id is 11879 and i=1
i am the parent process, my process id is 11878 and i=5
i am the parent process, my process id is 11878 and i=4
i am the parent process, my process id is 11878 and i=3
i am the parent process, my process id is 11878 and i=2
i am the parent process, my process id is 11878 and i=1[/color]
我觉得这样改写一下就更好理解了


 THEBEST 回复于:2005-03-08 15:30:11

引用:原帖由 "sniper" 发表:
哦,偶明白了,在程序段里用了fork();之后程序出了分岔,派生出了两个进程。具体哪个先运行就看该系统的调度算法了。
在这里,我们可以这么认为,[color=red]在运行到"pid=fork();"时系统派生出一个跟主程序一模一样的子进程。该进程的"pid=fork();"一句中pid得到的就是子进程本身的pid;子进程结束后,父进程的"pid=fork();"中pid得到的就是父进程本身的pid。因此改程序有两行输出。
..[/color]

本来也觉得各位讲的挺好的.但你这里说的不太懂,子进程一定先结束?如果系统调度是先执行父进程那它执行完了不就退出了?然后子进程还要执行.

该进程的"pid=fork();"一句中pid得到的就是子进程本身的pid;子进程结束后,父进程的"pid=fork();"中pid得到的就是父进程本身的pid

这是什么意思?怎么还分子进程和父进程的pid = fork()呢?


 THEBEST 回复于:2005-03-08 15:38:51

引用:因为我们知道fork()向父进程返回了派生子进程的pid,是个正整数;而派生子进程的pid变量并没有被改变。这一区别使得我们看到了他们的不同输出。
派生子进程的pid变量并没有被改变是什么意思?对于子进程来讲pid不就是0吗?


 albcamus 回复于:2005-03-08 15:56:11

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

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


 zlrll 回复于:2005-03-08 20:37:17

引用:原帖由 "bashfulboy" 发表:
我也来一下:
wujiajia 的理解有些错误,
printf("AAAAAAAA");//print 一次;   这里会print 2次
如果你将 printf("AAAAAA") 换成 printf("AAAAAA\n")   那么就是只打印一次了.
主要的区别是因为有了一个 \n  回车..........



佩服,实在太强了!


 icesummit 回复于:2005-03-08 23:03:55

佩服佩服。码这么多字已经是很不容易,何况还说的这么清楚呢?


 lchhcllch 回复于:2005-03-09 09:35:51

都成FORK()专家了,贴出fork()实现代码就完结了.


 wolf_xz 回复于:2005-09-30 17:37:01

我的理解是产生的新进程和父进程同样的代码,2个进程分时运行(可以理解为同时运行)所以2个都打印出来。(if 只是区别子和父进程 运行哪个代码)


 daomeidan1234 回复于:2005-10-01 22:46:53

还是不明白,子进程的PID是4286,为什么是零呢?


 trueno 回复于:2005-10-02 20:43:49

简单的说,
 

父子进程是从pid=fork();
这条语句之后开始分开执行的。
1.父进程,由于成功创建了一个pid=4865的子进程,所以执行else
2.子进程,由于pid=0(初始值),所以执行then

注意:
子进程和父进程的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是两个不同的变量,占用的计算机资源是不同的。


 menp9999 回复于:2005-10-04 13:22:00

引用:原帖由 "mingjwan"]根据 chg.s的解释,请问,子进程在进行fork()操作的时候,是怎么知道自己就子进程,需要返回0,而不是继续产生一个子进程呢?
 发表:


紫京城不会再FORK的,除非你搞个循环,因为子京城是从FORK后面执行。


 menp9999 回复于:2005-10-04 13:23:16

引用:原帖由 "lchhcllch"]都成FORK()专家了,贴出fork()实现代码就完结了.
 发表:


看看LINUX啊,有的。


 galaxywar 回复于:2005-10-04 18:05:06

引用:原帖由 "RedMarquis"]man了下,感觉还是做为个进程,另发现他的说明里推荐用exec,说什么会产生参数影响,不甚明白,盼楼上的能继续解释一下,谢谢
 发表:


当进程调用一种e x e c函数时,该进程完全由新程序代换,而新程序则从其m a i n函数开始执行。
因为调用e x e c并不创建新进程,所以前后的进程I D并未改变。e x e c只是用另一个新程序替换了
当前进程的正文、数据、堆和栈段。

v f o r k用于创建一个新进程,而该新进程的目的是e x e c一个新程序
它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用e x e c (或e x i t ),于
是也就不会存访该地址空间。不过在子进程调用e x e c或e x i t之前,它在父进程的空间中运行
v f o r k保证子进程先运行,在它调用e x e c或e x i t之后父进
程才可能被调度运行。(如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会
导致死锁。)

摘至《UNIX环境高级编程》


 chinawang 回复于:2006-01-11 12:13:25

好,百看不厌


 sdemon915 回复于:2006-01-11 17:35:12

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


 etflanker 回复于:2006-01-11 21:31:23

mark,学习ing!!


 ChinaDream 回复于:2006-01-12 10:56:52

引用:原帖由 ccf 于 2004-4-23 11:23 发表
#include <unistd.h>;

#include <sys/types.h>;

main ()
{
        pid_t pid;
        pid=fork();

        if (pid < 0)
                printf("error in fork!") ... 








老大,父进程在fork完之后会继续执行!子进程会得到父进在fork之前的所有数据复制并从fork之后执行!那两句其实是两个进程print出来和!不是一个进程!


 jeromeecho 回复于:2006-03-31 14:23:56

这里的兄弟很让我感动.


 sickcat2004 回复于:2006-04-01 17:11:01

引用:原帖由 bashfulboy 于 2004-7-14 22:10 发表
我也来一下:
wujiajia 的理解有些错误,
printf("AAAAAAAA");//print 一次;   这里会print 2次
如果你将 printf("AAAAAA") 换成 printf("AAAAAA\n")   那么就是只打印一次了.
 ... 


豁然,开朗,啊哈哈哈!!1
~~~


 fjingxu 回复于:2006-05-16 16:00:28

学到好多东西,顶!


 sinoman 回复于:2006-05-16 17:58:42

引用:原帖由 bashfulboy 于 2004-7-14 22:10 发表
我也来一下:
wujiajia 的理解有些错误,
printf("AAAAAAAA");//print 一次;   这里会print 2次
如果你将 printf("AAAAAA") 换成 printf("AAAAAA\n")   那么就是只打印一次了.
 ... 


是遇到"\n"是会fflush(stdout)吗? 
printf("AAAAAAAA");
fflush(stdout);
应该没问题。


 ketos 回复于:2006-05-16 22:26:16

受教!!


 chenju2121 回复于:2006-05-17 15:29:53

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


 windblood 回复于:2006-05-17 19:15:40

分析的不错


 lichuxin9801 回复于:2006-06-26 11:23:18

嗯 说的不错 当初看的时候也是很迷惑的


 epegasus 回复于:2006-06-26 11:33:32

我不知道可不可以这样理解,请高手指点:
当 fork()调用后
以下代码段:
pid={fork()};

        if (pid < 0)
                printf("error in fork!");
        else if (pid == 0)
                printf("i am the child process, my process id is %d\n",getpid());
        else
                printf("i am the parent process, my process id is %d\n",getpid());
由被子进程和父进程都执行了,注意我把pid={fork()};中的fork()框起来了,因为只有“pid=”才是共同执行的部分。在fork()执行的时候PID被复制了一个。


 xueyan 回复于:2006-06-26 21:24:01

#include <stdlib.h>

int main(int argc,char** argv)
{
  int a;
  int pid;
  a=0;
  pid = fork();
  if(pid == 0)
    {
      a++;
    }else
      {
        a--;
      }
  return 0;
}
disassem main
Dump of assembler code for function main:
0x08048368 <main+0>:    push   %ebp
0x08048369 <main+1>:    mov    %esp,%ebp
0x0804836b <main+3>:    sub    $0x8,%esp
0x0804836e <main+6>:    and    $0xfffffff0,%esp
0x08048371 <main+9>:    mov    $0x0,%eax
0x08048376 <main+14>:   add    $0xf,%eax
0x08048379 <main+17>:   add    $0xf,%eax
0x0804837c <main+20>:   shr    $0x4,%eax
0x0804837f <main+23>:   shl    $0x4,%eax
0x08048382 <main+26>:   sub    %eax,%esp
0x08048384 <main+28>:   movl   $0x0,0xfffffffc(%ebp)
0x0804838b <main+35>:   [color=Red]call   0x80482a0[/color]  //fork();从下一条指令开始,父子分道扬镳。操作系统通过复制父进程的相关信息创建子进程,父子共用一个代码段,但各有各的数据段。
0x08048390 <main+40>:   mov    %eax,0xfffffff8(%ebp)      //将系统调用fork()的返回值存放到ebp,子进程的返回值是0,父进程为子进程的pid
0x08048393 <main+43>:   cmpl   $0x0,0xfffffff8(%ebp)//由于子进程的为零所以执行main+49
0x08048397 <main+47>:   jne    0x80483a0 <main+56>   //父进程为非零所以调到main+56
0x08048399 <main+49>:   lea    0xfffffffc(%ebp),%eax
0x0804839c <main+52>:   incl   (%eax)
0x0804839e <main+54>:   jmp    0x80483a5 <main+61>
0x080483a0 <main+56>:   lea    0xfffffffc(%ebp),%eax
0x080483a3 <main+59>:   decl   (%eax)
0x080483a5 <main+61>:   mov    $0x0,%eax
0x080483aa <main+66>:   leave
0x080483ab <main+67>:   ret
End of assembler dump.
(gdb)

[ 本帖最后由 xueyan 于 2006-6-26 21:29 编辑 ]


 okyzx 回复于:2006-07-27 14:56:48

好贴 学习


 gankai1983 回复于:2006-07-28 21:34:27

简单的说就是子进程获取了父进程的数据段,具体哪些楼上说的很清楚了


 yjfuk 回复于:2006-11-11 00:38:52

如果把
 pid=fork();
  if (pid < 0)
改成
  if (pid =fork() <  0)

为什么回出现都是in child
i am the child process, my process id is 4286 
i am the child process, my process id is 4285 

引用:原帖由 ccf 于 2004-4-23 11:23 发表
#include <unistd.h>;

#include <sys/types.h>;

main ()
{
        pid_t pid;
        pid=fork();

        if (pid < 0)
                printf("error in fork!") ... 




 langue 回复于:2006-11-11 06:53:31

因为父子进程的终端都是同一个,系统就把两行全打印给你看了


 040240216 回复于:2006-11-11 09:00:08

专业


 los 回复于:2006-11-16 18:40:17

引用:原帖由 yjfuk 于 2006-11-11 00:38 发表
如果把
 pid=fork();
  if (pid < 0)
改成
  if (pid =fork() <  0)

为什么回出现都是in child
i am the child process, my process id is 4286 
i am the child process, my process id is 4285  ... 




pid =fork() <  0这个表达式中< 优先级大于=,fork()<0为假,所以pid总是等于0,父子两个进程执行下来走的都是分支(pid == 0),可看见他们的PID值打印出来是不同的


 namei 回复于:2006-11-16 18:53:22

引用:if (pid =fork() <  0)


兄弟,再加层括号

if ((pid = fork()) <  0)


 ChinaDream 回复于:2006-11-17 00:52:08

引用:原帖由 ccf 于 2004-4-23 11:23 发表
#include <unistd.h>;

#include <sys/types.h>;

main ()
{
        pid_t pid;
        pid=fork();

        if (pid < 0)
                printf("error in fork!") ... 




fork英文意思是渔叉,也就是说程序到这里会分叉为 Y 样,变成两个一模一样内存INSTANCE!如果fork()返回值=0的话,表示是新分叉出来进程执行if(fork()==0){...........................}里的代码!而最开始调用fork函数的进程在函数返回时会得到子进程的pid,这时原始进程也要继运行。所以原始进程会运行if(ret >0){.................................}里的代码


 x.jc 回复于:2006-11-17 10:34:46

好贴,长知识了!


 cnbird 回复于:2006-11-19 11:03:57

绝辟


 meiyuhan 回复于:2006-11-19 11:53:39

8楼的太强


 huntrsky 回复于:2006-11-19 14:54:35

谁人把fork()函数的源码贴出来研究一下啊~~~不胜感激


 old-cow 回复于:2006-11-19 15:35:53

系统调用,看 kernel 。


 langue 回复于:2006-11-19 15:55:02

引用:原帖由 huntrsky 于 2006-11-19 14:54 发表
谁人把fork()函数的源码贴出来研究一下啊~~~不胜感激 



我把它贴出来,只怕你看不懂。


/*      $OpenBSD: kern_fork.c,v 1.44 2001/10/14 14:39:03 art Exp $      */
/*      $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $  */

/*
 * Copyright (c) 1982, 1986, 1989, 1991, 1993
 *      The Regents of the University of California.  All rights reserved.
 * (c) UNIX System Laboratories, Inc.
 * All or some portions of this file are derived from material licensed
 * to the University of California by American Telephone and Telegraph
 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
 * the permission of UNIX System Laboratories, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      @(#)kern_fork.c 8.6 (Berkeley) 4/8/94
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/map.h>
#include <sys/filedesc.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/vnode.h>
#include <sys/file.h>
#include <sys/acct.h>
#include <sys/ktrace.h>
#include <sys/sched.h>
#include <dev/rndvar.h>
#include <sys/pool.h>

#include <sys/syscallargs.h>

#include <vm/vm.h>
#include <uvm/uvm_extern.h>
#include <uvm/uvm_map.h>

int     nprocs = 1;             /* process 0 */
int     randompid;              /* when set to 1, pid's go random */
pid_t   lastpid;
struct  forkstat forkstat;


/*ARGSUSED*/
int
sys_fork(p, v, retval)
        struct proc *p;
        void *v;
        register_t *retval;
{
        return (fork1(p, SIGCHLD, FORK_FORK, NULL, 0, retval));
}

/*ARGSUSED*/
int
sys_vfork(p, v, retval)
        struct proc *p;
        void *v;
        register_t *retval;
{
        return (fork1(p, SIGCHLD, FORK_VFORK|FORK_PPWAIT, NULL, 0, retval));
}

int
sys_rfork(p, v, retval)
        struct proc *p;
        void *v;
        register_t *retval;
{
        struct sys_rfork_args /* {
                syscallarg(int) flags;
        } */ *uap = v;

        int rforkflags;
        int flags;

        flags = FORK_RFORK;
        rforkflags = SCARG(uap, flags);

        if ((rforkflags & RFPROC) == 0)
                return (EINVAL);

        switch(rforkflags & (RFFDG|RFCFDG)) {
        case (RFFDG|RFCFDG):
                return EINVAL;
        case RFCFDG:
                flags |= FORK_CLEANFILES;
                break;
        case RFFDG:
                break;
        default:
                flags |= FORK_SHAREFILES;
                break;
        }

        if (rforkflags & RFNOWAIT)
                flags |= FORK_NOZOMBIE;

        if (rforkflags & RFMEM)
                flags |= FORK_VMNOSTACK;

        return (fork1(p, SIGCHLD, flags, NULL, 0, retval));
}

int
fork1(p1, exitsig, flags, stack, stacksize, retval)
        register struct proc *p1;
        int exitsig;
        int flags;
        void *stack;
        size_t stacksize;
        register_t *retval;
{
        struct proc *p2;
        uid_t uid;
        struct proc *newproc;
        struct vmspace *vm;
        int count;
        static int pidchecked = 0;
        vaddr_t uaddr;
        int s;
        extern void endtsleep __P((void *));
        extern void realitexpire __P((void *));

#ifndef RFORK_FDSHARE
        /* XXX - Too dangerous right now. */
        if (flags & FORK_SHAREFILES) {
                return (EOPNOTSUPP);
        }
#endif

        /*
         * Although process entries are dynamically created, we still keep
         * a global limit on the maximum number we will create. We reserve
         * the last 5 processes to root. The variable nprocs is the current
         * number of processes, maxproc is the limit.
         */
        uid = p1->p_cred->p_ruid;
        if ((nprocs >= maxproc - 5 && uid != 0) || nprocs >= maxproc) {
                tablefull("proc");
                return (EAGAIN);
        }

        /*
         * Increment the count of procs running with this uid. Don't allow
         * a nonprivileged user to exceed their current limit.
         */
        count = chgproccnt(uid, 1);
        if (uid != 0 && count > p1->p_rlimit[RLIMIT_NPROC].rlim_cur) {
                (void)chgproccnt(uid, -1);
                return (EAGAIN);
        }

        /*
         * Allocate a pcb and kernel stack for the process
         */
        uaddr = uvm_km_valloc(kernel_map, USPACE);
        if (uaddr == 0)
                return ENOMEM;

        /* Allocate new proc. */
        newproc = pool_get(&proc_pool, PR_WAITOK);

        lastpid++;
        if (randompid)
                lastpid = PID_MAX;
retry:
        /*
         * If the process ID prototype has wrapped around,
         * restart somewhat above 0, as the low-numbered procs
         * tend to include daemons that don't exit.
         */
        if (lastpid >= PID_MAX) {
                lastpid = arc4random() % PID_MAX;
                pidchecked = 0;
        }
        if (lastpid >= pidchecked) {
                int doingzomb = 0;

                pidchecked = PID_MAX;
                /*
                 * Scan the active and zombie procs to check whether this pid
                 * is in use.  Remember the lowest pid that's greater
                 * than lastpid, so we can avoid checking for a while.
                 */
                p2 = LIST_FIRST(&allproc);
again:
                for (; p2 != 0; p2 = LIST_NEXT(p2, p_list)) {
                        while (p2->p_pid == lastpid ||
                            p2->p_pgrp->pg_id == lastpid) {
                                lastpid++;
                                if (lastpid >= pidchecked)
                                        goto retry;
                        }
                        if (p2->p_pid > lastpid && pidchecked > p2->p_pid)
                                pidchecked = p2->p_pid;
                        if (p2->p_pgrp->pg_id > lastpid &&
                            pidchecked > p2->p_pgrp->pg_id)
                                pidchecked = p2->p_pgrp->pg_id;
                }
                if (!doingzomb) {
                        doingzomb = 1;
                        p2 = LIST_FIRST(&zombproc);
                        goto again;
                }
        }

        nprocs++;
        p2 = newproc;
        p2->p_stat = SIDL;                      /* protect against others */
        p2->p_pid = lastpid;
        p2->p_exitsig = exitsig;
        LIST_INSERT_HEAD(&allproc, p2, p_list);
        p2->p_forw = p2->p_back = NULL;         /* shouldn't be necessary */
        LIST_INSERT_HEAD(PIDHASH(p2->p_pid), p2, p_hash);

        /*
         * Make a proc table entry for the new process.
         * Start by zeroing the section of proc that is zero-initialized,
         * then copy the section that is copied directly from the parent.
         */
        bzero(&p2->p_startzero,
            (unsigned) ((caddr_t)&p2->p_endzero - (caddr_t)&p2->p_startzero));
        bcopy(&p1->p_startcopy, &p2->p_startcopy,
            (unsigned) ((caddr_t)&p2->p_endcopy - (caddr_t)&p2->p_startcopy));

        /*
         * Initialize the timeouts.
         */
        timeout_set(&p2->p_sleep_to, endtsleep, p2);
        timeout_set(&p2->p_realit_to, realitexpire, p2);

        /*
         * Duplicate sub-structures as needed.
         * Increase reference counts on shared objects.
         * The p_stats and p_sigacts substructs are set in vm_fork.
         */
        p2->p_flag = P_INMEM;
        p2->p_emul = p1->p_emul;
        if (p1->p_flag & P_PROFIL)
                startprofclock(p2);
        p2->p_flag |= (p1->p_flag & (P_SUGID | P_SUGIDEXEC));
        MALLOC(p2->p_cred, struct pcred *, sizeof(struct pcred),
            M_SUBPROC, M_WAITOK);
        bcopy(p1->p_cred, p2->p_cred, sizeof(*p2->p_cred));
        p2->p_cred->p_refcnt = 1;
        crhold(p1->p_ucred);

        /* bump references to the text vnode (for procfs) */
        p2->p_textvp = p1->p_textvp;
        if (p2->p_textvp)
                VREF(p2->p_textvp);

        if (flags & FORK_CLEANFILES)
                p2->p_fd = fdinit(p1);
        else if (flags & FORK_SHAREFILES)
                p2->p_fd = fdshare(p1);
        else
                p2->p_fd = fdcopy(p1);

        /*
         * If p_limit is still copy-on-write, bump refcnt,
         * otherwise get a copy that won't be modified.
         * (If PL_SHAREMOD is clear, the structure is shared
         * copy-on-write.)
         */
        if (p1->p_limit->p_lflags & PL_SHAREMOD)
                p2->p_limit = limcopy(p1->p_limit);
        else {
                p2->p_limit = p1->p_limit;
                p2->p_limit->p_refcnt++;
        }

        if (p1->p_session->s_ttyvp != NULL && p1->p_flag & P_CONTROLT)
                p2->p_flag |= P_CONTROLT;
        if (flags & FORK_PPWAIT)
                p2->p_flag |= P_PPWAIT;
        LIST_INSERT_AFTER(p1, p2, p_pglist);
        p2->p_pptr = p1;
        if (flags & FORK_NOZOMBIE)
                p2->p_flag |= P_NOZOMBIE;
        LIST_INSERT_HEAD(&p1->p_children, p2, p_sibling);
        LIST_INIT(&p2->p_children);

#ifdef KTRACE
        /*
         * Copy traceflag and tracefile if enabled.
         * If not inherited, these were zeroed above.
         */
        if (p1->p_traceflag & KTRFAC_INHERIT) {
                p2->p_traceflag = p1->p_traceflag;
                if ((p2->p_tracep = p1->p_tracep) != NULL)
                        VREF(p2->p_tracep);
        }
#endif

        /*
         * set priority of child to be that of parent
         * XXX should move p_estcpu into the region of struct proc which gets
         * copied.
         */
        scheduler_fork_hook(p1, p2);

        /*
         * Create signal actions for the child process.
         */
        if (flags & FORK_SIGHAND)
                sigactsshare(p1, p2);
        else
                p2->p_sigacts = sigactsinit(p1);

        /*
         * This begins the section where we must prevent the parent
         * from being swapped.
         */
        PHOLD(p1);

        if (flags & FORK_VMNOSTACK) {
                /* share as much address space as possible */
                (void) uvm_map_inherit(&p1->p_vmspace->vm_map,
                    VM_MIN_ADDRESS, VM_MAXUSER_ADDRESS - MAXSSIZ,
                    VM_INHERIT_SHARE);
        }

        p2->p_addr = (struct user *)uaddr;

        /*
         * Finish creating the child process.  It will return through a
         * different path later.
         */
        uvm_fork(p1, p2, ((flags & FORK_SHAREVM) ? TRUE : FALSE), stack,
            stacksize);

        vm = p2->p_vmspace;

        if (flags & FORK_FORK) {
                forkstat.cntfork++;
                forkstat.sizfork += vm->vm_dsize + vm->vm_ssize;
        } else if (flags & FORK_VFORK) {
                forkstat.cntvfork++;
                forkstat.sizvfork += vm->vm_dsize + vm->vm_ssize;
        } else if (flags & FORK_RFORK) {
                forkstat.cntrfork++;
                forkstat.sizrfork += vm->vm_dsize + vm->vm_ssize;
        } else {
                forkstat.cntkthread++;
                forkstat.sizkthread += vm->vm_dsize + vm->vm_ssize;
        }

        /*
         * Make child runnable, set start time, and add to run queue.
         */
        s = splstatclock();
        p2->p_stats->p_start = time;
        p2->p_acflag = AFORK;
        p2->p_stat = SRUN;
        setrunqueue(p2);
        splx(s);

        /*
         * Now can be swapped.
         */
        PRELE(p1);

        uvmexp.forks++;
        if (flags & FORK_PPWAIT)
                uvmexp.forks_ppwait++;
        if (flags & FORK_SHAREVM)
                uvmexp.forks_sharevm++;

        /*
         * tell any interested parties about the new process
         */
        KNOTE(&p1->p_klist, NOTE_FORK | p2->p_pid);

        /*
         * Preserve synchronization semantics of vfork.  If waiting for
         * child to exec or exit, set P_PPWAIT on child, and sleep on our
         * proc (in case of exit).
         */
        if (flags & FORK_PPWAIT)
                while (p2->p_flag & P_PPWAIT)
                        tsleep(p1, PWAIT, "ppwait", 0);

        /*
         * Return child pid to parent process,
         * marking us as parent via retval[1].
         */
        retval[0] = p2->p_pid;
        retval[1] = 0;
        return (0);
}



 yszll 回复于:2006-11-27 09:00:49

看了. 好几遍. 的确精彩


 guduzhe 回复于:2006-12-16 11:31:25

oh!thank you!


 lf_alex 回复于:2006-12-21 10:38:05

引用:原帖由 bashfulboy 于 2004-7-14 22:10 发表
我也来一下:
wujiajia 的理解有些错误,
printf("AAAAAAAA");//print 一次;   这里会print 2次
如果你将 printf("AAAAAA") 换成 printf("AAAAAA\n")   那么就是只打印一次了.
 ... 


看完大伙儿的回复,我还是不是很清楚,printf()这条语句在子进程中难道不执行吗?这和标准缓存有关吗。如果子进程都是独立于父进程的,它理所当然应该执行printf()这条语句。
请回答!:(


 tyc611 回复于:2006-12-21 13:04:04

引用:原帖由 lf_alex 于 2006-12-21 10:38 发表

看完大伙儿的回复,我还是不是很清楚,printf()这条语句在子进程中难道不执行吗?这和标准缓存有关吗。如果子进程都是独立于父进程的,它理所当然应该执行printf()这条语句。
请回答!:( 


子进程执行点在fork()之后,所以之前的printf("fork")不会被子进程执行!
之所以有原贴的两次输出"fork"(而不是prinf()执行两次)是因为输出的字符串"fork"在输出缓冲区中没有刷新到屏幕,而子进程会继承父进程的所有数据(当然也包括缓冲区),所以在子进程中也有“fork”的输出。


 liugnhm 回复于:2007-01-04 15:58:32

好多牛人,受教了!


 lonyce 回复于:2007-01-14 22:11:42

我很少回帖的,今天破例一次,谢谢


 hawk2012 回复于:2007-01-15 15:55:48


int add_new_node(int *pid){
 
  if ((*pid = fork()) == -1)
    return(-2); 
  return(0);
}

 int main(int argc,  char *argv[]){
   int   i;             /* number of this process (starting with 1)   */
   int   childpid;      /* indicates process should spawn another     */ 
   int   nprocs = 7;        /* total number of processes in ring          */ 
   int   delay;
    for (i = 1; i < nprocs;  i++) {
     if(add_new_node(&childpid) < 0){
       perror("Could not add new node to ring");
       exit(EXIT_FAILURE); };
     if (childpid) break; };

   /* ring process code  */
   {
     char buff[BUFFSIZE];
     sprintf(buff,"I'm process %d with parent %d whose i = %i\n", 
     getpid(), getppid(), i);
      exit(EXIT_SUCCESS);
   } 
   /* end of process code */
}  /* end of main program here */



请大家分析一下上面的进程是怎么产生并执行的?一共执行了多少次?

[ 本帖最后由 hawk2012 于 2007-1-15 15:57 编辑 ]


 hall919 回复于:2007-06-21 15:44:42

有兄弟说在if以后的代码是父进程和子进程都会执行的,但是我试验的时候,却没有呢????
 头文件省略
int Print(char * out)
{
   if(NULL == out)
     return -1;
  while(1)
    {
       printf("out is %s and pid is %d\n",out , getpid());
       sleep(1);
    }

}
 int main(void)
{
  pid_t newpid;
  printf("Hello\n");
  newpid = fork();
  if(newpid==0)
   {
     printf("Son is run!\n");
     Print("son");
   }
  else if(newpid>0){
    printf("my son is %d\n",newpid);
  }else if(newpid <0){
     printf("fork fail!\n");
     exit(-1);
   }


  while(1)
 {
    printf("The thread is running!pid is %d\n",getpid());
    sleep(1);
   }

}

 执行的是这样的:
  Hello
   my son is 7123
  The thread is running!pid is 7122
  Son is run!
  out is son and pid is 7123
 The thread is running!pid is 7122
  out is son and pid is 7123
 The thread is running!pid is 7122
   ...................
 这里为什么总是父进程在执行“The thread is。。。”,而子进程不执行呢?


 doujiao520 回复于:2007-06-23 20:59:00

fork后的二个进程是同时向后执行的。


 ffangmm 回复于:2007-06-24 09:39:28

看晕了!


 大O儿 回复于:2007-06-25 15:36:46

确实是简单而经典的例子


 while(1) 回复于:2007-06-25 17:53:04

fork是叉子的意思:em11: 程序分成2股拉


 joinbaijun 回复于:2007-06-26 19:12:33

学到好多东西~


 marco_hxj 回复于:2007-08-18 19:44:10

看了此贴从清晰到很晕
借贴问个问题,vfork()之后,要等子进程用exec后才能运行父进程,那和顺序执行有什么区别?并发的优势怎么体现?

我是想接收到网络上的1个包以后开个子进程让它去把包发到串口,而父进程继续接受下一个包
因为串口发送慢,如果顺序执行的话,网络接受上可能丢包


PS:我的uclinux好象只能用vfork()

[ 本帖最后由 marco_hxj 于 2007-8-18 19:45 编辑 ]


 flw2 回复于:2007-08-18 20:41:25

那和顺序执行有什么区别?

你说的顺序是什么?

你的数据包的平均速度大于串口的速度,那么肯定是会失败了,

否则多线程就可以了,或者用个管道什么的就可以,用select返回能写就写,否则父进程保留收到的包。


 marco_hxj 回复于:2007-08-18 21:36:49

ls的,我现在是这样做的
while(1)
{
   recvfrom;
  串口发送;//这里比如需要0.1s,
}
在这0.1s内还有网络数据过来,就接收不到了
所以我想
vfork一个子进程去发送,父进程继续接收网络,但是父进程要等子进程完成才运行,那和我上面的做法好象没区别


父子进程如果能够同时进行的话,我开3个包的缓冲,这个速度应该可以匹配了

PS:希望我的问题不要污染了这帖子

[ 本帖最后由 marco_hxj 于 2007-8-18 21:48 编辑 ]


 flw2 回复于:2007-08-18 21:53:23

thread1:
    recvfrom and add this packet to buf (if buf is full, discard this packet);

thread2 
    if buf is not empty, send packet

而且recvfrom有缓冲,不会因为你0.1S不读就丢包,不过这个缓冲比起自己的缓冲来说很小


 tux_linux 回复于:2007-12-26 14:59:38

Administrator@syw-tju ~
$ cat fork_ex.c
#include <unistd.h>
#include <sys/types.h>
main()
{
int pid;
pid=fork();
if(pid<0) printf("fork error!\t");
else if(pid==0)
{printf("child process now! pid=%d ppid=%d\t",getpid(),getppid());}
else
{printf("parent process now! pid=%d child_pid=%d\t",getpid(),pid);}
}

Administrator@syw-tju ~
$ gcc fork_ex.c -o fork_ex

Administrator@syw-tju ~
$ ./fork_ex
child process now! pid=3968 ppid=3804parent process now! pid=3804 child_pid=3968

这个应该很明白了吧。

[ 本帖最后由 tux_linux 于 2007-12-26 15:01 编辑 ]


 暗底 回复于:2007-12-26 21:51:17

我很菜, 说的可能很墨迹,但是希望高人能仔细读完我的话,要不我会很伤心的!
兄弟们回了12页了,  辛苦了, 每条回复我都仔细看了, 真是从豁然开朗到一头雾水啊. 正是因为看明白了很多,所以才有了自己的问题, 
各位高人都是解释的为什么if 和else都打印了,原因很明确了因为产生了子进程, 却没有人 能清晰的解释 产生子进程之后,
父子进程又是怎么往下执行的,  所以又有兄弟 测试了fork()之前放置printf("fork");  为什么也打印两遍.
 至于"fork\n"后有回车只打印一遍,原因也很明确了,是因为父进程的缓冲区遇见回车清空了,子进程没继承着. 那么现在就说没有\n的情况,也就是子进程继承到了,父进程的printf("fork"); 那么现在子进程的缓冲区里应该有printf("fork"); 就刚才一兄弟发的这段代码来说
main () 

        pid_t pid; 
        printf("fork!";    // printf("fork!\n";
        pid=fork(); 
        if (pid < 0) 
                printf("error in fork!"; 
        else if (pid == 0) 
                printf("i am the child process, my process id is %d\n",getpid()); 
        else 
                printf("i am the parent process, my process id is %d\n",getpid()); 
}
父进程执行完printf("fork"); 之后, 执行pid = fork();  产生了一个子进程, 根据操作系统的进程调度, 此时执行子进程, 那么应该马上执行子进程的printf("fork");  而为什么,执行了i am the child process, my process id is 4286 之后又跳到上面执行printf("fork"); 如果缓冲区是堆栈的话,先进后出,可以解释通过,但是我印象里缓冲区应该是队列,要不程序都倒着打印了. 之前有兄弟说pid = fork()之后,父子都是从这之后开始执行,那么你怎么解释之前的printf("fork");因为这段程序的执行结果是
fork!i am the child process, my process id is 4286 
fork!i am the parent process, my process id is 4285
希望高手,能给个这段简单代码的执行流程, 感激不尽!


 暗底 回复于:2007-12-26 21:54:46

之前有人说 子进程也执行了这个程序,  那么为什么子进程不继续执行pid=fork();   继续产生子进程, 我真是有点迷惑了....   看完前几页  我自认为我理解的已经很透了,可是看完这12页,我彻底迷糊了!


 xi2008wang 回复于:2007-12-27 00:26:38

我也是个初学者,说说我的理解


      1 main ()
      1 #include <unistd.h>
      2 #include <sys/types.h>
      3 
      4 int
      5 main (void)  
      6 {
      7         pid_t pid;
      8 
      9         printf("fork!\n");   
//输出"fork!"这一行一定是最先输出的(这是一人在未做老爸之前,在屏幕上涂鸦的)
     10         pid=fork();           
//创建子进程(生了一个小孩了) ,并且将返回值赋给pid(时刻注意:家里多了一个人了,
//小孩的pid赋0,老爸的pid为子进程的ID)
     11         if (pid < 0)
     12                 printf("error in fork!\n");          
     13         else if (pid == 0)  
//这是一个判断语句,子进程,父进程都会执行这一句           
//pid为0的进程会执行下面的语句(知道是哪个进程的pid为0了吧? 
//不错,这是儿子在屏幕上写的.
//老爸的pid是子进程的ID,因为子进程ID不会为0,因此老爸pid不可能为0,自然老爸不会写这一行)
     14                 printf("i am the child process, my process id is %d\n",getpid());
     15         else   
//判断语句,pid不为0的进程会输出这一句(嗯,老爸的会在屏幕上写上这么一句,
//儿子的pid为0,因此儿子不会写这一行)
     16                 printf("i am the parent process, my process id is %d\n",getpid());
     17 }

验证1:
交互式运行(标准输出为行缓冲:遇到\n就冲洗其数据)
[root@mylinux ~]# ./a.out 
fork!   
//不错这时只有一个人,他最先输出这一行                                                  
i am the child process, my process id is 27991     
//fork后,子进程先输出了这一行,因为一般来说子进程的优先级高一点(老爸让着它呢)
i am the parent process, my process id is 27990        
//老爸接着写了这一行

验证2:
非交互式运行(标准输出为全缓冲:缓冲区满后冲洗其数据)
[root@mylinux ~]# ./a.out > 1.out
[root@mylinux ~]# cat 1.out 
fork!        
//同上,最先输出的,永远是这一行
i am the child process, my process id is 28002    
//依然是子进程先输出了这一行
fork!         
//这是哪多来的?原来这是老爸printf写东西时,残留在缓冲区中的东东,因为缓冲未满,数据没有被冲洗干净,
//fork后,儿子从老爸那获得的,现在一起被子进程写了出来.
//其实printf语句只执行了一句,但是因为缓冲有残留数据,因此才会一起被输出,这样就输出了两行

//然而为什么在交互式运行时却没有这样的东东,原来是换行符\n帮着冲洗数据,
//由于缓冲区被清空,fork后,老爸并没有遗留给儿子什么东东.儿子自然只输出了一行
i am the parent process, my process id is 28001  
//这是老爸最后写的



 xi2008wang 回复于:2007-12-27 00:28:01


[root@mylinux ~]# vi temp2.c 

      1 #include <unistd.h>
      2 #include <sys/types.h>
      3 
      4 int
      5 main (void)
      6 {
      7         pid_t pid;
      8 
      9         printf("fork!");     //这里没有了\n了
     10         pid=fork(); 
     11         if (pid < 0) 
     12                 printf("error in fork!\n"); 
     13         else if (pid == 0) 
     14                 printf("i am the child process, my process id is %d\n",getpid()); 
     15         else 
     16                 printf("i am the parent process, my process id is %d\n",getpid()); 
     17 }
验证3:
交互式运行
[root@mylinux ~]# ./a.out 
fork!i am the child process, my process id is 28696 
//最先输出的依然是"fork!"然后是子进程的输出
fork!i am the parent process, my process id is 28695
//父进程的printf是行缓冲,因为没有\n的冲洗,缓冲中有"fork"的残留,
//frok后子进程从父进程获得的"fork!"并输出
//再说一遍printf语句只执行了一次,但是因为缓冲有残留才会多输出这一部分
//最后才是父进程输出



验证4:
非交互式运行
[root@mylinux ~]# ./a.out > 2.c
[root@mylinux ~]# cat 2.c
fork!i am the child process, my process id is 28698
//最先输出的依然是"fork!"然后是子进程的输出
fork!i am the parent process, my process id is 28697
//父进程printf是全缓冲,它只有在缓冲满了时才会冲洗,因此父进程没有被冲洗"fork",
//子进程会从父进程获得的"fork!"并输出,最后才是父进程输出
[root@mylinux ~]# 

总结:
一般来说,因为子进程的优先级别高一点(但并不一定)
因此其执行顺序:未做父进程的进程(从main到fork),子进程(从fork到子进程结束),父进程(也是从fork到父进程结束)


 geba168 回复于:2007-12-28 14:07:35

fork返回2个结果


 joneson119 回复于:2007-12-29 11:55:45

有没有搞错啊。。一个fork就能搞多这么多的名堂。。。能吸引这么多人讨论。。。

爽。。牛。。。


 Aryang 回复于:2007-12-29 12:41:34

看来还得大牛出来讲讲printf


 spring_wind 回复于:2008-08-25 18:04:23

向各位强人学习了,基本明白fork的使用:)


 虑而后能得 回复于:2008-08-25 20:48:38

没发现真正讲的明白的


 shmild 回复于:2008-08-25 22:10:54

fork之后程序(或者说资源)被拷贝了一份,也就是他们是两个程序了,而fork成功就有两个返回值,0为子,非零正为父的ID,你自己的printf里说很明白吧


 fera 回复于:2008-08-28 15:31:12

参考:[url=http://blog.chinaunix.net/u/12783/showart_722257.html]http://blog.chinaunix.net/u/12783/showart_722257.html
PID fork()
{
    //...
    [color=Blue]将一个假系统级上下文层压到子进程的系统上下文栈,该上下文层包括能够让子进程识别自己的一些信息,并且当子进程获得处理器时从此处开始执行; // 就像进程被调度时保存的上下文。
  // 一旦被调度,将从假系统级上下文层开始执行,父子进程将共享下面的代码。
    if(IsParent())
    {
        procTable[aPid]->State = READY_TO_RUN; // 此时子进程才可能被调度。
        return aPid;
    }
    else
    {
        初始化u area的与分时相关的一些字段;
        return 0;
    }[/color]
}

或者参考一下上下文切换的思路:
void ContextSwitch()
{
    if (NeedSched() && ContextSwitchPermitted())
    {
        if (SaveContext())
        {
            // 在旧的进程上下文中
            Process newProc = FindEligibleProc();
            ResumeContext(newProc);
            // 永远执行不到这里!!!
        }
        // else
            // 从这里开始执行旧进程
    }
}
[color=Blue]该函数的奥妙就在于SaveContext()。它保存了进程的完整上下文,然后返回1。然而其中对一些信息作了特殊处理,它将0保存在register 0中,然后将其保存到旧进程的上下文中。然后,将register 1的值设置为1并返回该值。这样,当进程A调用ContextSwitch(),它将调用SaveContext(),该函数返回1,因此会查找新进程(假设为进程B)并继续执行,那么继续执行的将是B的上下文,因此ResumeContext()后永远执行不到。当scheduler最终又选择了A执行时,它会从SaveContext()保存的上下文开始执行,读取保存的register 0的值(0)存到register 0中并将其作为返回值返回。此时SaveContext()的返回值变成了0!于是就不会进入if块,从而达到注释掉的else部分继续执行A进程。[/color]


 liu1061 回复于:2008-09-29 13:35:13

想想现在讨论的力量真大! 1+1 > n !现在的学习环境相比以前真的好多了! 真让人感动呀!这里的CU兄弟!


 cpring 回复于:2008-10-22 09:26:10

顶啊
我看半天了还没有看懂。。
继续看。


 sy_xjf 回复于:2008-12-29 09:39:40

:mrgreen: 简单的地说,fork之后,就是两个程序了,如此而已,呼呼。


 风者归来 回复于:2009-02-02 17:40:07

我很晕,还是不明白。既然缓冲区没有冲掉,那就应该是先有fork!再有i am child...,为什么fork!在后面?难道缓冲区是堆栈?

不懂,请高手指教,不胜感激。:em14:


 samon_fu 回复于:2009-02-03 02:20:10

fork是个双返回的接口,这点非常特殊。

你就认为fork后,有两个进程同时往下走了,一个等于0,一个大于0。


 aripio 回复于:2009-02-05 14:43:21

引用:原帖由 chg.s 于 2004-4-27 21:09 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=2079854&ptid=311067]
要搞清楚fork的执行过程,就必须先讲清楚操作系统中的“进程(process)”概念。一个进程,主要包含三个元素:

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


非常精彩和精辟的叙述


 redor 回复于:2009-02-05 17:42:09

引用:原帖由 ccf 于 2004-4-23 11:23 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=2058661&ptid=311067]
#include ;
#include ;

main ()
{
        pid_t pid;
        pid=fork();

        if (pid < 0)
                printf("error in fork!");
        else if (pid == 0)
                print ... 




fork() 成功就应该是两个进程, 一个进程 pid == 0 表示子进程
else 那个就是父进程本身, 两个都要执行各自的代码。。。。


 zhonghuajia 回复于:2009-05-24 23:04:50

我觉的程序里面的pid并不是进程的id所以会有这样的结果。


 fera 回复于:2009-05-26 17:48:54

引用:原帖由 fera 于 2008-8-28 15:31 发表 [url=http://bbs3.chinaunix.net/redirect.php?goto=findpost&pid=9155564&ptid=311067]
PID fork()
{
    //...
    将一个假系统级上下文层压到子进程的系统上下文栈,该上下文层包括能够让子进程识别自己的一些信息,并且当子进程获得处理器时从此处开始执行; // 就像进程被调度时保存的上下文。
  // 一旦被调度,将从假系统级上下文层开始执行,父子进程将共享下面的代码。
    if(IsParent())
    {
        procTable[aPid]->State = READY_TO_RUN; // 此时子进程才可能被调度。
        return aPid;
    }
    else
    {
        初始化u area的与分时相关的一些字段;
        return 0;
    }
}


In Linux 0.99.15, sys_call.S, there's an instruction after a sys call:
movl %eax,EAX(%esp)             # save the return value
And the sys_fork() returns the pid of newly created process. When the new process is scheduled, stack is restored and the pid is returned via eax.

That's why fork() returns twice: 1 in parent CONTEXT, indicating whether fork() succeeded or not; 1 in child CONTEXT, with pid returned.
if (IsParent()) mentioned above may just check eax on the stack to see if it's 0 or non-zero.




原文链接:http://bbs.chinaunix.net/viewthread.php?tid=311067
转载请注明作者名及原文出处