fork()函数的理解:
含义: os.fork()函数会从调用此函数的地方创建出子进程,子进程会与主进程(即父进程)一起在CPU内执行。
注意:os.fork()函数只在linux环境下才有用,如果是windows环境下,会报错 no os.fork()。
1、每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
返回值为0,代表当前进程是子进程;
返回值为非负数(>0),代表当前进程是父进程
调用失败,返回-1
直接返回getpid()的值,则表示当前进程PID
2、使用fork函数创建一个进程,实验举例
#include <sys/types.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
pid=fork(); //创建子进程
if(pid<0)
printf("error in fork");
else if(pid==0)
printf("I am the child process,ID is %d\n", getpid());
else
printf("I am the parent process,ID is %d\n", getpid());
}
运行结果:
I am the child process,ID is 2723
I am the parent process,ID is 2722
实验程序分析:
1)pid=fork();
先来看看子进程的表现:
操作系统调用fork()函数创建一个新的进程(子进程),并且在进程表中相应为它建立一个新的表项,
此时子进程得到CPU的调度,它的上下文被换入,占据 CPU,操作系统对fork的实现,使得子进程中fork调用返回0
所以在这个进程中pid=0,这个进程继续执行的过程中,if语句中 pid<0不满足,但是pid= =0是true。所以输出i am the child process…
父进程的表现:
操作系统对fork的实现,使这个调用在父进程中返回刚刚创建的子进程的pid(一个正整数),所以下面的if语句中pid<0,pid==0的两个分支都不会执行。所以输出i am the parent process…
2)对子进程来说,fork返回给它0,但它的pid绝对不会是0,之所以fork返回0给它,是因为它随时可以调用getpid()来获取自己的pid
3)fork之后父子进程除非采用了同步手段,否则不能确定谁先运行,也不能确定谁先结束.认为子进程结束后父进程才从fork返回的,这是不对的,fork不是这样的,vfork才这样。
4)父进程执行了所有的进程,而子进程只执行了fork()后面的程序,这是因为子进程继承了父进程的PC(程序计数器).
vfork与fork的区别
vfork与fork主要有三点区别:
.fork():子进程拷贝父进程的数据段,堆栈段
vfork():子进程与父进程共享数据段
.fork()父子进程的执行次序不确定vfork 保证子进程先运行,在调用 exec 或 exit 之前与父进程数据是共享的,在它调用 exec或 exit 之后父进程才可能被调度运行。
.vfork()保证子进程先运行,在它调用 exec 或 exit 之后父进程才可能被调度运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
(1)先用fork()进行试验
#include <unistd.h>
#include <stdio.h>
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 <unistd.h>
#include <stdio.h>
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 <unistd.h>
#include <stdio.h>
#include <sys/types.h>
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.