fork()性质
-
一次调用,两次返回。返回值是0则是子进程,>0为父进程
- 父、子进程是两个独立的进程,虚拟地址是相同但独立的
- 并发执行
- 共享文件
fork()之后内核会做:
- 给子进程分配新的内存块
- 将父进程部分内容拷贝给子进程,包括指令、数据、缓冲区、堆栈等
- 添加子进程到系统进程列表
- fork()返回,开始调度器调度
实际上,并没有真的全都“复制”一份,父进程只复制了PCB块(进程控制块),其他的代码段、数据段、用户堆栈内存空间并没有复制一份,而是与子进程共享。只有当子进程在运行中出现写操作时,才会产生中断,并为子进程分配内存空间。由于父进程的PCB和子进程的一样,所以在PCB中断中所记录的父进程占有的资源,也是与子进程共享使用的。
使用方法
pid_t pid=fork();
例题详解
例一
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
void fork2()
{
printf("L0\n");
fork();
printf("L1\n");
fork();
printf("Bye\n");
}
int main()
{
fork2();
return 0;
}
进程图:
输出:
补充:使用fork()函数时,提示符为什么会在子进程结束之前运行?
因为父进程是在当前shell命令执行的,父进程结束后就返回了shell。
小总结:
循环n遍,进程数(包括主进程)为2^n个
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(int argc, char* argv[])
{
int i;
int n=argv[1][0]-'0';
for(i=0;i<n;i++)
fork();
printf("hello\n");
return 0;
}
结果图:
例二
void fork4()
{
printf("L0\n");
if (fork() != 0)
{
printf("L1\n");
if (fork() != 0)
printf("L2\n");
}
printf("Bye\n");
}
进程图:
结果:
例三
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int i;
for(i=0;i<2;++i)
{
fork();
printf("-");
}
return 0;
}
进程图:
结果:
多输出-原因:
printf的缓冲机制:
printf某些内容时,操作系统仅仅是把该内容放到了stdout的缓冲队列里了,并没有实际的写到屏幕上。但是,只要看到有\n或EOF 则会立即刷新stdout,因此就马上能够打印了。
再来看一下有\n情况:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int i;
for(i=0;i<2;++i)
{
fork();
printf("-\n");
}
return 0;
}
进程图:
结果:
可以看出正常输出了6个-,因为每次都及时输出刷新缓存了。
例四
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
fork()&&fork()||fork();
printf("hello\n");
return 0;
}
对于A&&B||C,只要清楚A&&B为真时,不会再执行C了;A为假时不会再执行B了,因为表达式的真假已经唯一确定了。
进程图:
结果图: