fork()、vfork()、exec() 和 clone() 之间的区别

2024-10-14 08:40:00
admin
原创
263
摘要:问题描述:我一直在 Google 上寻找这四个之间的区别,我预计会有大量这方面的信息,但实际上并没有对这四个调用进行任何实质性的比较。我开始尝试编写一种基本的一目了然的方法来查看这些系统调用之间的差异,以下是我得到的。所有这些信息都正确吗?我是否遗漏了任何重要信息?Fork:fork 调用基本上是对当前进程的...

问题描述:

我一直在 Google 上寻找这四个之间的区别,我预计会有大量这方面的信息,但实际上并没有对这四个调用进行任何实质性的比较。

我开始尝试编写一种基本的一目了然的方法来查看这些系统调用之间的差异,以下是我得到的。所有这些信息都正确吗?我是否遗漏了任何重要信息?

Fork:fork 调用基本上是对当前进程的复制,几乎所有方面都相同(并非所有内容都被复制,例如,某些实现中的资源限制,但其想法是创建尽可能接近的副本)。

新进程(子进程)获得不同的进程 ID(PID),并将旧进程(父进程)的 PI​​D 作为其父进程 PID(PPID)。由于这两个进程现在运行的代码完全相同,因此它们可以通过 fork 的返回代码来区分哪个是哪个 - 子进程获得 0,父进程获得子进程的 PID。当然,这一切都是假设 fork 调用有效 - 如果不成功,则不会创建子进程,父进程会获得错误代码。

Vfork`vfork():和之间的基本区别fork()在于,当使用 创建新进程时vfork(),父进程会暂时挂起,而子进程可能会借用父进程的地址空间。这种奇怪的状态会一直持续到子进程退出或调用execve()`,此时父进程将继续运行。

这意味着 的子进程vfork()必须小心谨慎,避免意外修改父进程的变量。具体来说,子进程不得从包含vfork()调用 的函数返回,也不得调用exit()(如果需要退出,则应使用_exit();实际上,对于普通 的子进程也是如此fork())。

Exec:exec 调用是一种基本上用新程序替换整个当前进程的方法。它将程序加载到当前进程空间并从入口点运行。exec()用函数指向的可执行文件替换当前进程。除非出现错误,否则控制权永远不会返回到原始程序exec()

Cloneclone(),作为fork(),创建一个新进程。与不同fork(),这些调用允许子进程与调用进程共享其部分执行上下文,例如内存空间、文件描述符表和信号处理程序表。

当使用 创建子进程时clone(),它会执行函数应用程序fn(arg)(这与 不同fork(),后者在子进程中从原始调用点继续执行fork()。)fn参数是指向子进程在执行开始时调用的函数的指针。arg参数传递给fn函数。

当fn( arg )函数应用程序返回时,子进程终止。fn 返回的整数是子进程的退出代码。子进程也可以通过调用或在收到致命信号后明确终止。exit(2)

信息来自:

感谢您花时间阅读本文!:)


解决方案 1:

  • vfork()是一种过时的优化。在良好的内存管理出现之前,fork()会完整复制父级内存,因此成本非常高。由于在许多情况下, afork()后面跟着exec(),这会丢弃当前内存映射并创建新的内存映射,因此这是不必要的开销。如今,fork()不会复制内存;它只是设置为“写时复制”,因此fork()+与+exec()一样高效。vfork()`exec()`

  • clone()是所使用的系统调用fork()。使用某些参数,它会创建一个新进程,使用其他参数,它会创建一个线程。它们之间的区别仅在于哪些数据结构(内存空间、处理器状态、堆栈、PID、打开的文件等)是共享的,还是不共享。

解决方案 2:

  • execve()用从可执行文件加载的另一个可执行映像替换当前的可执行映像。

  • fork()创建一个子进程。

  • vfork()是 的历史优化版本fork(),旨在用于execve()之后直接调用的情况fork()。事实证明,它在非 MMU 系统(fork()无法高效工作)以及fork()运行占用大量内存的进程来运行一些小程序(比如 Java 的Runtime.exec())时运行良好。POSIX 已将 标准化,posix_spawn()以取代 的后两种更现代的用法vfork()

  • posix_spawn()相当于fork()/execve(),还允许在两者之间进行一些 fd 切换。它应该会取代fork()/execve(),主要用于非 MMU 平台。

  • pthread_create()创建一个新线程。

  • clone()是 Linux 特有的调用,可用于实现从fork()到 的任何操作pthread_create()。它提供了许多控制权。灵感来自rfork()

  • rfork()是 Plan-9 特有的调用。它应该是一个通用调用,允许在完整进程和线程之间进行多级共享。

