原创
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条评论)
登录后参与讨论