fork()与vfork():
我们在创建一个进程时主要是通过某个已存在的进程来调用fork()或者vfork()函数来实现的,那些已存在的进程是作为系统启动的一部分由内核来创建的。
1.fork函数介绍
#include<unistd.h>
pid_t fork(void);
功能:创建子进程
正确返回:父进程中返回子进程的pid,子进程返回0.(单调用双返回函数)
错误返回:-1
子进程是父进程的一个拷贝,fork()函数子进程拷贝父进程的数据段代码段,但是子进程不与父进程共享数据而是单独分配内存。fork()返回后,父子进程都会从fork函数的下一条语句开始执行,父子进程之间的运行时相互独立的,因此父子进程执行的顺序是不确定的,父进程可以先执行,子进程也可以先执行。
这里用到一个例子来说明:
# include <stdio.h>
# include <sys/types.h>
# include <stdlib.h>
# include <assert.h>
# include <string.h>
# include <unistd.h>
int main()
{
pid_t pid;
int count=0;
pid = fork();
assert( pid != -1);
if(pid<0)
{
printf("fork is error\n");
}
else if(pid == 0)
{
printf("\n");
count++;
printf("count=%d\n",count);
printf("I am child process,ID=%d\n",getpid());
}
else
{
count++;
printf("count=%d\n",count);
printf("I am father process,ID=%d\n",getpid());
}
return 0;
}
运行结果如下:
可以看出,count的值只是加了1,我们想着执行子进程时count的值应该为2。
但是通过分析可以得知,fork()函数执行之后,子进程拷贝父进程的数据段代码段,此时count++会被父子进程各执行一次,
子进程运行时打印自己数据段中的count+1,父进程与子进程互不影响,父进程执行自己数据段中的count+1,所以才会出现执行出来的结果。
2.vfork函数介绍
vfork创建进程的主要目的是用exec来执行另外的程序,exec或者exit之前,子进程与父进程之间是数据段共享的。vfork调用中,子进程先执行,父进程挂起,直到子进程调用exec或者exit后,父进程才开始执行。
有一个形象的例子可以描述父子进程的关系:
为什么会有vfork,因为以前的fork 很傻, 它创建一个子进程时,将会创建一个新的地址空间,并且拷贝父进程的资源,而往往在子进程中会执行exec 调用,这样,前面的拷贝工 作就是白费力气了,这种情况下,聪明的人就想出了vfork,它产生的子进程刚开始暂时与父进程共享地址空间(其实就是线程的概念了),因为这时候子进程在父进程的地址空间中 运行,所以子进程不能进行写操作,并且在儿子 霸占”着老子的房子时候,要委屈老子一 下了,让他在外面歇着(阻塞),一旦儿子执行了exec 或者exit 后,相当于儿子买了自己的 房子了,这时候就相 于分家了。
通过vfork 函数创建进程,这里同样引入一个计数器:
# include <stdio.h>
# include <sys/types.h>
# include <stdlib.h>
# include <assert.h>
# include <string.h>
# include <unistd.h>
int main()
{
pid_t pid;
int count=0;
pid = vfork();
assert( pid != -1);
if(pid<0)
{
printf("fork is error\n");
}
else if(pid == 0)
{
count++;
printf("count=%d\n",count);
printf("I am child process,ID=%d\n",getpid());
_exit(0);
}
else
{
count++;
printf("count=%d\n",count);
printf("I am father process,ID=%d\n",getpid());
}
return 0;
}
执行结果如下:
可以看到父进程中count的值为2,这个不难理解,在没有调用_exit(0)之前,父子进程是共享数据段的,此时程序执行时count的值为1。子进程调用了_exit(0)函数后,父进程开始执行,此时父进程数据段中count的值就为1,父进程执行count加1,因此父进程执行完后 count的值就成了2。
总结
区别:
1、fork()用于创建一个新进程。由fork()创建的子进程是父进程的副本。即子进程获取父进程数据空间、堆和栈的副本。父子进程之间不共享这些存储空间的部分。而vfork()创建的进程并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec (或exit)于是也就不会存放该地址空间。相反,在子进程调用exec或exit之前,它在父进程的空间进行。
2、vfork()与fork()另一个区别就是:vfork保证子进程先运行,在它调用exec或exit之后父进程才可能被调度运行。
相同:
1、两者被调用一次,但是返回两次。两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。