fork(),vfork(),exec()和clone()之间的区别
我期待在Google上找到这四者之间的差异,我预计在这方面会有大量的信息,但这四个调用之间确实没有任何可靠的比较。
我着手编译一种基本的一览,看看这些系统调用之间的差异,这里是我得到的。 这些信息都是正确的吗?我错过了什么重要的东西吗?
Fork
:分叉调用基本上复制了当前进程,几乎在所有方面都相同(并非所有内容都被复制过,例如,某些实现中的资源限制,但想法是尽可能创建尽可能多的副本)。
新进程(子进程)获取不同的进程标识(PID),并将旧进程(父进程)的PID作为其父进程PID(PPID)。 因为这两个进程现在运行完全相同的代码,所以他们可以知道哪一个是由fork的返回代码 - 子代得到0,父代获得子代的PID。 当然,这是假设分叉调用起作用 - 如果不是,则不会创建子对象,并且父对象会获得错误代码。
Vfork
:vfork和fork之间的基本区别在于,当使用vfork()创建新进程时,父进程会暂时挂起,并且子进程可能会借用父进程的地址空间。 这种奇怪的状态继续下去,直到子进程退出,或者调用execve(),此时父进程继续。
这意味着vfork()的子进程必须小心以避免意外修改父进程的变量。 特别是,子进程不能从包含vfork()调用的函数返回,并且它不能调用exit()(如果需要退出,它应该使用_exit();实际上,对于孩子来说也是如此正常fork())。
Exec :
执行调用是基本上用新程序替换整个当前进程的一种方法。 它将程序加载到当前进程空间并从入口点运行它。 exec()用函数指向的可执行文件替换当前进程。 除非出现exec()错误,否则控制不会返回到原始程序。
Clone :
克隆,作为分叉,创建一个新的过程。 与fork不同,这些调用允许子进程与调用进程共享部分执行上下文,如内存空间,文件描述符表和信号处理程序表。
当使用克隆创建子进程时,它会执行函数应用程序fn(arg)。 (这与fork不同,从原始fork调用点继续执行子程序。)fn参数是一个指向子进程在其执行开始时调用的函数的指针。 arg参数被传递给fn函数。
当fn(arg)函数应用程序返回时,子进程终止。 fn返回的整数是子进程的退出代码。 子进程也可以通过调用exit(2)或收到致命信号后明确终止。
获得的信息形式:
感谢您抽时间阅读 ! :)
vfork()
是一个过时的优化。 在良好的内存管理之前, fork()
完成了父内存的完整拷贝,所以它非常昂贵。 因为在很多情况下, fork()
之后是exec()
,这会丢弃当前的内存映射并创建一个新的内存映射,这是不必要的开销。 如今, fork()
不会复制内存; 它只是设置为“copy on write”,所以fork()
+ exec()
和vfork()
+ exec()
一样高效。
clone()
是fork()
使用的系统调用。 用一些参数,它创建一个新的进程,与其他人一起创建一个线程。 它们之间的区别仅在于哪些数据结构(内存空间,处理器状态,堆栈,PID,打开的文件等)是否共享。
execve()
用从可执行文件加载的另一个替换当前的可执行映像。 fork()
创建一个子进程。 vfork()
是一个历史优化版本fork()
意在使用时execve()
之后直接调用fork()
事实证明,在非MMU系统(其中fork()
无法高效工作)以及fork()
具有巨大内存空间的进程运行一些小程序(认为Java的Runtime.exec()
)时可以很好地工作。 POSIX已经标准化了posix_spawn()
来取代vfork()
后两种更现代的用法。 posix_spawn()
与fork()/execve()
等价,并且还允许在两者之间进行一些fd杂耍。 它应该替换fork()/execve()
,主要用于非MMU平台。 pthread_create()
创建一个新的线程。 clone()
是一个特定于Linux的调用,可用于实现fork()
到pthread_create()
。 它提供了很多控制。 灵感来自rfork()
。 rfork()
是一个Plan-9特定的调用。 它应该是一个通用的调用,允许在完整的进程和线程之间进行多个程度的共享。 fork()
- 创建一个新的子进程,它是父进程的完整副本。 子进程和父进程使用不同的虚拟地址空间,最初由相同的内存页面填充。 然后,随着这两个进程被执行,虚拟地址空间开始越来越不同,因为操作系统执行由这两个进程中的任一个写入的内存页的懒惰复制并且分配独立副本每个进程的内存。 这种技术被称为写时复制(COW)。 vfork()
- 创建一个新的子进程,它是父进程的“快速”副本。 与系统调用fork()
,子进程和父进程共享相同的虚拟地址空间。 注意! 使用相同的虚拟地址空间,父和子都使用相同的堆栈,堆栈指针和指令指针,就像经典的fork()
! 为了防止使用相同堆栈的父级和子级之间的干扰,父级进程的执行将被冻结,直到子级调用exec()
(创建新的虚拟地址空间并转换到不同的堆栈)或_exit()
(终止流程执行)。 vfork()
是“fork-and-exec”模型的fork()
的优化。 它可以比fork()
执行4-5倍,因为与fork()
(即使在头脑中保留了COW),执行vfork()
系统调用不包括创建新地址空间(分配和设置新的页面目录)。 clone()
- 创建一个新的子进程。 此系统调用的各种参数指定父进程的哪些部分必须复制到子进程中,以及哪些部分将在它们之间共享。 因此,这个系统调用可以用来创建各种执行实体,从线程开始并通过完全独立的进程完成。 事实上, clone()
系统调用是用于实现pthread_create()
和fork()
系统调用的所有系列的基础。 exec()
- 重置进程的所有内存,加载并解析指定的可执行二进制文件,设置新的堆栈并将控制权传递给加载的可执行文件的入口点。 该系统调用永远不会将控制权返回给调用者,并用于将新程序加载到已存在的进程中。 这个使用fork()
系统调用的系统调用一起形成了一个名为“fork-and-exec”的经典UNIX进程管理模型。 上一篇: The difference between fork(), vfork(), exec() and clone()
下一篇: Raw types with generic methods independent of the generic type