在这里插入图片描述

文章目录

    • 一、引言
    • 二、核心概念:进程 (Process)
      • 功能与作用
    • 三、C++ 多进程的实现方式
    • 四、核心函数详解
      • 1. `fork()` - 创建子进程
        • 函数原型
        • 功能说明
        • 返回值
        • 完整使用格式
      • 2. `wait()` 和 `waitpid()` - 等待子进程结束
        • 函数原型
        • 参数与返回值详解
      • 3. `exec` 系列函数 - 执行新程序
        • 函数族
        • 返回值
    • 五、完整示例
      • 示例一:基本的 `fork` 使用
      • 示例二:`fork` 与 `exec` 结合 (fork-exec 模型)
    • 六、关键注意事项
    • 七、总结


如果觉得本文对您有所帮助,点个赞和关注吧,谢谢!!!你的支持就是我持续更新的最大动力


一、引言

在现代计算中,为了充分利用多核处理器的计算能力并提高应用的稳定性,多进程编程是一种至关重要且应用广泛的技术。

二、核心概念:进程 (Process)

在操作系统中,一个 进程 (Process) 是一个正在执行的程序的实例。每个进程都拥有独立的内存空间,这包括代码段、数据段、堆和栈。这种内存隔离是进程最重要的特性之一。

功能与作用

多进程编程的 核心优势 在于:

  1. 稳定性与健壮性:由于进程间内存相互独立,一个进程的崩溃(如内存访问错误)通常不会影响到其他进程的正常运行。这使得多进程架构在需要高可靠性的服务中备受青睐。
  2. 资源隔离:操作系统为每个进程分配独立的资源(内存、文件描述符等),简化了资源管理,避免了复杂的同步问题。
  3. 利用多核CPU:操作系统可以轻易地将不同的进程调度到不同的CPU核心上并行执行,从而最大限度地利用硬件性能。

与多线程相比,多进程的主要区别在于内存模型。线程共享同一进程的内存空间,通信效率高但需要复杂的同步机制(如互斥锁、信号量)来避免数据竞争;而进程通信(IPC)需要借助操作系统提供的机制,相对开销更大,但模型更简单、更安全。

三、C++ 多进程的实现方式

C++ 标准库本身并未提供直接创建进程的API(不同于 thread,可直接调用创建线程)。因此,C++ 的多进程编程严重依赖于底层操作系统提供的接口。最主流的实现方式是使用 POSIX 标准定义的 fork() 系统调用,这在所有类Unix系统(Linux, macOS等)上都是通用的。

Windows系统使用另一套API(CreateProcess),其模型与fork有本质区别。本文将重点阐述POSIX标准的fork模型。

四、核心函数详解

1. fork() - 创建子进程

fork() 是在类Unix系统中创建新进程的唯一方式。它通过复制调用它的进程(父进程)来创建一个新的、几乎完全相同的子进程。

函数原型
#include <unistd.h>pid_t fork(void);
功能说明

调用 fork() 后,操作系统会创建一个新的子进程。子进程是父进程的一个副本,它拥有父进程内存空间的副本(采用写时复制 Copy-on-Write 技术以优化性能)、相同的文件描述符、相同的程序计数器(即子进程从fork()返回处开始执行)等。

返回值

fork() 的返回值是区分父子进程的关键,它有三种可能性:

  • 在父进程中:返回新创建的子进程的ID(一个正整数)。
  • 在子进程中:返回 0
  • 创建失败:返回 -1,并设置全局变量 errno
完整使用格式
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>// ...pid_t pid = fork();if (pid < 0) {// fork 失败,处理错误cerr << "fork failed!" << endl;exit(1);
} else if (pid == 0) {// 此代码块由子进程执行cout << "This is the child process, PID = " << getpid() << endl;// ... 执行子进程的任务 ...exit(0); // 子进程任务完成后必须退出
} else {// 此代码块由父进程执行cout << "This is the parent process, PID = " << getpid() << ", child PID = " << pid << endl;// ... 父进程可以继续执行自己的任务,或者等待子进程结束 ...
}

2. wait()waitpid() - 等待子进程结束

父进程通常需要等待子进程执行完毕,以回收其资源并获取其退出状态。否则,已终止但未被父进程回收的子进程将成为“僵尸进程”(Zombie Process),浪费系统资源。

