clone()、fork()、vfork()都是Linux的系统调用。
进程一般由代码段、数据段和PCB进程控制块组成。
fork 创造的子进程复制了父亲进程的资源,包括内存的内容task_struct内容,新旧进程使用同一代码段,复制数据段和堆栈段,这里的复制采用了注明的copy_on_write技术,即一旦子进程开始运行,则新旧进程的地址空间已经分开,两者运行独立。
优点是子进程的执行独立于父进程,具有良好的并发性。
缺点是两者的通信需要专门的通信机制,如pipe、fifo和system V等。
其实在复制过程中,子进程复制了父进程的task_struct,系统堆栈空间和页面表,在子进程运行前,两者指向同一页面。而当子进程改变了父进程的变量时候,会通过copy_on_write的手 段为所涉及的页面建立一个新的副本。因此fork效率并不低。
vfork函数创建的子进程完全运行在父进程的地址空间上,子进程对虚拟地址空间任何数据的修改都为父进程所见。这与fork是完全不同的,fork进程是独立的空间。另外一点不同的是vfork创建的子进程后,父进程会被阻塞,直到子进程执行exec()和exit()。
当创建子进程的目的仅仅是为了调用exec()执行另一个程序时,子进程不会对父进程的地址空间又任何引用。因此,此时对地址空间的复制是多余的,通过vfork可以减少不必要的开销。
系统调用fork()和vfork()是无参数的,而clone()则带有参数。fork()是全部复制,vfork()是共享内存,而clone()是则可以将父进程资源有选择地复制给子进程,而没有复制的数据结构则通过指针的复制让子进程共享,具体要复制哪些资源给子进程,由参数列表中的clone_flags来决定。另外,clone()返回的是子进程的pid。
轻量级进程由clone()函数创建。clone函数功能强大,带了众多参数,因此由他创建的进程要比前面2种方法要复杂。clone可以让你有选择性的继承父进程的资源,你可以选择想vfork一样和 父进程共享一个虚存空间,从而使创造的是线程,你也可以不和父进程共享,你甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系
do_fork的参数与clone系统调用的参数类似, 不过多了一个regs(内核栈保存的用户模式寄存器). 实际上其他的参数也都是用regs取的
- clone:
- clone的API外衣, 把fn, arg压入用户栈中, 然后引发系统调用. 返回用户模式后下一条指令就是fn.
- sysclone: parent_tidptr, child_tidptr都传到了 do_fork的参数中
- sysclone: 检查是否有新的栈, 如果没有就用父进程的栈 (开始地址就是regs.esp)
- fork, vfork:
- 服务例程就是直接调用do_fork, 不过参数稍加修改
- clone_flags:
- sys_fork: SIGCHLD|0;
- sys_vfork: SIGCHLD| (clone_vfork | clone_vm)
- 用户栈: 都是父进程的栈.
- parent_tidptr, child_ctidptr都是NULL.