原创 fork vfork函数

2011-6-8 19:02 2544 10 10 分类: 工程师职场
//http://blog.csdn.net/benny_cen/archive/2009/03/06/3963556.aspx

1:
#include <unistd.h>
#include <stdio.h>

int main(void)
{
pid_t child;
  int count=0;
/* 创建子进程 */
child=fork();
  count++;
printf("count=%d\n",count);
  return 0;
}

运行结果:

[root@localhost heiying]# ./fork_pid
count=1
count=1
[root@localhost heiying]#


2:当我把fork改为vfork时运行结果为:
[root@localhost heiying]# ./vfork_pid
count=1
count=1
Segmentation fault
[root@localhost heiying]# 

请问为什么会出现段错误,哪位高手帮我解释下fork和vfork函数的用法和原理,谢谢。


fork完全复制父进程的地址和数据空间
vfork不完全复制,采用共享的方法,他们共享了数据段和代码段

VFORK建立一个新进程
而新进程建立的目的是调用EXEC
这样就是楼上兄弟说的不完全复制地址空间因为马上要调用EXEC
其实FORK函数的实现也并没有去完全复制而是用“写时调用”
另外建议楼主学习新函数先用MAN是这去读
这样练出来比较NB
因为MAN是最权威经典的


说说我的理解:
我们首先要弄懂fork和vfork一项重要区别(vfork调用do_fork时flags带有CLONE_VM位图,而fork没有),fork的子父进程是不共享人会空间的,这是不管子进程怎么样,都不会影响到父进程,这不是保护模式的优点之一吗?
而vfork则不一样,子父进程会共享所以的内存空间(代码段,数据段,堆栈段等),也就是说子进程对内存里面的人会修改,都会在父进程里面反应出来。天啦!他们竟然共享堆栈段,所以子进程不能破坏堆栈的,这也就是4楼说的原因,如果你的子进程从函数里面返回,这显然会破坏堆栈,所以,你父进程再返回时可能就跑飞了,从而在成了你后面的segmentation fault



vfork比起fork,一个很大不同是,vfork出来的子进程总是先于父进程执行,所以,程序的最后一句return 0;被子进程执行退出了,资源都销毁了,父进程再执行的时候就出现段错误


#include"stdio.h"
#include"errno.h"
#include"unistd.h"
#include"stdlib.h"
int main(void)
{
  pid_t pid;
  int count=0;
  if((pid=vfork())==-1)
  {
  printf("vfork error:%s\n",strerror(errno));
  exit(1);
  }
  count++;
  printf("count=%d\n",count);
  return(0);
}
执行结果为:
count=1
count=1
段错误

而把return(0)换成exit(0)后
就能顺利执行呢?此时结果为
count=1
count=2
期待各位大侠回答


return 函数返回。。结束一个函数
exit是抛出异常。。。结束整个进程


正如4楼所说,问题在于vfork.
如果没有使用vfork(), 在main函数中return (0)和exit(0)是没有区别的。

vfork是在子进程结束后父进程才继续运行,这个和fork是有区别的。另外,vfork并不完全复制父进程的地址空间。结合这些好好考虑下




linux系统调用fork()创建一个和当前进程完全相同的拷贝进程,其中父进程和子进程的代码段,堆栈段,数据段均独立

进程必须的4要点:

a.要有一段程序供该进程运行

b.进程专用的系统堆栈空间。

c.进程控制块,在linux中具体实现是task_struct

d.有独立的存储空间。

当一个进程缺少其中一个条件时候,我们称其为线程。

1.先看fork()的原型 :

#include<sys/types.h> /* 提供类型pid_t的定义 */
#include<unistd.h> /* 提供函数的定义 */
pid_t fork(void);
返回: 父进程中执行则返回子进程PID,子进程中执行返回0

现在进行一个例子,创建一个子进程,然后根据返回的 PID进行识别:

view plaincopy to clipboardprint?
/*test.c*/ 
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
 
main()  
{  
        int count = 0;  
    pid_t pid;      /*此时仅有一个进程*/      
    pid=fork(); /*此时已经有两个进程在同时运行*/    
    if(pid<0)    /*返回错误*/      
    printf("error in fork!");     
    else   
    {  
    if(pid==0)      /*代码在子进程中执行*/ 
        printf("I am the child process, my count is %d,my process ID is %d\n",count,getpid());  
    else        /*代码在父进程中执行*/ 
        printf("I am the parent process,my count is %d, my process ID is %d\n",++count,getpid());  
    }  

/*test.c*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

main()
{
        int count = 0;
pid_t pid; /*此时仅有一个进程*/
pid=fork(); /*此时已经有两个进程在同时运行*/
if(pid<0) /*返回错误*/
printf("error in fork!");
else
{
if(pid==0) /*代码在子进程中执行*/
printf("I am the child process, my count is %d,my process ID is %d\n",count,getpid());
else /*代码在父进程中执行*/
printf("I am the parent process,my count is %d, my process ID is %d\n",++count,getpid());
}
}

