C++ 多進程編程深度解析【C++進階每日一學】

在這里插入圖片描述

文章目錄

    • 一、引言
    • 二、核心概念:進程 (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…