当前位置: 首页 > news >正文

深入解析Linux进程创建与fork机制

目录

一、fork函数初识

二、fork函数返回值

思考:

1. fork函数为何给子进程返回0,而给父进程返回子进程的PID?

2. 关于fork函数为何有两个返回值这个问题

三、写时复制机制

写时拷贝(Copy-On-Write)机制解析

1. 必要性:为什么需要写时拷贝?

2. 延迟拷贝的优势:为何不立即拷贝?

3. 代码段的写时拷贝适用性

关键结论

四、fork函数的常规用法

五、fork调用失败的原因:


一、fork函数初识

        在Linux系统中,fork函数是一个关键的系统调用,它通过复制现有进程来创建新进程。被创建的进程称为子进程,而原始进程则称为父进程。

#include <unistd.h>
pid_t fork(void);

函数返回值:

  • 子进程返回0
  • 父进程返回子进程的PID
  • 出错时返回-1

当进程调用fork函数时,内核会执行以下操作:

  1. 为子进程分配新的内存空间和内核数据结构
  2. 将父进程的部分数据结构内容复制到子进程
  3. 将子进程添加到系统进程列表
  4. fork函数返回,由调度器开始进行进程调度

        当进程调用fork()后,会生成两个二进制代码完全相同的子进程。这两个进程会从相同的执行点继续运行,但随后各自进入独立的执行流程。请看以下示例代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main(void) 
{pid_t pid;printf("Before: pid is %d\n", getpid());if ((pid = fork()) == -1) {perror("fork()");exit(1);}printf("After: pid is %d, fork return %d\n", getpid(), pid);sleep(1);return 0;
}

运行结果:

        输出内容分为三行:一行"Before"和两行"After"。进程2619首先打印"Before"消息,随后又打印了一条"After"消息。另一条"After"消息则是由进程2620打印的。值得注意的是,进程2620并未打印"Before"消息。为什么呢?如下图所示:

        在fork调用之前,父进程独立运行;调用之后,父进程和子进程将各自执行。需要注意的是,fork之后哪个进程先执行完毕,完全取决于系统调度器的安排。


二、fork函数返回值

  • 子进程返回0
  • 父进程返回子进程的PID
  • 出错时返回-1

思考:

1. fork函数为何给子进程返回0,而给父进程返回子进程的PID?

        这种设计源于进程间的关系特性:一个父进程可以创建多个子进程,但每个子进程只能有一个父进程。对子进程而言,它无需识别父进程;而对父进程来说,必须明确每个子进程的标识。父进程需要获取子进程的PID才能有效分配和管理任务。

2. 关于fork函数为何有两个返回值这个问题

当父进程调用fork函数时,系统会执行一系列创建子进程的操作:

  1. 创建子进程的进程控制块
  2. 建立子进程的进程地址空间
  3. 生成子进程对应的页表完成这些步骤后,操作系统会将子进程的进程控制块加入系统进程列表,此时子进程创建完成。

        换句话说,当fork函数执行return语句时,子进程的创建已经完成。因此,后续的return语句不仅由父进程执行,子进程也会执行同样的操作,这就解释了为何fork函数会返回两个值。


三、写时复制机制

        子进程刚创建时,与父进程共享相同的内存数据段和代码段,两者的页表都指向同一块物理内存区域。只有当父进程或子进程尝试修改数据时,系统才会执行写时复制操作,将原始数据复制一份供修改使用。

具体原理如下图所示:

        得益于写时拷贝技术,父子进程得以完全分离,从而确保了进程的独立性。写时拷贝是一种延迟申请技术,可以有效提高系统内存的使用效率。

这种在需要进行数据修改时再进行拷贝的技术,称为写时拷贝技术。

写时拷贝(Copy-On-Write)机制解析

1. 必要性:为什么需要写时拷贝?

  • 进程独立性要求
    多进程环境下,操作系统需确保各进程资源独占性。写时拷贝通过延迟拷贝策略,保证子进程修改数据时才会复制父进程资源,避免进程间数据干扰。

  • 性能优化
    直接拷贝父进程全部数据会带来显著开销(如内存占用、CPU复制时间),而多数情况下子进程可能仅读取数据或使用部分资源。

