2011.06
我们先说说函数原型:
头文件:
#include
函数定义:
int fork( void );
返回值:
子进程中返回0,父进程中返回子进程ID,出错返回-1
函数说明:一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间,它们之间共享的存储空间只有代码段。
示例代码:
#include
#include
int main(int argc, char ** argv )
{
int pid = fork();
if(pid == -1 )
{
// print("error!");
} else if( pid = =0 ) {
// print("This is the child process!");
} else
{
// print("This is the parent process! child process , pid);
}
return 0;
}
Fork()系统在Linux中的返回值是没有NULL的.
Error Codes
出错返回错误信息如下:EAGAIN
达到进程数上限.ENOMEM没有足够空间给一个新进程分配。
呵呵呵,要理解了,这些还远远不够的,你知道在Linux系统下,fork()是关键,下面多说一些:
第一点就是:
操作系统对进程的管理,是通过进程表完成的.进程表中的每一个表项,记录的是当前操作系统中一个进程的信息.进程在系统的唯一标识是PID,PID是一个从1到32768的正整数,其中1一般是特殊进程init,其它进程从2开始依次编号.当用完32768后,从2重新开始.
.一个称为“程序计数器(program counter, pc)”的寄存器,指出当前占用 CPU的进程要执行的下一条指令的位置.当分给某个进程的 CPU时间已经用完,操作系统将该进程相关的寄存器的值,保存到该进程在进程表中对应的表项里面,把将要接替这个进程占用 CPU的那个进程的上下文,从进程表中读出,并更新相应的寄存器.
第二点就是:vfork()
vfork与fork主要有三点区别:
.fork():子进程拷贝父进程的数据段,堆栈段;vfork():子进程与父进程共享数据段.fork()父子进程的执行次序不确定vfork 保证子进程先运行,在调用 exec 或 exit 之前与父进程数据是共享的,在它调用 exec或 exit 之后父进程才可能被调度运行。
.vfork()保证子进程先运行,在它调用 exec 或 exit 之后父进程才可能被调度运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
1)先用fork()进行试验
#include
#include
int main(void)
{
pid_t pid;
int count=0;
pid=fork();
count++;
printf("count= %d\n",count);
return 0;
}
分析:
通过上面fork()的说明,这个程序的输出应该是:
./test
count= 1
count= 1
2)而将fork()换成vfork()呢,程序如下
#include
#include
int main(void)
{
pid_t pid;
int count=0;
pid=vfork();
count++;
printf("count= %d\n",count);
return 0;
}
执行结果:
./test
count= 1
count= 1
Segmentation fault (core dumped)
分析:
通过将fork()换成vfork(),由于vfork()是共享数据段,为什么结果不是2呢,答案是:
vfork保证子进程先运行,在它调用 exec 或 exit 之后父进程才可能被调度运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁.
3)做最后的修改,在子进程执行时,调用_exit(),程序如下:
#include
#include
#include
int main(void)
{
pid_t pid;
int count=0;
pid=vfork();
if(pid==0)
{
count++;
_exit(0);
}
else
{
count++;
}
printf("count= %d\n",count);
return 0;
}
执行结果:
./test
count= 2
分析:如果子进程中如果没有调用_exit(0),则父进程不可能被执行,在子进程调用exec(),exit()之后父进程才可能被调用.
所以加上_exit(0),使子进程退出,父进程执行.
这样 else 后的语句就会被父进程执行,又因在子进程调用 exec 或 exit 之前与父进程数据是共享的,
所以子进程退出后把父进程的数据段 count 改成1了,子进程退出后,父进程又执行,最终就将count 变成了 2.
简要的概括的说是:
1)fork()系统调用是创建一个新进程的首选方式,fork的返回值要么是0,要么是非0,父进程与子进程的根本区别在于fork函数的返回值.
2)vfork()系统调用除了能保证用户空间内存不会被复制之外,它与fork几乎是完全相同的.vfork存在的问题是它要求子进程立即调用exec,
而不用修改任何内存,这在真正实现的时候要困难的多,尤其是考虑到exec调用有可能失败.
3)vfork()的出现是为了解决当初fork()浪费用户空间内存的问题,因为在fork()后,很有可能去执行exec(),vfork()的思想就是取消这种复制.
4)现在的所有unix变量都使用一种写拷贝的技术(copy on write),它使得一个普通的fork调用非常类似于vfork.因此vfork变得没有必要.