1.打印字符串“hello world!”
2.
在打印字符串“hello world!”前调用三次fork,
3.分析打印结果。
源代码:
#include <stdio.h>
#include <unistd.h>
#include<stdlib.h>
int main()
{
fork(); // 会产生两个进程分支
fork();// 前面产生的两个进程每个都会产生两个进程,有四个进程
fork();// 前面产生的4个进程都会产生两个进程,总共有八个进程
printf("hello world!\n");// 八个进程所以会打印八次
return 0;
}
所用函数:
1.fork函数
一个进程可以调用fork函数创建一个新进程
新进程被称为子进程
函数原型
pid_t fork(void);
返回值
fork函数调用一次,但是返回两次
在子进程中返回0,在父进程中返回子进程ID,出错返回-1
通过返回值,可以确定是在父进程还是子进程中
子进程和父进程继续执行fork调用之后的指令
子进程是父进程的副本
子进程获得父进程数据空间、堆和栈的副本
父子进程并不共享这些存储空间
父子进程共享正文段(只读的)
为了提高效率,fork后不并立即复制父进程空间,采用了COW(Copy-On-Write)
当父子进程任意之一,要修改数据段、堆、栈时,进行复制操作,但仅复制修改区域
fork()过程
调用alloc_task_struct( )函数为新进程分配8KB的task_struct空间和系统堆栈空间。
让当前指针指向父进程的task_struct ,并把父进程task_struct的内容拷贝到子进程的task_struct中。
检查新创建这个子进程后,当前用户所拥有的进程数目是否超出给他分配的资源的限制。
接下来,子进程的状态被设置为TASK_UNINTERRUPTIBLE以保证它不会马上投入运行。
调用get_pid()为新进程获取一个有效的PID。
更新不能从父进程继承的task_struct的其他所有域,例如,进程间亲属关系的域。
把新的task_struct插入进程链表,以确保进程之间的亲属关系。
把新的task_struct插入pidhash哈希表。
把子进程task_struct中的状态域设置成TASK_RUNNING,并调用wake_up_process( )把子进程插入到运行队列链表。
让父进程和子进程平分剩余的时间片。
返回子进程的PID,这个PID最终由用户态下的父进程读取
fork函数常见用法:
一个父进程希望复制自己,使父子进程同时执行不同的代码段
网络服务程序中,父进程等待客户端的服务请求,当请求达到时,父进程调用fork,使子进程处理该次请求,而父进程继续等待下一个服务请求到达
一个进程要执行一个不同的程序
子进程从fork返回后,立即调用exec执行另外一个程序