vfork(),fork()与clone()的区别

本文详细介绍了Linux系统中进程创建的两个阶段:创建和执行。解释了fork()与clone()的区别,以及vfork()如何提高系统效率。同时,还介绍了与进程创建相关的系统调用如execve()的作用。

       Linux将创建进程和执行所创建的进程分为2个阶段。

       第一个阶段是创建。父进程首先复制子进程,所复制出来的子进程拥有自己的任务结构体和系统堆栈,除此之外所有资源都与父进程共享。Linux提供两种方式复制子进程:一个是fork(),另外一个是clone()。

      fork()函数复制时将父进程的所以资源都通过复制数据结构进行了复制,然后传递给子进程,所以fork()函数不带参数;clone()函数则是将部分父进程的资源的数据结构进行复制,复制哪些资源是可选择的,这个可以通过参数设定,所以clone()函数带参数,没有复制的资源可以通过指针共享给子进程。

       Clone()函数的声明如下:int clone(int (*fn)(void *), void *child_stack, int flags, void *arg)

         fn为函数指针,此指针指向一个函数体,即想要创建进程的静态程序;

         child_stack为给子进程分配系统堆栈的指针;

         arg就是传给子进程的参数;

         flags为要复制资源的标志:

               CLONE_PARENT 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”  

               CLONE_FS 子进程与父进程共享相同的文件系统,包括root、当前目录、umask  

               CLONE_FILES 子进程与父进程共享相同的文件描述符(file descriptor)表 CLONE_NEWNS 在新的namespace启动子进程,namespace描述了进程的文件hierarchy  

               CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表 CLONE_PTRACE 若父进程被trace,子进程也被trace 

              CLONE_VFORK 父进程被挂起,直至子进程释放虚拟内存资源 CLONE_VM 子进程与父进程运行于相同的内存空间 CLONE_PID 子进程在创建时PID与父进程一致

               CLONE_THREAD Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群

         fork()可以看出是完全版的clone(),而clone()克隆的只是fork()的一部分。   

         为了提高系统的效率,后来的Linux设计者又增加了一个系统调用vfork()。vfork()所创建的不是进程而是线程,它所复制的是除了任务结构体和系统堆栈之外的所有资源的数据结构,而任务结构体和系统堆栈是与父进程共用的。

         第二个阶段就是所创建进程的执行。子进程创建完后一般都会走自己的路。Linux为了子进程能做自己的事特意提供了一个系统调用execve(),用以执行一个可执行程序的映像,这个映像以文件形式存在(这句话其实就是说用execve()可以调用一个可执行程序,因为这个可执行程序就在磁盘上,所有是以文件形式存在的,而映像是说已经编译链接好了的,只要调入内存就可以执行,一般为二进制文件)。vfork创建的子进程要先于父进程执行,子进程执行时,父进程处于挂起状态,子进程执行完,唤醒父进程。

### Linux 中 `fork`、`vfork` 和 `clone` 的区别 #### 一、基本概念 在 Linux 系统中,`fork`、`vfork` 和 `clone` 是用于创建新进程的重要系统调用。它们各自有不同的实现方式和适用场景。 - **`fork`**: 创建一个新的子进程,该子进程几乎完全复制了父进程的状态[^1]。 - **`vfork`**: 类似于 `fork`,但它不会复制父进程的地址空间,而是让父子进程共享相同的内存区域。 - **`clone`**: 提供了一个更灵活的方式来创建线程或进程,允许开发者指定哪些资源应该被共享[^3]。 --- #### 二、具体差异分析 ##### 1. 地址空间处理 - **`fork`** 当通过 `fork` 调用创建子进程时,操作系统会为子进程分配新的虚拟地址空间,并将父进程的整个内存映像(包括堆、栈、代码段等)拷贝到这个新地址空间中[^2]。这种方式虽然简单直观,但在某些情况下可能导致性能开销较大,尤其是在父进程占用大量资源的情况下。 - **`vfork`** 使用 `vfork` 创建子进程时,不立即分配独立的地址空间。相反,它会让子进程暂时借用父进程的地址空间直到调用了特定函数(如 `_exit` 或 `exec`),从而减少了不必要的数据副本操作。需要注意的是,在此期间任何对共享内存区修改都会影响到原始父进程状态。 - **`clone`** `clone` 函数提供了高度可配置选项来决定如何构建目标实体及其行为模式。例如可以通过设置标志位控制是否共享文件描述符表、信号处理器列表以及内存管理单元等等。因此相比前两者更加复杂但也更为强大。 ##### 2. 性能考量 由于上述机制上的不同之处,“重量级”的传统 fork 方法可能并不适合所有应用场景;而轻量化的替代方案则各有千秋: | 特性/方法 | Fork | Vfork | Clone | |------------------|-------------------------|-----------------------|----------------------| | 是否复制内存 | 完全复制 | 不复制 | 可选 | | 子进程独立程度 | 高 | 极低 | 自定义 | | 开销 | 较大 | 很小 | 视配置而定 | 从表格可以看出如果仅需短暂存在并执行单一任务的小型作业,则优先考虑采用效率更高的 vfork 或者定制化能力更强的 clone 来减少不必要的计算负担. ##### 3. 实现细节对比 以下是三种调用内部实现的主要流程概述: - 对于标准 fork() : ```c long sys_fork(void){ struct task_struct *p; int retval; p = dup_task_struct(current); if (!p) return -ENOMEM; // 复制各种上下文信息... wake_up_new_task(p); return nr_threads++; } ``` 这里可以看到除了简单的结构体克隆外还有许多额外步骤涉及到了资源初始化等问题. 至于 vfork(),其核心逻辑实际上是在 do_fork() 内部特别标记出来的: ```c if ((flags & CLONE_VFORK) && !(flags & __WCLONE)){ ... } ``` 最后关于 clone(): ```c retval = -EINVAL; if (!(flags & (CLONE_NEWNS|CLONE_FS))) goto bad_fork_free_pid; // 更多复杂的参数解析... return kernel_clone(flags, newsp, stack_size, parent_tidptr, child_tidptr); ``` 显然它的灵活性体现在众多 flag 参数上,这使得它可以模拟出其他两种甚至更多变种的行为特性. --- ### 结论 综上所述,fork,vforkclone 各自适应不同的需求环境:前者适用于一般情况下的常规分叉动作;中间那个更适合那些希望快速启动临时辅助程序而又不想浪费太多时间做准备工作的场合;后者则是现代多线程编程不可或缺的基础工具之一. ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值