linux下的fork()函数
1.传统的fork()系统调用直接把所有的资源复制给新创建的进程.Linux的fork()使用写时拷贝(copy-on-write)页实现.写时拷贝是一种可以推迟甚至免除拷贝数据的技术,内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝.
www.zhishiwu.com
只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝,也就是说,资源的复制只有在需要写入的时候才进行,在此之前,只是以只读的方式共享.这种技术使地址空间上的页的拷贝被推迟到实 发生写入的时候才进行.在页跟本不会被写入的情况下(比如: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标志被设置。 www.zhishiwu.com
6.调用get_pid()为新进程获取一个有效的PID。
7.根据传递给clone()的参数标志,copy_process()拷贝或共享打开的文件,文件系统信息,信号处理函数,进程地址空间和命名空间等。再一半情况下,这些资源会被给定进程的所有线程共享;否则,这些资源对每个进程是不同的,因此被拷贝到了这里。
8.让父进程和子进程平分剩余的时间片。
9.最后,copy_process()做扫尾工作并返回一个指向子进程的指针。
再回到do_fork()函数,如果copy_process()函数返回成功,新创建的子进程被唤醒并让其投入运行。内核有意选择子进程首先执行。因为一半子进程都会马上调用exec()函数,这样可以避免写时拷贝的额外开销,如果父进程首先执行的话,有可能会开始向地址空间写入。