弄好后,在linux中键入:

$ gcc test.c -o test

$ ./test

在本次试验中

I am the parent process,my count is 1,my process ID is 3196

I am the child process, my count is 0,my process ID is 3776

从代码里面可以看出2者的pid不同,内存资源count是值得复制,父进程改变了count的值,而子进程中的count没有被改变。有人认为这样大批量的复制会导致执行效率过低。其实在复制过程中,子进程复制了父进程的task_struct,系统堆栈空间和页面表,这意味着上面的程序,我们没有执行count++前,其实子进程和父进程的count指向的是同一块内存。而当子进程改变了父进程的变量时候,会通过copy_on_write的手段为所涉及的页面建立一个新的副本。所以当我们执行++count后,这时候子进程才新建了一个页面复制原来页面的内容,基本资源的复制是必须的,而且是高效的。整体看上去就像是父进程的独立存储空间也复制了一遍。

在fork中,父子进程是独立开来的 ,并没有影响

2.vfork函数

vfork创建出来的不是真正意义上的进程,而是一个线程,因为它缺少了上面提到的进程的四要素的第4项,独立的内存资源,我们编一个程序练习:

view plaincopy to clipboardprint?
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
 
main()   
{  
        int count = 1;  
        int child;  
 
        printf("Before create son, the father's count is:%d\n", count);//打印没创建进程前  
        if(!(child = vfork())) //创建子进程  
    {  
                printf("This is son, his pid is: %d and the count is: %d\n", getpid(), ++count);  
                exit(1);  
        } else   
    {  
                printf("After son, This is father, his pid is: %d and the count is: %d, and the child is: %d\n",      
 
    getpid(), count, child);  
        }  

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

main()
{
        int count = 1;
        int child;

        printf("Before create son, the father's count is:%d\n", count);//打印没创建进程前
        if(!(child = vfork())) //创建子进程
{
                printf("This is son, his pid is: %d and the count is: %d\n", getpid(), ++count);
                exit(1);
        } else
{
                printf("After son, This is father, his pid is: %d and the count is: %d, and the child is: %d\n",

getpid(), count, child);
        }
}

然后编译,执行,得到下列结果:

Before create son, the father's count is:1

This is son, his pid is: 4048 and the count is: 2
After son, This is father, his pid is: 4048 and the count is: 2, and the child is: 2748

从这里我们看到,子进程和父进程是共享count的,也就是说,内存区是共享的

另外由vfork创造出来的子进程还会导致父进程挂起,除非子进程exit或者execve才会唤起父进程,看下面程序:

view plaincopy to clipboardprint?
#include <sys/types.h>  
#include <sys/stat.h>  
#include <unistd.h>  
 
main()   
{  
        int count = 1;  
        int child;  
 
        printf("Before create son, the father's count is:%d\n", count);  
        if(!(child = vfork()))  
     {//这里是子进程执行区  
                int i;  
                for(i = 0; i < 100; i++)   
        {  
                        printf("This is son, The i is: %d\n", i);  
                        if(i == 70)  
                                exit(1);  
                }  
                printf("This is son, his pid is: %d and the count is: %d\n", getpid(), ++count);  
                exit(1);//子进程退出  
        }   
    else 
     {//父进程  
                printf("After son, This is father, his pid is: %d and the count is: %d, and the child is: %d\n",      
 
    getpid(), count, child);  
        }  

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

main()
{
        int count = 1;
        int child;

        printf("Before create son, the father's count is:%d\n", count);
        if(!(child = vfork()))
{//这里是子进程执行区
                int i;
                for(i = 0; i < 100; i++)
{
                        printf("This is son, The i is: %d\n", i);
                        if(i == 70)
                                exit(1);
                }
                printf("This is son, his pid is: %d and the count is: %d\n", getpid(), ++count);
                exit(1);//子进程退出
        }
else
{//父进程
                printf("After son, This is father, his pid is: %d and the count is: %d, and the child is: %d\n",

getpid(), count, child);
        }
}

好,编译通过,执行。。。

Before create son, the father's count is:1

This is son, The i is: 0

...

...

This is son, The i is: 68
This is son, The i is: 69
This is son, The i is: 70
After son, This is father, his pid is: 2564 and the count is: 1, and the child is: 2736

可以看到父进程总是等子进程执行完毕后才开始继续执行


文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
10
关闭 站长推荐上一条 /5 下一条