fork函数原型
#include<unistd.h>
pid_t fork(void);
关于fork的了解
一个现有的进程可以调用fork函数创建一个新的进程。由fork创建的新进程被称为子进程。
关于fork的返回值
fork函数被调用一次,但是它的返回值有两个。子进程的返回值为0,父进程的返回值则是新创建的子进程的ID。
fork函数创建子进程之后
父子进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程的数据空间,堆和栈的副本。注意,这是子进程所拥有的副本。父进程和子进程并不共享这些存储空间的部分。父子进程共享的是正文段。
关于写时复制技术
fork函数创建出子进程以后,经常跟着exec,所以现在很多实现并不执行一个父进程数据段、栈和堆的完全副本。作为替代,使用了写时复制的技术。这些区域由父进程和子进程共享,而且内核将他们的访问权限改变成只读权限。如果父子进程中其中一个试图修改这些区域,则内核只为修改区域的这块内存制作一个副本,通常是虚拟存储系统中的一页。
子进程对变量所做的改变并不影响父进程中该变量的值
fork函数之后先执行父进程还是子进程
在fork函数之后先执行父进程还是子进程是不确定的,这是取决于内核所使用的调度算法。如果要求父进程和子进程之前相互同步,则要求某种形式的进程间通信。
文件共享
在重定向父进程的标准输出时,子进程的标准输出也被重定向。实际上,fork的一个特性是父进程的的所有打开文件描述符都被复制到子进程。我们说“复制”是因为对每个文件描述符来说,就好像执行了dup函数。重要的一点是,父进程和子进程共享同一个文件偏移量。如果父进程的标准输出已重定向,那么子进程写到标准输出时,它将更新与父进程共享的该文件的偏移量。
fork之后文件描述符有一下两种常见的情况
(1)父进程等待子进程完成。父进程无需对其描述符做任何处理
(2)父进程和子进程各自执行不同的程序段。父子进程各自关闭他们不需使用的文件描述符,不干扰对方使用的文件描述符。
fork失败的两个主要的原因
(1)系统中已经有了太多的进程
(2)该实际用户ID的进程总数超过了系统限制
fork的两种用法
(1)一个父进程希望复制自己,使父进程和子进程同时执行不同的代码段。这在网络服务进程中是常见的——父进程等待客户端的服务请求,子进程处理此请求。父进程继续等待下一个服务请求
(2)一个进程要执行一个不同的程序,子进程从fork返回后立即调用exec
fork和vfork的区别
(1)fork和vfork一样都是创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec(或exit),于是也就不会引用该地址空间,不过在子进程调用exec(或exit)之前,他在父进程的空间中运行。
(2)vfork保证子进程先运行,在它调用exec(或exit)之后,父进程才可能被调度运行,当子进程调用这两个函数(exec或exit)中的任意一个时,父进程会恢复运行。(如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁)。因为我们可以保证在子进程调用exec或exit之前,内核会使父进程处于休眠状态。