fork(),vfork(),exec()和clone()

我期待在Google上find这四者之间的差异,我预计在这方面会有大量的信息,但这四个调用之间确实没有任何可靠的比较。

我着手编译一下这些系统调用之间的区别,这里是我得到的。 所有这些信息都是正确的/我错过了什么重要的东西?

Fork :叉调用基本上是复制当前进程,几乎在所有方面都是相同的(并非所有的东西都被复制,例如,在一些实现中资源限制,但是想法是尽可能地创build副本)。

新进程(subprocess)获取不同的进程ID(PID),并将旧进程(父进程)的PID作为其父PID(PPID)。 因为这两个进程现在运行完全相同的代码,所以他们可以知道哪个是由fork的返回代码 – 子代得到0,父代获得子代的PID。 当然,这一切都假设分叉调用起作用 – 否则,不会创build子对象,并且父对象将获得错误代码。

Vfork :vfork和fork的基本区别在于,当使用vfork()创build一个新进程时,父进程暂时挂起,subprocess可能借用父进程的地址空间。 这种奇怪的状态继续下去,直到subprocess退出,或调用execve(),在这一点父进程继续。

这意味着vfork()的subprocess必须小心,以避免意外地修改父进程的variables。 特别的,subprocess不能从包含vfork()调用的函数返回,也不能调用exit()(如果需要退出,应该使用_exit();实际上,对于subprocess也是这样一个普通的fork())。

Exec :执行调用是基本上用新程序replace整个当前进程的一种方法。 它将程序加载到当前进程空间并从入口点运行它。 exec()用函数指向的可执行文件replace当前进程。 除非有exec()错误,否则控制将不会返回到原始程序。

Clone :克隆,作为分叉,创build一个新的过程。 与fork不同,这些调用允许subprocess与调用进程共享部分执行上下文,如内存空间,文件描述符表和信号处理程序表。

当使用克隆创buildsubprocess时,它将执行函数应用程序fn(arg)。 (这与fork不同,fork从原始fork调用的位置继续执行。)fn参数是一个指向subprocess在执行开始时调用的函数的指针。 arg参数被传递给fn函数。

当fn(arg)函数应用程序返回时,subprocess终止。 fn返回的整数是subprocess的退出代码。 subprocess也可以通过调用exit(2)或在收到致命信号后明确终止。

获得的信息forms:

  • fork和exec之间的区别
  • http://www.allinterview.com/showanswers/59616.html
  • http://www.unixguide.net/unix/programming/1.1.2.shtml
  • http://linux.about.com/library/cmd/blcmdl2_clone.htm

