fork()函数:
在Linux中fork函数是一个非常重要的函数,它从已存在的进程中创建一个新的进程,而新进程位子进程,原进程位父进程
函数原型:
#include<unistd.h>
pid_t fork(viod);
返回值:成功子进程中返回0,父进程返回子进程的id,出错返回-1
调用fork函数:
进程调用fork,当控制转移到内核中的fork代码后,内核会做以下几件事
- 分配下的内存块和内核数据结构给子进程(将进程加载到内存)
- 将父进程部分数据结构拷贝到子进程
- 将子进程加入到调度队列
- fork返回后,开始调度器调度(凭优先级调度)
当一个进程调用fork返回后,就有俩个二进制代码相同的进程。而且他们都运行到相同的地方。但每个进程可以干他们自己的事
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 pid_t pid;
8 printf("Before:pid is %d\n",getpid());
9 if((pid = fork())<0)
10 {
11 perror("fork");
12 exit(1);
13 }
14 printf("After:pid is %d,fork return %d\n",getpid(),pid);
15 sleep(1);
16 return 0;
17 }
运行结果如下:
我们看到运行结果有三行,一行before,但是调用了fork之后输出两行after。为什么会这样呢?
由此可见,fork之前只有父进程独立执行,fork之后,父子进程俩个执行流分别执行。
注意!fork之后,父子进程谁先执行完全由调度器决定!!
写时拷贝:
通常父子进程共享代码,父子进程在不写入时,数据也是共享的,当任意一方写入,便以写时拷贝的方式各自一份副本。如下图所示:
fork的常规用法:
- 一个进程希望复制自己,使得父子进程同时执行不同的代码段。(如:父进程等待客户端请求,生成子进程来处理请求)
- 一个进程要执行一个不同的程序,例如子进程从fork返回后,调用exec函数
fork调用失败的原因:
- 系统中有太多的进程
- 实际用户的进程数超过了限制(Linux事一款多用户的操作系统,每个人创建的进程是有上限的)
vfork()函数:
vfork和fork一样,也是用来创建子进程的
函数原型:
#include<unistd.h>
#include<sys/types.h>
pid_t vfork(void);
返回值:父进程返回子进程的id,子进程返回0
fork和vfork的区别:
- vfork共享地址空间,vfork创建一个子进程,父子进程的PCB指向同一块物理空间。而fork的子进程具有自己独立的地址空间
- vfork保证子进程先运行,在它调用exec(或exit)之后,父进程才能被调度执行。而fork的父子进程执行顺序由调度器决定
我们来看一段代码:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<sys/types.h>
5
6 int glob = 100;
7
8 int main()
9 {
10 pid_t pid = vfork();
11 if((pid<0))
12 {
13 perror("fork");
14 exit(1);
15 }
16 else if(pid==0)//child
17 {
18 glob = 200;
19 printf("child glob %d\n",glob);
20 exit(0);
21 }
22 else//father
23 {
24 printf("father glob %d\n",glob);
25 }
26 return 0;
27 }
运行结果如下:
我们之修改了子进程中的glob,然而父进程中的值页改变了,由此可见子进程和父进程在同一块可见内运行,而且保证子进程先运行