#include <unistd.h> |
Returns: 0 in child, process ID of child in parent, -1 on error |
fork函数调用一次返回两次的值,在子进程返回0,在父进程返回子进程的进程ID号,出错时返回-1.
fork之后,子进程和父进程都会继续执行fork调用之后的指令。子进程是父进程的副本。它将获得父进程的数据空间,堆和栈的副本,这些都是副本,父子进程并不共享这部分的内存。也就是说,子进程对父进程中的同名变量进行修改并不会影响其在父进程中的值。但是父子进程又共享一些东西,简单说来就是程序的正文段。正文段存放着由cpu执行的机器指令,通常是read-only的.
由于在fork之后我们常常都是跟个exec在后面,所以为了提高效率,很多的实现并不完全复制数据段和堆、栈,而是采用写时复制(copy-on-write),只有当子进程修改到相应数据时,才会复制父进程的数据进行修改.
一个进程一旦调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了(不过exec类函数中有的还允许继承环境变量之类的信息).
文件共享:
fork之后,父进程和子进程共享每个打开文件的文件表目录,所以采用同步机制很重要。因为父进程和子进程写同一个文件描述符时,如果没有同步机制,那么父进程就会覆盖子进程所写内容或者相反。有两种方法处理fork执行后的文件描述符.
1.父进程等待子进程完成.当子进程结束后,父进程对于同子进程共享的文件描述符进行文件偏移同步更新.
2.父进程和子进程独立执行.在fork执行之后,父进程和子进程都关闭它不需要的文件描述符,这样两者不需要考虑同步问题.
vfork最早起源于2.9BSD,它与fork的不同就在于它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用exec.vfork出来的子进程是在父进程的空间中运行的,它的存在就是为了exec调用,所以它不需要复制这些东西。如果这时子进程修改了某个变量,这将影响到父进程.
vfork与fork的另一区别是:vfork保证子进程先运行,在它调用exec或exit后父进程才可能调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。 而fork的父子进程运行顺序是不定的,它取决于内核的调度算法.