从手册页vfork()
:
vfork() 与 fork() 的不同之处在于,父进程被挂起,直到子进程调用 execve(2) 或 _exit(2)。子进程与其父进程共享所有内存,包括堆栈,直到子进程发出 execve() 为止。子进程不得从当前函数返回或调用 exit(),但可以调用 _exit()。
为什么孩子应该使用 an_exit()
而不是简单地 call exit()
?我希望这适用于vfork()
和fork()
。
答案1
作为早些时候见过,vfork
不允许子进程访问父进程的内存。exit
是一个 C 库函数(这就是为什么它是经常写作为exit(3)
)。它执行各种清理任务,例如刷新和关闭 C 流(通过在 中声明的函数打开文件stdio.h
)以及执行在 中注册的用户指定函数atexit
。所有这些任务都涉及对进程内存的读取和写入。
_exit
退出而不进行清理。它直接是一个系统调用(这就是它被写为 的原因_exit(2)
),通常通过将系统调用号放入处理器寄存器中并执行特定的处理器指令(分支到系统调用处理程序)来实现。这不需要访问进程内存,因此在之后执行是安全的vfork
。
之后fork
,就没有这样的限制:父进程和子进程现在是完全自治的。
答案2
exit
进行额外的清理,例如调用注册的函数,atexit
因此它访问复制部分之外的数据。_exit
直接执行系统调用,无需任何清理(内核内除外)。
答案3
您让子进程调用 _exit() 以避免子进程退出时刷新 stdio(或其他)缓冲区。由于子进程构成父进程的精确副本,因此子进程仍然拥有父进程在“stdout”或“stderr”中拥有的任何内容,即来自 <stdio.h> 的缓冲区。您可以(并且会在不合时宜的时候)通过调用 exit() 获得双重输出,一种来自子进程的 atexit 处理程序,另一种来自父进程(当父进程中的缓冲区已满并被刷新时)。
我意识到上面的答案集中在 stdio.h 的细节上,但这个想法可能会延续到其他缓冲 I/O,正如上面的答案之一所示。
答案4
exit()
:- 执行一些清理任务,例如关闭 i/o 流等,然后返回内核。
_exit()
:- 直接进入内核(不执行任何清理任务)。
fork()
:父级和子级都有不同的文件表,因此子级所做的更改不会影响父级的环境参数,反之亦然。
vfork()
:父进程和子进程使用相同的文件表,因此子进程所做的更改会影响父进程的环境参数。例如某个变量var=10
,现在由子进程运行,然后运行父进程,您也可以在父进程的输出中var++
看到效果。var++
正如我所说,如果你使用exit()
invfork()
那么所有的 i/o 都已经关闭了。因此,即使父进程正确运行,您也无法获得正确的输出,因为所有变量都被刷新并且所有流都被关闭。