我希望在谷歌上找到这四者之间的区别,我希望有关于此的大量信息,但四个电话之间确实没有任何可靠的比较。
我着手尝试编写一种基本的一目了然的看看这些系统调用之间的差异,这就是我得到的。所有这些信息都是正确的吗?我错过了什么重要的信息吗?
Fork
:fork调用基本上复制了当前进程,几乎在所有方面都相同(并非所有内容都被复制,例如,某些实现中的资源限制,但想法是创建尽可能接近的尽可能复制。)
新进程(子进程)获取不同的进程ID(PID),并将旧进程(父进程)的PID作为其父PID(PPID)。因为这两个进程现在运行的代码完全相同,所以它们可以通过fork的返回代码告诉哪个进程 - 子进程为0,父进程获取子进程的PID。当然,这就是假设fork调用有效 - 如果没有,则不创建子节点并且父节点获取错误代码。
Vfork
:vfork和fork之间的基本区别在于,当使用vfork()创建新进程时,父进程会暂时挂起,子进程可能会借用父进程的地址空间。这种奇怪的状态一直持续到子进程退出或调用execve(),此时父进程
过程继续。
这意味着vfork()的子进程必须小心,以避免意外修改父进程的变量。特别是,子进程不能从包含vfork()调用的函数返回,并且它不能调用exit()(如果需要退出,它应该使用_exit();实际上,对于子进程也是如此正常的fork())。
Exec :
exec调用是一种基本上用新程序替换整个当前进程的方法。它将程序加载到当前进程空间并从入口点运行它。 exec()用函数指向的可执行文件替换当前进程。除非存在exec()错误,否则控件永远不会返回到原始程序。
Clone :
Clone,作为fork,创建一个新进程。与fork不同,这些调用允许子进程与调用进程共享其执行上下文的一部分,例如内存空间,文件描述符表和信号处理程序表。
使用clone创建子进程时,它将执行函数应用程序fn(arg)。 (这与fork不同,fork从原始fork调用开始继续执行。)fn参数是指向子进程在执行开始时调用的函数的指针。 arg参数传递给fn函数。
当fn(arg)函数应用程序返回时,子进程终止。 fn返回的整数是子进程的退出代码。子进程也可以通过调用exit(2)或在收到致命信号后明确终止。
信息来自:
感谢您花时间阅读本文! :)
答案 0 :(得分:143)
vfork()
是一种过时的优化。在良好的内存管理之前,fork()
制作了父级内存的完整副本,因此它非常昂贵。因为在很多情况下fork()
之后是exec()
,它会丢弃当前的内存映射并创建一个新内存映射,这是一笔不必要的费用。如今,fork()
并没有复制记忆;它只是设置为“写入时复制”,因此fork()
+ exec()
与vfork()
+ exec()
一样有效。
clone()
是fork()
使用的系统调用。使用一些参数,它会创建一个新进程,与其他参数一起创建一个线程。它们之间的区别只是共享或不共享哪些数据结构(内存空间,处理器状态,堆栈,PID,打开文件等)。
答案 1 :(得分:73)
execve()
将当前可执行映像替换为从可执行文件加载的另一个可执行映像。fork()
创建了一个子进程。vfork()
是fork()
的历史优化版本,旨在execve()
之后直接调用fork()
时使用。事实证明它在非MMU系统中运行良好(fork()
无法以高效的方式工作)以及fork()
具有巨大内存占用的进程运行一些小程序(想想Java {{1} }})。 POSIX已对Runtime.exec()
进行了标准化,以取代posix_spawn()
的后两种更现代用途。vfork()
相当于posix_spawn()
,并允许一些fd杂乱。它应该替换fork()/execve()
,主要用于非MMU平台。fork()/execve()
创建一个新主题。pthread_create()
是特定于Linux的调用,可用于实现从clone()
到fork()
的任何内容。它给了很多控制权。灵感来自pthread_create()
。rfork()
是针对Plan-9的特定呼叫。它应该是一个通用调用,允许在完整进程和线程之间进行多度共享。答案 2 :(得分:39)
fork()
- 创建一个新的子进程,它是父进程的完整副本。子进程和父进程使用不同的虚拟地址空间,最初由相同的内存页填充。然后,当两个进程都被执行时,虚拟地址空间开始变得越来越不同,因为操作系统对这两个进程中的任何一个进行的内存页面执行惰性复制,并分配修改后的页面的独立副本。每个进程的内存。这种技术称为写时复制(COW)。vfork()
- 创建一个新的子进程,这是一个快速的"父进程的副本。与系统调用fork()
相反,子进程和父进程共享相同的虚拟地址空间。注意!使用相同的虚拟地址空间,父和子都使用相同的堆栈,堆栈指针和指令指针,如经典fork()
的情况!为防止使用相同堆栈的父级和子级之间的不必要干扰,父进程的执行将被冻结,直到子进程调用exec()
(创建新的虚拟地址空间并转换到其他堆栈)或{ {1}}(终止进程执行)。 _exit()
是vfork()
for" fork-and-exec"的优化模型。它可以比fork()
快4-5倍,因为与fork()
不同(即使COW保留在脑海中),fork()
系统调用的实现也不包括创建一个新的地址空间(分配和设置新的页面目录)。vfork()
- 创建一个新的子进程。此系统调用的各种参数,指定必须将父进程的哪些部分复制到子进程以及哪些部分将在它们之间共享。因此,此系统调用可用于创建各种执行实体,从线程开始并通过完全独立的进程完成。实际上,clone()
系统调用是用于实现clone()
以及pthread_create()
系统调用的所有系列的基础。fork()
- 重置进程的所有内存,加载和解析指定的可执行二进制文件,设置新堆栈并将控制权传递给加载的可执行文件的入口点。此系统调用永远不会将控制权返回给调用者,并用于将新程序加载到现有进程。这个带有exec()
系统调用的系统调用形成了一个称为" fork-and-exec"的经典UNIX进程管理模型。答案 3 :(得分:6)
fork(),vfork()和clone()都调用do_fork()来完成实际的工作,但是使用不同的参数。
asmlinkage int sys_fork(struct pt_regs regs)
{
return do_fork(SIGCHLD, regs.esp, ®s, 0);
}
asmlinkage int sys_clone(struct pt_regs regs)
{
unsigned long clone_flags;
unsigned long newsp;
clone_flags = regs.ebx;
newsp = regs.ecx;
if (!newsp)
newsp = regs.esp;
return do_fork(clone_flags, newsp, ®s, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0);
}
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */
#define CLONE_VM 0x00000100 /* set if VM shared between processes */
SIGCHLD means the child should send this signal to its father when exit.
对于fork,child和father有独立的VM页面表,但由于效率,fork不会真正复制任何页面,它只是将所有可写页面设置为只读子进程。因此,当子进程想要在该页面上写入内容时,会发生页面异常,并且内核将使用写入权限分配从旧页面克隆的新页面。这就是所谓的“写作时复制”。
对于vfork来说,虚拟记忆完全是由孩子和父亲 - 正因为如此,父亲和孩子不能同时醒来,因为他们会相互影响。所以父亲将在“do_fork()”结束时睡觉,并在孩子调用exit()或execve()时唤醒,因为它将拥有新的页面表。这是父亲睡觉的代码(在do_fork()中)。
if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;
这是代码(在exit()和execve()调用的mm_release()中唤醒父亲的代码。
up(tsk->p_opptr->vfork_sem);
对于sys_clone(),它更灵活,因为您可以输入任何clone_flags。所以pthread_create()用许多clone_flags调用这个系统调用:
int clone_flags =(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);
总结:fork(),vfork()和clone()将使用父进程创建具有不同共享资源的子进程。我们还可以说vfork()和clone()可以创建线程(实际上它们是进程,因为它们具有独立的task_struct),因为它们与父进程共享VM页表。
答案 4 :(得分:3)
Vfork()行为在下面的程序中有更详细的说明。
shashi@linuxtechi ~}$ cat vfork_advanced.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int n =10;
pid_t pid = vfork(); //creating the child process
if (pid == 0) //if this is a chile process
{
printf("Child process started\n");
}
else//parent process execution
{
printf("Now i am coming back to parent process\n");
}
printf("value of n: %d \n",n); //sample printing to check "n" value
return 0;
}
shashi@linuxtechi ~}$ cc vfork_advanced.c
shashi@linuxtechi ~}$ ./a.out
Child process started
value of n: 10
Now i am coming back to parent process
value of n: 594325573
a.out: cxa_atexit.c:100: __new_exitfn: Assertion `l != NULL' failed.
Aborted
注意:同样,如果您观察到vfork的结果未定义,预期第一次将“ n”的值打印为10。但是下一次在父进程中,它打印了一些垃圾值。
答案 5 :(得分:-4)