一、问题引入
工作期间,某系统设计师抛出如下一个问题,下面的代码,输出几个“-”?:
/******************************************************************************
Copyright by Thomas Hu, All rights reserved!
Filename : fork01.c
Author : Thomas Hu
Date : 2012-8-5
Version : 1.0
Description : fork函数问题原型
******************************************************************************/
#include <unistd.h>
#include <stdio.h>
int main()
{
int i = 0;
for(i = 0; i < 2; i++)
{
fork();
printf("-");
}
return 0;
}
过了N久之后,仍然没有人回答这个问题(也许大家都忙,没空理他^_^)。
如果您回答是2, 那建议您还是先看看Linux中的fork函数使用说明;
如果您回答是6, 说明您对fork函数有一定的理解了,但还需要继续看本篇文档;
如果您回答是8,并且理解背后原理(不是执行程序得出的结论),那您就不需要看本文啦,请绕道行走^_^。
我大略分析了一下,然后输入代码编译执行,执行结果竟然为8个,觉得不可思议!(理论上是6个啊,对8个百思不得其解,后来查阅了资料,才发现自己还没搞懂 fork 背后的本质,因此撰此文,大家共同探讨。)
要搞清楚fork的执行过程,就必须先弄清楚操作系统中的“进程(process)”概念。一个进程,主要包含三个元素:
1. 一个可以执行的程序;
2. 和该进程相关联的全部数据(包括变量,内存空间,缓冲区等等);
3. 程序的执行上下文(execution context)。
不妨简单理解为,一个进程表示的,就是一个可执行程序的一次执行过程中的一个状态。操作系统对进程的管理,典型的情况,是通过进程表完成的。进程表中的每一个表项,记录的是当前操作系统中一个进程的情况。对于单 CPU的情况而言,每一特定时刻只有一个进程占用 CPU,但是系统中可能同时存在多个活动的(等待执行或继续执行的)进程。
一个称为“程序计数器(program counter, pc)”的寄存器,指出当前占用 CPU的进程要执行的下一条指令的位置。
当分给某个进程的 CPU时间已经用完,操作系统将该进程相关的寄存器的值,保存到该进程在进程表中对应的表项里面;把将要接替这个进程占用 CPU的那个进程的上下文,从进程表中读出,并更新相应的寄存器(这个过程称为“上下文交换(process context switch)”,实际的上下文交换需要涉及到更多的数据,那和fork无关,不再多说,主要要记住程序寄存器pc记录了程序当前已经执行到哪里,是进程上下文的重要内容,换出 CPU的进程要保存这个寄存器的值,换入CPU的进程,也要根据进程表中保存的本进程执行上下文信息,更新这个寄存器)。
二、fork函数详解
#include<unistd.h>
#include<sys/types.h>
函数定义:
pid_t fork( void);
(pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中)