2. 延迟拷贝的优势:为何不立即拷贝?

  • 资源利用率

    • 避免冗余拷贝:子进程可能仅访问父进程部分数据(如只读代码段),立即全量拷贝会导致内存浪费。

    • 按需分配:仅在子进程尝试修改数据时触发拷贝,减少无效的内存占用(例如fork()后接exec()的场景无需拷贝父进程数据)。

  • 效率提升
    现代操作系统通过页表映射共享父进程资源,写时拷贝将实际拷贝操作推迟到最后一刻,显著降低进程创建开销。

3. 代码段的写时拷贝适用性

  • 常规情况(90%以上)
    代码段通常是只读的,父子进程可共享同一物理内存页,无需触发拷贝。

  • 例外场景

    • 进程替换(如exec():新程序加载会覆盖原代码段,此时需重新分配内存,本质上仍遵循"修改时拷贝"逻辑。

    • 自修改代码:极少见情况下程序动态修改代码段内容,会触发写时拷贝机制。

关键结论

写时拷贝通过共享只读、延迟写入的策略,在保证进程独立性的同时,最大化内存和计算资源的利用率,是操作系统优化进程创建的核心机制。


四、fork函数的常规用法

  • 父进程需要复制自身,使父子进程可以执行不同的代码段。例如,父进程监听客户端请求,创建子进程来处理具体请求。
  • 进程需要执行新程序。例如子进程在fork返回后调用exec函数。

五、fork调用失败的原因:

fork函数创建子进程也可能会失败,有以下两种情况:

  1. 系统中有太多的进程,内存空间不足,子进程创建失败。
  2. 实际用户的进程数超过了限制,子进程创建失败。
http://www.dtcms.com/a/285130.html

相关文章:

  • Dify:在MacOS系统下Dify的本地部署与使用
  • Android Jetpack 系列(四)DataStore 全面解析与实践
  • RSTP:快速收敛的生成树技术
  • 深入解析SVM:从对偶问题求解到核函数理论
  • [3-03-01].第61节:开发应用 - Seata中的SAGA模式
  • 防止电脑息屏 html
  • Bell不等式赋能机器学习:微算法科技MLGO一种基于量子纠缠的监督量子分类器训练算法技术
  • Java 8 jdk1.8下载及安装教程和环境变量配置
  • 电子电路中的电压符号命名约定
  • 【前端如何利用 localStorage 存储 Token 及跨域问题解决方案】
  • Python网络爬虫之requests库
  • ISL8121IRZ-T 瑞萨电子Renesas高效双路同步降压控制器 【5G基站、AI服务器】专用
  • LIN通信驱动代码开发注意事项
  • 多重共线性Multicollinearity
  • 复合机器人在生物制药实验室上下料搬运案例
  • LeetCode热题100【第二天】
  • 91套商业策划创业融资计划书PPT模版
  • AppTrace:重新定义免填邀请码,解锁用户裂变新高度
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | PasswordGenerator(密码生成器)
  • 三、了解OpenCV的数据类型
  • 高效去除字符串末尾重复单元的 KMP 前缀函数优化算法实现
  • VR 远程系统的沉浸式协作体验​
  • SpringBoot 使用MyBatisPlus
  • 在windows平台上基于OpenHarmony sdk编译三方库并暴露给ArkTS使用(详细)
  • VSCODE常规设置
  • No catalog entry ‘md5‘ was found for catalog ‘default‘. 的简单解决方法
  • 学习软件测试的第十八天
  • 一款基于PHP开发的不良事件上报系统源码,适用于医院安全管理。系统提供10类事件类别、50余种表单,支持在线填报、匿名上报及紧急报告。
  • 前端防复制实战指南:5 种主流方案效果对比与实现
  • Ubuntu20.04上安装Anaconda