我知道答案"为什么会这样?"是因为语言是这样发明的,但似乎很多浪费的努力fork()
产生了一个叫做它的过程的副本。也许它有时是有用的,但肯定大多数时候有人想要开始一个新的过程它不是一个重复的呼叫一个?为什么fork创建一个相同的进程而不是一个空的或一个通过传递参数定义的进程?
来自yolinux
fork()系统调用将生成一个新的子进程,它是一个 除了具有新系统进程之外,与父进程相同的进程 ID
换句话说,什么时候开始使用父进程的副本是有用的?
答案 0 :(得分:8)
让父进程在子进程中重复的一个重要优点是它允许父程序对子进程进行自定义。环境在执行之前。例如,父母可能想要阅读子进程' stdout
,在这种情况下,它需要设置管道,以便在执行新程序之前读取。
它也没有听起来那么糟糕,效率明智。整个过程是在Linux上使用进程的写时复制语义实现的。内存(手册页中注明的特殊情况除外):
在Linux下(在版本7以来的大多数unices中,现在所有unices的父级都是),fork()是使用copy-on-write页面实现的,所以只有 它产生的惩罚是复制所需的时间和记忆 父母的页面表(也可以是写时复制),并为孩子创建一个独特的任务结构。
答案 1 :(得分:4)
与所有期望相反,它主要是fork
,使得在Unices上创建流程的速度非常快。
AFAIK,在Linux上,实际进程内存不会在fork上复制,子进程以与父进程相同的虚拟内存映射开始,并且仅在子进行更改的位置和时间复制页面。无论如何,大多数页面都是只读代码,因此永远不会复制它们。这称为copy-on-write
。
复制父进程很有用的用例:
当你说cat foo >bar
时,shell分叉,子进程(仍然是shell)准备重定向,然后执行cat foo
。执行的程序在与子shell相同的PID下运行,并继承所有打开的文件描述符。你不会相信编写一个基本的Unix shell是多么容易。
守护进程在后台运行。他们中的许多人在初步准备后进行分叉,父母退出,孩子从终端分离并继续在后台运行。
许多网络守护进程必须同时处理多个连接。示例sshd。主守护程序以root身份运行并侦听端口22上的新连接。当新连接进入时,它会分叉子进程。子进程只保留表示该连接的新套接字,对用户进行身份验证,删除权限等。
答案 2 :(得分:4)
fork
系统调用有一些非常合理的用法。以下是一些例子:
fork
在子和父之间共享内存(通过写时复制语义),所以父进程可以加载一些静态数据,这些数据可以立即共享给子进程。 Android上的zygote
进程执行此操作:它预加载Java(Dalvik)运行时和许多类,然后简单地按需创建新的应用程序进程(继承父运行时和已加载类的副本)。fork
关闭工作程序以执行使用预加载的初始化数据的任务。fork/exec
,进程可以通过标准系统调用(close
,signal
,dup
等来分叉,执行自定义。)然后exec
当它准备就绪时。fork/exec
因此是现存最简单的流程创建API之一,同时也是最强大和最灵活的API之一。公平地说,fork
也有其公平的问题。例如,它对多线程程序不起作用:在新进程中只创建一个线程,并且锁未正确关闭(导致atfork
处理程序必须重置{{1}的锁状态}})。
答案 3 :(得分:3)
为什么fork()
?它与C无关.C本身只在当时存在。由于原始UNIX内存页面和进程管理的工作方式,导致进程被分页,然后在不同位置重新分页,而无需卸载进程的第一个副本,这是微不足道的。
在 Unix时间共享系统的演变(http://cm.bell-labs.com/cm/cs/who/dmr/hist.html)中,Dennis Ritchie说" 事实上,PDP-7的分叉电话需要精确的27行汇编代码。"请参阅该链接了解更多信息。
线程是邪恶的。对于线程,你基本上有许多进程都可以访问相同的内存空间,这些内存空间可以互相跳舞。值。根本没有记忆保护。有关更全面的解释,请参阅 Unix编程的艺术,第7章(http://www.faqs.org/docs/artu/ch07s03.html#id2923889)。