1、许多其它操作系统提供spawn产生进程的机制。
而Unix采用与众不同的方式,它把上述步骤分解到两个单独的函数中去执行:fork()和exec()。
首先,fork()通过拷贝当前进程创建一个子进程、子进程与父进程的唯一区别就在一进程ID。PID、PPID(父进程进程号)
exec()函数负责读取可执行文件并将其载入地址空间开始运行。
2、写时拷贝
传统的fork()系统调用直接把所有的资源赋值给新创建的进程。这种实现过于简单并效率低下。因为他拷贝的数据也许并不共享,更糟糕的是如果新进程打算立即执行一个新的
映像,那么所有的拷贝都将成为无用功。
Linux的fork()使用写时复制技术。写时拷贝是一种可以推迟甚至免除拷贝数据的一种技术。内核此时并不复制整个进程地址空间,而是让父子进程共享同一份拷贝。
只有在需要写入的时候,数据才会被复制,从而使整个进程拥有各自的拷贝。也就是说资源的复制只有在需要写入的时候才会进行。在此之前,只是以只读的方式共享。当需要
写入的时候,内核将复制一份副本,设置为可写,并修改相应的页表。这种技术使地址空间上的页拷贝被推迟到实际发生写入的时候才进行。在页根本不会被写入的情况下,他们就无需复制了。(比如fork()后立即调用exec())
3、fork()
fork()通过clone()系统调用实现。这个调用通过一系列的参数来标志指明父子进程需要共享的资源。
fork()的实际开销就是拷贝父进程的页表以及给予子进程创建唯一的进程描述符。
fork()、vfork()、和__clone()库函数都根据各自的需要的参数去调用clone(),然后由clone()去掉用do_fork()
do_fork()完成了创建中大部分的工作。它的定义在kernel/fork.c文件中。该函数调用copy_process()函数,然后让进程开始运行。
4、vfork()
除了不拷贝父进程的页表项外,vfork()系统调用和fork()的功能相同。
子进程作为父进程的一个单独的线程在它的地址空间运行。
父进程被阻塞,直到子进程退出或者执行exec()。
子进程不能向地址空间写入。
现代由于执行fork()引入了写时拷贝技术并且明确了子进程先运行,vfork() 的好处就仅限于不拷贝父进程的页表项了。
如果linux将来fork()有了写时拷贝页表项,那么vfork()就可以彻底退休了。