操作系统概念与APUE说
使用vfork(),父进程被挂起,子进程使用父进程的地址空间。因为vfork()不使用写副本,因此如果子进程更改父进程地址空间的任何页面,则一旦父进程恢复,修改后的页对父进程将是可见的。因此,必须谨慎使用vfork(),以确保子进程不修改父进程的地址空间。当子进程在创建后立即调用exec()或exit()时,将使用vfork()。
我该如何理解最后一句?
当vfork()创建的子进程调用exec()时,exec()是否会通过加载新程序来修改父进程的地址空间?
当vfork()创建的子进程调用exit()时,exit()是否在终止子进程时修改父进程的地址空间?
我更喜欢Linux。
谢谢。
发布于 2018-10-15 16:25:03
当
vfork()创建的子进程调用exec()时,exec()是否会通过加载新程序来修改父进程的地址空间?
不,exec()为新程序提供了一个新的地址空间;它不修改父地址空间。例如,请参见讨论execPOSIX中的职能和Linuxexecve()命令页。
当由vfork()创建的子进程调用exit()时,exit()是否在终止子进程时不修改父进程的地址空间?
普通的exit()可能-它运行由运行的程序安装的出口挂钩(包括它的库)。vfork()的限制性更强;因此,在Linux上,它的任务使用了不调用C库的清理函数的_exit()。
vfork()被证明是很难纠正的;它已经在POSIX标准的当前版本中被删除了,应该使用posix_spawn()。
但是,除非您真的知道自己在做什么,否则不应该使用vfork()或posix_spawn();坚持使用好的旧fork()和exec()。
上面链接的Linux手册提供了更多的上下文:
然而,在过去糟糕的日子里,
fork(2)需要对调用者的数据空间进行完整的复制,这通常是不必要的,因为通常是在完成exec(3)之后。因此,为了提高效率,BSD引入了vfork()系统调用,它没有完全复制父进程的地址空间,而是借用了父进程的内存和控制线程,直到调用execve(2)或退出为止。父进程在子进程使用其资源时被挂起。vfork()的使用很棘手:例如,不修改父进程中的数据取决于是否知道哪些变量保存在寄存器中。
发布于 2018-10-15 16:52:45
调用vfork()时,将创建一个新进程,该新进程借用父进程的进程映像(堆栈除外)。子进程被赋予了自己的新堆栈星号,但是不允许从调用return的函数中获取vfork()。
当子进程运行时,父进程将被阻塞,因为子进程借用父进程的地址空间。
不管您做什么,只要访问堆栈的所有内容都只修改子堆栈的私有堆栈。但是,如果修改全局数据,则会修改公共数据,因此也会影响父数据。
修改全球数据的内容有:
vfork()的函数的非本地变量。一旦您调用_exit() (重要,永远不要调用exit()),子节点将被终止并将控制权返回给父节点。
如果您调用exec*()家族中的任何函数,则会使用新的程序代码、新数据和父级堆栈的一部分创建一个新的地址空间(参见下面)。一旦准备就绪,子节点就不再从子节点借用地址空间,而是使用自己的地址空间。
该控件将返回给父进程,因为它的地址空间不再被另一个进程使用。
重要:在Linux上,没有真正的vfork()实现。相反,Linux是基于1988年SunOS4.0引入的写vfork()概念来实现fork()的。为了让用户相信他们使用了vfork(),Linux只是设置共享数据并挂起父数据,而子代没有调用_exit()或其中一个exec*()函数。
因此,真正的vfork()不需要为内核中的子程序设置地址空间描述,这对Linux并没有好处。这将导致vfork()的速度不会比fork()快。在实现实际vfork()的系统上,它通常比fork()快3倍,并且影响使用vfork() - ksh93、最近的Bourne Shell和csh的shell的性能。
您不应该从exit()子级调用vfork()的原因是,如果在调用vfork()之前有未刷新的数据,exit()会刷新stdio。这可能会导致奇怪的结果。
顺便说一句:posix_spawn()是在vfork()之上实现的,所以vfork()不会从操作系统中删除。已经有人提到,Linux不使用vfork()来实现posix_spawn()。
对于堆栈,几乎没有文档,以下是Solaris手册页所述的内容:
The vfork() and vforkx() functions can normally be used the
same way as fork() and forkx(), respectively. The calling
procedure, however, should not return while running in the
child's context, since the eventual return from vfork() or
vforkx() in the parent would be to a stack frame that no
longer exists. 因此,实现可以做它喜欢做的任何事情。Solaris实现对调用vfork()的函数的堆栈帧使用共享内存。没有实现从父堆栈授予对堆栈旧部分的访问权限。
https://unix.stackexchange.com/questions/475609
复制相似问题