解决方案 3:

  1. fork()- 创建一个新的子进程,它是父进程的完整副本。子进程和父进程使用不同的虚拟地址空间,最初由相同的内存页填充。然后,随着两个进程的执行,虚拟地址空间开始越来越不同,因为操作系统对这两个进程中任何一个正在写入的内存页执行惰性复制,并为每个进程分配已修改内存页的独立副本。这种技术称为写时复制 (COW)。

  2. vfork()- 创建一个新的子进程,它是父进程的“快速”副本。与系统调用相比fork(),子进程和父进程共享相同的虚拟地址空间。注意!使用相同的虚拟地址空间,父进程和子进程都使用相同的堆栈、堆栈指针和指令指针,就像经典的情况一样fork()!为了防止使用相同堆栈的父进程和子进程之间发生不必要的干扰,父进程的执行将冻结,直到子进程调用exec()(创建新的虚拟地址空间并转换到不同的堆栈)或_exit()(终止进程执行)。是针对“fork-and-exec”模型vfork()的优化。它的执行速度可以比快 4-5 倍,因为与(即使考虑到 COW)不同,系统调用的实现不包括创建新地址空间(分配和设置新的页面目录)。fork()`fork()fork()vfork()`

  3. clone()- 创建一个新的子进程。该系统调用的各种参数指定了父进程的哪些部分必须复制到子进程中,以及哪些部分将在它们之间共享。因此,该系统调用可用于创建各种执行实体,从线程开始,到完全独立的进程结束。事实上,clone()系统调用是用于实现pthread_create()所有系统调用系列的基础fork()

  4. exec()- 重置进程的所有内存,加载并解析指定的可执行二进制文件,设置新堆栈并将控制权传递给已加载可执行文件的入口点。此系统调用永远不会将控制权返回给调用者,而是用于将新程序加载到已经存在的进程中。此系统调用与fork()系统调用一起形成了一种经典的 UNIX 进程管理模型,称为“fork-and-exec”。

解决方案 4:

fork()、vfork() 和 clone() 都调用 do_fork() 来完成实际工作,但是参数不同。

asmlinkage int sys_fork(struct pt_regs regs)
{
    return do_fork(SIGCHLD, regs.esp, &regs, 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, &regs, 0);
}
asmlinkage int sys_vfork(struct pt_regs regs)
{
    return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 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 来说,子进程和父进程拥有独立的 VM 页表,但出于效率考虑,fork 不会真正复制任何页面,它只是将所有可写的页面设置为子进程的只读。因此,当子进程想要在该页面上写入内容时,会发生页面异常,内核将分配一个从旧页面克隆而来的新页面,并具有写入权限。这称为“写时复制”。

对于 vfork 来说,虚拟内存是分别由子进程和父进程使用的——正因为如此,父进程和子进程不能同时被唤醒,否则会互相影响。所以父进程会在“do_fork()”结束时休眠,当子进程调用 exit() 或 execve() 时被唤醒,然后它就会拥有新的页表。下面是父进程休眠的代码(在 do_fork() 中)。

if ((clone_flags & CLONE_VFORK) && (retval > 0))
down(&sem);
return retval;

这是唤醒父亲的代码(在 mm_release() 中,由 exit() 和 execve() 调用)。

up(tsk->p_opptr->vfork_sem);

对于 sys_clone(),它更灵活,因为您可以向其输入任何 clone_flags。因此 pthread_create() 使用许多 clone_flags 调用此系统调用:

int 克隆标志 = (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 页表。

解决方案 5:

在 fork() 中,根据 CPU 选择,子进程或父进程都会执行。但在 vfork() 中,子进程肯定会先执行。子进程终止后,父进程才会执行。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用