1.传统的fork()系统调用直接把所有的资源复制给新创建的进程.Linux的fork()使用写时拷贝(copy-on-write)页实现.写时拷贝是一种可以推迟甚至免除拷贝数据的技术,内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝.
只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝,也就是说,资源的复制只有在需要写入的时候才进行,在此之前,只是以只读的方式共享.这种技术使地址空间上的页的拷贝被推迟到实際发生写入的时候才进行.在页跟本不会被写入的情况下(比如:fork()后立即调用exec())它们就无需复制了.
2.linux通过系统调用clone()来实现fork().然后由clone()来调用do_fork().
附:linux下fork()函数的实现:
Linux通过clone()系统调用实现fork()。这个调用通过一系列的参数标志来指明父,子进程需要共享的资源。fork(),vfork()和__clone()库函数都根据各自需要的参数标志去调用clone().然后由clone()去调用do_fork().
do_frok完成了创建中的大部分工作,它的定义在ker/frok.c文件中。该函数调用copy_process()的函数,然后让进程开始运行。copy_process()函数完成的工作很有意思:
1.调用dup_task_struct()为新进程创建一个内核栈,thread_info结构和task_struct,这些值与当前进程的值相同。此时,子进程和父进程的描述符是完全相同的。
2.检查新创建的这个子进程后,当前用户所拥有的进程数目没有超出给它分配的资源的限制。
3.现在,子进程着手使自己与父进程区别开来。进程描述符内的许多成员都要被清0或者设为初始值。进程描述符的成员值并不是继承而来的,而主要是统计信息。进程描述符中的大多数数据都是共享的。
4.接下来,子进程的状态被设置为TASK_UNINTERRUPTIBLE以保证它不会投入运行。
5.copy_process()调用copy_flags()以更新task_struct的flags成员。表明进程是否拥有超级用户权限的PF_SUPERPRIV的标志被清0.表明进程还没有调用exec()函数的PF_FORKNOEXEC标志被设置。
6.调用get_pid()为新进程获取一个有效的PID。
7.根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件,文件系统信息,信号处理函数,进程地址空间和命名空间等。再一半情况下,这些资源会被给定进程的所有线程共享;否则,这些资源对每个进程是不同的,因此被拷贝到了这里。
8.让父进程和子进程平分剩余的时间片。
9.最后,copy_process()做扫尾工作并返回一个指向子进程的指针。
再回到do_fork()函数,如果copy_process()函数返回成功,新创建的子进程被唤醒并让其投入运行。内核有意选择子进程首先执行。因为一半子进程都会马上调用exec()函数,这样可以避免写时拷贝的额外开销,如果父进程首先执行的话,有可能会开始向地址空间写入。