1:fork()
在linux中可以使用fork()来创建一个进程,来看下函数的定义以及返回值
函数原型 pid_t fork(void)
函数返回值:
0: 子进程
-1: 出错
>0(为子进程ID): 父进程
头文件:include<sys/types.h>
inculde<unistd.h>
fork()函数用于从已存在的进程中创建一个新的进程。新进程为子进程,而原进程称为父进程。使用fork()函数得到的子进程时父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程上下文,代码段,进程堆栈,内存信息,文件描述符,信号控制设定,进程优先级,进程组号,当前工作目录,根目录,资源限制和控制终端等,而子进程所独有的只有它的进程号,资源使用和计时器等子进程几乎是父进程的完全复制,所以父子进程会同时运行一个程序。
下面来看一个例子:
#inculde <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t result;
/*create new process */
result = fork();
if (-1 == result)
{
printf("Fork error\n");
} else if (result == 0) /* children process */
{
printf("Child process id is %d\n", getpid());
} else
{
printf("Father process id is %d \n", getpid());
}
return result;
运行结果:
Father process id is 6798
Child process is is 6799
fork出错可能有两种原因:
1:当前进程数目达到了系统规定的上限,这是error的值被设置为EAGAIN
2:系统内存不足,这时error的值被设置为ENOMEM.
可以看到fork()函数看起来执行一次却有两个返回值。这时因为fork()执行时,父进程会复制出一个子进程,而父子进程的代码从fork()函数的返回开始分别在各自的地址空间同时运行,从而是两个进程分别获得其所属fork的返回值,其中在父进程中返回的是子进程的进程号,而在进程中返回0.因此,可以分别通过返回值来判定该进程时父进程还是子进程。
2:写时拷贝
在传统的fork()时,子进程会直接把所有的父进程资源完全复制给新创建的进程,因此执行速度比较慢,而且实现过于简单,因为它拷贝的数据并不共享。更糟糕的情况是,如果新进程打算立即执行一个新的映像,那么所有的copy都将前功尽弃。linux的fork使用写时拷贝(copy-on-write)页实现。而写时拷贝是一种可以推迟甚至免除拷贝数据的技术。内核此时并不是复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝。
只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。也就是说,资源的复制只有在需要写入的时候才进行,在此之前,只是以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候才进行。在页根本不会写入的情况下,它们就无需复制。
这样fork()的实际开销就是复制父进程的页表已经给子进程创建唯一的进程描述符。在一般情况下,子进程创建后都马上运行一个可执行的文件,这种优化可以避免拷贝大量根本就不会被使用的数据。