函数原型
#include <sys/wait.h>pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
参数与返回值详解
  • wait(int *status):

    • 功能:阻塞当前进程(父进程),直到它的 任意一个 子进程结束。
    • status:一个整型指针,用于存储子进程的退出状态信息。如果不需要,可以传入nullptr
    • 返回值:成功时返回结束的子进程的ID;如果没有子进程或出错,则返回-1
  • waitpid(pid_t pid, int *status, int options):

    • 功能:提供了更灵活的等待方式。
    • pid:指定要等待的子进程ID。
      • > 0:等待指定ID的子进程。
      • -1:等待任意子进程(与wait相同)。
      • 0:等待与当前进程组ID相同的任何子进程。
    • status:同wait
    • options:控制waitpid的行为,最常用的是WNOHANG,它使waitpid变为非阻塞调用。如果没有子进程退出,它会立即返回0
    • 返回值:成功时返回结束的子进程ID;如果使用了WNOHANG且没有子进程退出,则返回0;出错时返回-1

3. exec 系列函数 - 执行新程序

fork() 创建的子进程执行的是与父进程相同的代码。如果我们希望子进程执行一个全新的程序,就需要使用 exec 系列函数。

exec 系列函数会用一个全新的程序替换当前进程的内存空间(包括代码、数据、堆栈),进程ID保持不变。一旦调用成功,原程序中 exec 调用之后的代码将永远不会被执行。

函数族

exec 不是一个函数,而是一族函数,它们的命名规则反映了其参数传递方式:

  • l (list): 参数以可变参数列表的形式给出,以NULL结尾。
  • v (vector): 参数以一个字符串数组(char*[])的形式给出。
  • p (path): 会在系统的PATH环境变量中搜索要执行的程序。
  • e (environment): 允许额外传递一个环境变量数组。

常用组合:

  • execl(const char *path, const char *arg, ...)
  • execlp(const char *file, const char *arg, ...)
  • execv(const char *path, char *const argv[])
  • execvp(const char *file, char *const argv[])
返回值

如果 exec 调用成功,它将不会返回。如果调用失败(例如程序不存在、没有权限),它会返回-1,并设置 errno

五、完整示例

示例一:基本的 fork 使用

这个例子展示了如何创建一个子进程,父子进程如何执行不同的代码路径,以及父进程如何等待子进程结束。

#include <iostream>
#include <string>
#include <unistd.h>    // for fork, getpid, getppid
#include <sys/wait.h>  // for waitusing namespace std;int main() {cout << "Main process started, PID: " << getpid() << endl;pid_t pid = fork();if (pid < 0) {// Errorcerr << "Fork failed. Exiting." << endl;return 1;} else if (pid == 0) {// Child Processcout << "--> Child process started." << endl;cout << "--> My PID is " << getpid() << ", my parent's PID is " << getppid() << "." << endl;// 模拟子进程执行任务sleep(2);cout << "--> Child process finished." << endl;exit(0); // 子进程正常退出} else {// Parent Processcout << "Parent process continues." << endl;cout << "Created a child with PID: " << pid << endl;cout << "Parent is waiting for the child to finish..." << endl;int status;wait(&status); // 阻塞等待子进程结束if (WIFEXITED(status)) {cout << "Child process exited with status: " << WEXITSTATUS(status) << endl;} else {cout << "Child process terminated abnormally." << endl;}cout << "Parent process finished." << endl;}return 0;
}

示例二:forkexec 结合 (fork-exec 模型)