感谢您抽时间阅读 ! 🙂

  • vfork()是一个过时的优化。 在良好的内存pipe理之前, fork()完成了父内存的完整拷贝,所以它非常昂贵。 因为在很多情况下, fork()之后是exec() ,这会丢弃当前的内存映射并创build一个新的内存映射,这是不必要的开销。 如今, fork()不会复制内存; 它只是设置为“copy on write”,所以fork() + exec()vfork() + exec()一样高效。

  • clone()fork()使用的系统调用。 用一些参数创build一个新的进程,与其他进程一起创build一个线程。 它们之间的区别在于哪些数据结构(内存空间,处理器状态,堆栈,PID,打开文件等)是否共享。

  • execve()用从可执行文件加载的另一个replace当前的可执行映像。
  • fork()创build一个subprocess。
  • vfork()vfork()的历史优化版本,意味着在fork()之后直接调用execve()时使用。 事实certificate,在非MMU系统(其中fork()不能以有效的方式工作),以及fork()内存占用大的进程运行一些小程序(认为Java的Runtime.exec() )。 POSIX已经标准化了posix_spawn()来代替vfork()后两种更现代的用法。
  • posix_spawn()fork()/execve()相当,也允许一些fd之间的杂耍。 它应该replacefork()/execve() ,主要用于非MMU平台。
  • pthread_create()创build一个新的线程。
  • clone()是一个特定于Linux的调用,可以用来实现从fork()pthread_create()任何事情。 它给了很多控制。 灵感来自rfork()
  • rfork()是一个Plan-9特定的调用。 它应该是一个通用的调用,允许在完整的进程和线程之间进行多个程度的共享。
  1. fork() – 创build一个新的subprocess,它是父进程的完整副本。 subprocess和父进程使用不同的虚拟地址空间,最初由相同的内存页面填充。 然后,随着这两个进程被执行,虚拟地址空间开始越来越不同,因为操作系统执行由这两个进程中的任何一个正在写入的内存页面的惰性拷贝,并且分配独立的修改页面每个进程的内存。 这种技术被称为写时复制(COW)。
  2. vfork() – 创build一个新的subprocess,这是父进程的“快速”副本。 与系统调用fork() ,subprocess和父进程共享相同的虚拟地址空间。 注意! 使用相同的虚拟地址空间,父和子都使用相同的堆栈,堆栈指针和指令指针,就像在传统的fork() ! 为了防止使用相同堆栈的父和子之间的干扰,父进程的执行被冻结,直到subprocess调用exec() (创build一个新的虚拟地址空间并转换到另一个堆栈)或_exit() (终止stream程执行)。 vfork()是“fork-and-exec”模型的fork()的优化。 它可以比fork()快4-5倍,因为与fork() (即使在头脑中保留了COW),执行vfork()系统调用不包括创build一个新的地址空间分配和build立新的页面目录)。
  3. clone() – 创build一个新的subprocess。 此系统调用的各种参数指定父进程的哪些部分必须复制到subprocess中,以及哪些部分将在它们之间共享。 因此,这个系统调用可以用来创build各种执行实体,从线程开始,完全独立的进程完成。 事实上, clone()系统调用是用于实现pthread_create()fork()系统调用的所有族的基础。
  4. exec() – 重置进程的所有内存,加载并parsing指定的可执行二进制文件,设置新的堆栈并将控制权交给加载的可执行文件的入口点。 这个系统调用永远不会将控制权返回给调用者,并用于将新程序加载到已经存在的进程中。 这个使用fork()系统调用的系统调用一起形成了一个名为“fork-and-exec”的经典UNIX进程pipe理模型。

fork(),vfork()和clone()都调用do_fork()来做真正的工作,但是使用不同的参数。

 asmlinkage int sys_fork(struct pt_regs regs) { return do_fork(SIGCHLD, regs.esp, &regs, 0); } asmlinkage int sys_clone(struct pt_regs regs) { unsigned long clone_flags; unsigned long newsp; clone_flags = regs.ebx; newsp = regs.ecx; if (!newsp) newsp = regs.esp; return do_fork(clone_flags, newsp, &regs, 0); } asmlinkage int sys_vfork(struct pt_regs regs) { return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0); } #define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ #define CLONE_VM 0x00000100 /* set if VM shared between processes */ SIGCHLD means the child should send this signal to its father when exit. 

对于fork,子和父有独立的VM页表,但是由于效率,fork不会真的复制任何页面,只是将所有可写页面设置为只读subprocess。 所以当subprocess想要在页面上写东西的时候,会发生一个页面exception,内核会从拥有写权限的旧页面中分配一个新页面。 这就是所谓的“抄写”。

对于vfork来说,虚拟记忆完全是由孩子和父亲共同的,正因为如此,父母和孩子才会互相影响才能同时醒来。 因此,父亲会睡在“do_fork()”的末尾,当小孩调用exit()或execve()时会醒来,因此它将拥有新的页表。 这是父亲睡觉的代码(在do_fork()中)。

 if ((clone_flags & CLONE_VFORK) && (retval > 0)) down(&sem); return retval; 

这里是代码(在mm_release()调用exit()和execve())唤醒父亲。

 up(tsk->p_opptr->vfork_sem); 

对于sys_clone(),它可以更灵活,因为你可以input任何clone_flags。 因此,pthread_create()使用许多clone_flags来调用这个系统调用:

int clone_flags =(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);

简介:fork(),vfork()和clone()将用父进程创build具有不同的共享资源的subprocess。 我们也可以说vfork()和clone()可以创build线程(实际上它们是具有独立task_struct的进程),因为它们共享父进程的VM页表。

在fork()中,subprocess或父进程将根据CPUselect执行。但是在vfork()中,孩子肯定会先执行。 在孩子终止之后,父母将执行。