Dea*_*orf 43 unix linux operating-system fork multiprocessing
目前我正在学习fork(),并execv()和我有关于组合的效率问题.
我看到了以下标准代码:
pid = fork();
if(pid < 0){
//handle fork error
}
else if (pid == 0){
execv("son_prog", argv_son);
//do father code
Run Code Online (Sandbox Code Playgroud)
我知道fork()克隆整个过程(复制整个堆等)并execv()用新程序替换当前地址空间.考虑到这一点,使用这种组合不是非常低效吗?我们正在复制进程的整个地址空间,然后立即覆盖它.
所以我的问题是:
即使我们有浪费,使用这个组合(而不是其他解决方案)使人们仍然使用它的优势是什么?
Joh*_*ger 45
使用这个组合(而不是其他一些解决方案)可以获得的优势是什么,即使我们有浪费,人们仍然会使用它?
你必须以某种方式创建一个新的进程.用户空间程序很少有方法可以实现这一点.POSIX曾经有过vfork()alognside fork(),有些系统可能有自己的机制,比如特定于Linux clone(),但自2008年以来,POSIX只指定fork()了posix_spawn()系列.该fork+ exec路线是比较传统的,是很好理解的,并且有一些缺点(见下文).该posix_spawn家庭被设计为一种特殊目的替代品,用于存在困难的环境fork(); 您可以在其规范的"基本原理"部分找到详细信息.
这篇来自Linux手册页的摘录vfork()可能很有启发性:
在Linux下,
fork(2)是使用copy-on-write页面实现的,因此fork(2)引起的唯一损失是复制父页面表所需的时间和内存,以及为子项创建唯一的任务结构.然而,在过去的糟糕时期,fork(2)需要制作一个完整的呼叫者数据空间副本,通常是不必要的,因为通常紧接着exec(3)完成.因此,为了提高效率,BSD引入了vfork()系统调用,该调用没有完全复制父进程的地址空间,而是借用父进程的内存和控制线程,直到调用execve(2)或退出.当孩子使用其资源时,父进程被暂停.vfork()的使用很棘手:例如,不修改父进程中的数据取决于知道寄存器中保存了哪些变量.
(重点补充)
因此,您对废物的关注对于现代系统(不限于Linux)来说并不是很有根据,但它在历史上确实是一个问题,并且确实存在旨在避免它的机制.目前,大多数这些机制已经过时.
小智 24
另一个答案是:
然而,在过去的糟糕时期,fork(2)需要制作调用者数据空间的完整副本,这通常是不必要的,因为通常紧接着就会执行exec(3).
显然,一个人过去的坏日子比其他人记得要年轻得多.
原始的UNIX系统没有用于运行多个进程的内存,并且它们没有用于将物理内存中的多个进程保持在同一逻辑地址空间中运行的MMU:它们将进程交换到磁盘而不是目前正在运行
fork系统调用与将当前进程交换到磁盘几乎完全相同,除了返回值和不通过交换另一个进程替换剩余的内存中副本.因为你必须换掉父进程才能运行子进程,所以fork + exec不会产生任何开销.
确实有一段时间fork + exec很尴尬:当有MMU提供逻辑和物理地址空间之间的映射但是页面错误没有保留足够的信息,即写时复制和其他一些虚拟 - 存储/请求 - 寻呼方案是可行的.
这种情况非常痛苦,不仅仅是对于UNIX,硬件的页面错误处理被调整为非常快速地"重放".
Ton*_*ous 23
不再是.有一种称为COW(写入时复制)的东西,只有当两个进程中的一个(父/子)尝试写入共享数据时,才会复制它.
在过去的:
该fork()系统调用复制调用进程(父)的地址空间中创建一个新的进程(孩子).将父母的地址空间复制到孩子身上是操作中最昂贵的部分fork().
现在:
呼叫到fork()经常几乎立即将呼叫跟随exec()子进程,这与一个新的程序取代了孩子的记忆.例如,这就是shell通常所做的事情.在这种情况下,复制父地址空间所花费的时间在很大程度上被浪费了,因为子进程在调用之前将使用很少的内存exec().
出于这个原因,Unix的更高版本利用虚拟内存硬件来允许父和子共享映射到其各自地址空间的内存,直到其中一个进程实际修改它.这种技术称为写时复制.为此,在fork()内核上将地址空间映射从父级复制到子级而不是映射页面的内容,同时将现在共享的页面标记为只读.当其中一个进程尝试写入其中一个共享页面时,该进程会发生页面错误.此时,Unix内核意识到该页面实际上是一个"虚拟"或"写时复制"副本,因此它为错误进程创建了一个新的,私有的,可写的页面副本.这样,在实际写入之前,实际上不会复制各个页面的内容.这种优化使得fork()后面exec()的孩子便宜得多:孩子可能只需要在调用之前复制一个页面(其堆栈的当前页面)exec().
| 归档时间: |
|
| 查看次数: |
2836 次 |
| 最近记录: |