这个例子展示了多进程编程最经典的用法:父进程创建一个子进程,然后子进程通过exec执行一个全新的程序(例如系统的 ls 命令)。

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>using namespace std;int main() {cout << "Parent process (PID: " << getpid() << ") is starting..." << endl;pid_t pid = fork();if (pid < 0) {cerr << "Fork failed." << endl;return 1;} else if (pid == 0) {// Child Processcout << "--> Child (PID: " << getpid() << ") is about to run 'ls -l /'" << endl;// 第一个参数是要执行的程序名// 后续参数是程序的命令行参数,最后一个必须是 nullptrexeclp("ls", "ls", "-l", "/", nullptr);// 如果 execlp 成功,下面的代码将不会被执行// 如果执行到这里,说明 execlp 失败了cerr << "--> execlp failed!" << endl;exit(1); // 必须退出,否则子进程会继续执行父进程的代码} else {// Parent Processcout << "Parent is waiting for the command to complete..." << endl;wait(nullptr); // 等待子进程结束,这里不关心退出状态cout << "Child has finished. Parent is exiting." << endl;}return 0;
}

六、关键注意事项

  1. 绝不忘记 wait:父进程必须调用 waitwaitpid 来回收子进程资源,否则会产生僵尸进程。
  2. fork 后的资源处理fork 会复制文件描述符。这意味着父子进程可能同时操作同一个文件句柄,可能导致输出混乱或数据损坏,需要小心处理或关闭不需要的描述符。
  3. 写时复制 (Copy-on-Write):理解 fork 的 COW 机制。父子进程共享物理内存页,直到其中一方尝试写入,这时内核才会为写入方复制一份私有页面。这使得 fork 的开销远比想象中要小。
  4. 进程间通信 (IPC):由于内存隔离,进程间通信必须通过显式机制,如管道 (Pipe)、共享内存 (Shared Memory)、消息队列 (Message Queue) 或套接字 (Socket)。选择合适的IPC机制是多进程设计的关键。
  5. 信号处理:在多进程环境中,信号处理变得更加复杂。需要明确哪个进程应该处理哪个信号,并妥善设计信号处理函数。

七、总结

C++ 多进程编程是一种强大而基础的技术,它通过利用操作系统提供的 forkwaitexec 等原生接口,实现了程序的并行化和模块化。其核心优势在于无与伦比的稳定性和资源隔离性。虽然带来了进程间通信的开销,但在许多高可靠、高并发的系统设计中,这种代价是完全值得的。熟练掌握 fork-exec 模型,并正确处理进程的生命周期管理,是每一位资深C++系统程序员必备的技能。

如果觉得本文对您有所帮助,点个赞和关注吧,谢谢!!!你的支持就是我持续更新的最大动力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95964.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95964.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/95964.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一周学会Matplotlib3 Python 数据可视化-绘制面积图(Area)

锋哥原创的Matplotlib3 Python数据可视化视频教程&#xff1a; 2026版 Matplotlib3 Python 数据可视化 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 课程介绍 本课程讲解利用python进行数据可视化 科研绘图-Matplotlib&#xff0c;学习Matplotlib图形参数基本设置&…

北京JAVA基础面试30天打卡11

1.索引创建注意事项 适合的场景 1.频繁使用where语句查询的字段 2.关联字段需要建立索 3.如果不创建索引&#xff0c;那么在连接的过程中&#xff0c;每个值都会进行一次全表扫描 4.分组和排序字段可以建立索引因为索引天生就是有序的&#xff0c;在分组和排序时优势不言而喻 5…

vscode无法检测到typescript环境解决办法

有一个vitereacttypescript项目&#xff0c;在工作电脑上一切正常。但是&#xff0c;在我家里的电脑运行&#xff0c;始终无法检测到typescript环境。即使出现错误的ts语法&#xff0c;也不会有报错提示&#xff0c;效果如下&#xff1a;我故意将一个string类型&#xff0c;传入…

【MCP开发】Nodejs+Typescript+pnpm+Studio搭建Mcp服务

MCP服务支持两种协议&#xff0c;Studio和SSE/HTTP&#xff0c;目前官方提供的SDK有各种语言。 开发方式有以下几种&#xff1a; 编程语言MCP命令协议发布方式PythonuvxSTUDIOpypiPython远程调用SSE服务器部署NodejspnpmSTUDIOpnpmNodejs远程调用SSE服务器部署… 一、初始化项…

vscode使用keil5出现变量跳转不了和搜索全局不了

vscode使用keil5出现变量跳转不了&#xff0c;或者未包含文件&#xff0c;或者未全局检索&#xff1b; 参考如下文章后还会出现&#xff1b; 为什么vscode搜索栏只搜索已经打开的文件_vscode全局搜索只能搜当前文件-CSDN博客 在机缘巧合之下发现如下解决方式&#xff1a; 下载…

命名空间——网络(net)

命名空间——网络&#xff08;net&#xff09; 一、网络命名空间&#xff1a;每个都是独立的“网络房间” 想象你的电脑是一栋大楼&#xff0c;每个网络命名空间就是大楼里的一个“独立房间”&#xff1a; 每个房间里有自己的“网线接口”&#xff08;网卡&#xff09;、“门牌…

一文读懂16英寸笔记本的实际尺寸与最佳应用场景

当您搜索"16寸笔记本电脑长宽"时&#xff0c;内心真正在问的是什么&#xff1f;是背包能否容纳&#xff1f;桌面空间是否足够&#xff1f;还是期待屏幕尺寸与便携性的完美平衡&#xff1f;这个看似简单的尺寸数字背后&#xff0c;凝结着计算机制造商对用户体验的深刻…

Android Studio中创建Git分支

做一些Android项目时&#xff0c;有时候想要做一些实验性的修改&#xff0c;这个实验可能需要很多步骤&#xff0c;所以不是一时半会能完成的&#xff0c;这就需要在实验的过程中不断修改代码&#xff0c;且要提交代码&#xff0c;方便回滚或比较差异&#xff0c;但是既然是实验…

内存可见性和伪共享问题

文章目录什么是内存可见性问题为什么会出现可见性问题解决可见性问题的方法1. 使用volatile关键字2. 使用synchronized3. 使用java.util.concurrent包下的原子类什么是伪共享问题CPU缓存行伪共享的危害解决伪共享的方法1. 缓存行填充2. 使用Contended注解&#xff08;JDK 8&…

Spring MVC 九大组件源码深度剖析(三):ThemeResolver - 动态换肤的奥秘

文章目录一、主题机制的核心价值二、核心接口设计三、四大实现类源码解析1. FixedThemeResolver&#xff08;固定主题策略&#xff09;2. CookieThemeResolver&#xff08;Cookie存储策略&#xff09;3. SessionThemeResolver&#xff08;Session存储策略&#xff09;4. Abstra…

一、Docker本地安装

((这里引用知乎上大佬的说法&#xff1a;https://www.zhihu.com/question/48174633 服务器虚拟化解决的核心问题是资源调配&#xff0c;而容器解决的核心问题是应用开发、测试和部署。 一、参考帖子 Ubuntu 的 |Docker 文档 【docker】ubuntu完全卸载docker及再次安装_ubuntu…

LeetCode 分类刷题:2962. 统计最大元素出现至少 K 次的子数组

题目给你一个整数数组 nums 和一个 正整数 k 。请你统计有多少满足 「 nums 中的 最大 元素」至少出现 k 次的子数组&#xff0c;并返回满足这一条件的子数组的数目。子数组是数组中的一个连续元素序列。示例 1&#xff1a;输入&#xff1a;nums [1,3,2,3,3], k 2 输出&#…

10分钟掌握swift

整理一个 10分钟掌握 Swift 的精华指南&#xff0c;用一个 Demo 串联 Swift 的核心语法、数据结构、函数、类/结构体和闭包&#xff0c;让你快速入门。1️⃣ 基础语法与变量import Foundation // 引入基础库// 变量和常量 var name: String "Alice" // 可变 let…

【完整源码+数据集+部署教程】食品分类与实例分割系统源码和数据集:改进yolo11-AggregatedAttention

背景意义 研究背景与意义 随着全球食品产业的快速发展&#xff0c;食品安全和质量控制日益成为社会关注的焦点。食品分类与实例分割技术的应用&#xff0c;能够有效提升食品识别的准确性和效率&#xff0c;为食品监管、营养分析以及智能餐饮等领域提供重要支持。传统的食品识别…

C# 中的N+1问题

目录 含义 影响 避免方法 1. 立即加载&#xff08;Eager Loading&#xff09; 2. 显式加载&#xff08;Explicit Loading&#xff09; 3. 投影&#xff08;Projection&#xff09; 4. 批处理查询 5. 禁用延迟加载 含义 N1 问题 是 ORM&#xff08;对象关系映射&#x…

国内多光谱相机做得好的厂家有哪些?-多光谱相机品牌厂家

多光谱相机是一种能够同时捕捉多个特定波段的光谱信息&#xff0c;这些波段覆盖可见光、近红外以及短波红外等区域。广泛应用于遥感、农业、环境监测、工业检测、安防等领域。近年来&#xff0c;我国在多光谱技术领域取得了显著进步&#xff0c;涌现出一批技术实力强、产品性能…

如何用外部电脑访问本地网页?

之前本来说用内网穿透工具来查看完成这个工具&#xff0c;结果感觉各种不符合心意&#xff0c;突然发现有更简单的方法。如果想让两台电脑在 同一局域网 内都能访问运行在 http://localhost:5174/ 上的项目&#xff0c;而不需要使用内网穿透工具&#xff0c;可以通过以下方法实…

PromptPilot — AI 自动化任务的下一个环节

作者:陈大鱼头 github:https://github.com/KRISACHAN 邮箱:chenjinwen77@gmail.com PromptPilot 体验地址:https://promptpilot.volcengine.com/ 前言 如果大家有关注 AI 相关新闻的话,一定会知道在 2025 年 6 月 11 日火山引擎 FORCE 原动力大会上,豆包大模型 1.6 系列…

[Responsive theme color] 动态更新 | CSS变量+JS操控 | 移动端-汉堡菜单 | 实现平滑滚动

第3章&#xff1a;CSS变量操控 欢迎回来&#x1f43b;‍❄️ 通过前两章&#xff0c;我们掌握了 动态主题定制 的交互逻辑&#xff0c;以及 色彩工具函数 如何实现色值格式转换。 本章将揭示技术拼图的最后一块&#xff1a;CSS变量动态操控&#xff0c;解析JavaScript如何实…

数学建模 15 逻辑回归与随机森林

逻辑回归&#xff08;用于分类&#xff09;用途&#xff1a;通过已有数据&#xff0c;计算出线性方程的参数w后&#xff0c;可以用于预测某一个物品属于某一类的概率&#xff0c;[0,1];求解思想&#xff1a;逻辑回归通过最大似然估计&#xff08;Maximum Likelihood Estimation…