Linux 進程控制:全面深入剖析進程創建、終止、替換與等待

文章目錄

    • 引言
    • 一、進程創建:`fork()`系統調用的奧秘
      • 1.1 `fork()`的基本原理
      • 1.2 代碼示例與解讀
      • 1.3 寫時復制(COW)優化
    • 二、進程終止:`exit()`與`_exit()`的抉擇
      • 2.1 `exit()`和`_exit()`的區別
      • 2.2 代碼示例與分析
    • 三、進程替換:`exec()`函數族的魔法
      • 3.1 `exec()`函數族的概述
      • 3.2 代碼示例與執行過程
    • 四、進程等待:`wait()`與`waitpid()`的作用
      • 4.1 僵尸進程的危害
      • 4.2 `wait()`和`waitpid()`的功能
      • 4.3 代碼示例與關鍵函數解讀
    • 五、進程控制綜合應用:多進程任務調度
      • 5.1 代碼示例與工作流程
    • 六、進程控制進階技巧
      • 6.1 僵尸進程處理
      • 6.2 信號處理
      • 6.3 進程組與會話
      • 6.4 守護進程
    • 七、總結

在這里插入圖片描述

引言

在 Linux 操作系統的宏大舞臺上,進程猶如活躍的舞者,是程序執行的鮮活實例,更是資源分配的基本單元。對系統編程而言,精通進程控制技術就如同掌握了舞蹈的精髓,是實現高效、穩定系統的關鍵所在。本文將全方位、深入地解析 Linux 的進程控制機制,不僅會對fork()exec()exit()wait()等核心系統調用進行細致解讀,還會輔以大量詳細注釋的代碼示例,同時探討一些進階的進程控制技巧。

一、進程創建:fork()系統調用的奧秘

1.1 fork()的基本原理

fork()是 Linux 系統中用于創建新進程的核心系統調用,它的神奇之處在于能夠創建當前進程的一個副本,這個副本被稱為子進程。父子進程在創建之初共享代碼段,這意味著它們執行的是相同的程序代碼,但擁有獨立的數據段和堆棧,這保證了它們在運行過程中可以獨立地處理各自的數據。

1.2 代碼示例與解讀

#include <stdio.h>
#include <unistd.h>int main() {pid_t pid = fork();  // 創建子進程if (pid < 0) {perror("Fork failed");  // 錯誤處理return 1;} else if (pid == 0) {// 子進程代碼printf("Child PID: %d, Parent PID: %d\n", getpid(), getppid());} else {// 父進程代碼printf("Parent PID: %d, Child PID: %d\n", getpid(), pid);}return 0;
}

代碼解讀

  • fork()調用會返回兩次,這是其獨特之處。在父進程中,fork()返回子進程的進程 ID(PID);而在子進程中,fork()返回 0。通過判斷fork()的返回值,我們可以區分父子進程并執行不同的代碼邏輯。
  • getpid()函數用于獲取當前進程的 PID,getppid()函數用于獲取當前進程的父進程的 PID。這兩個函數在進程控制中非常實用,可以幫助我們跟蹤進程之間的關系。
  • 典型的輸出結果可能如下:
Parent PID: 1234, Child PID: 1235
Child PID: 1235, Parent PID: 1234

1.3 寫時復制(COW)優化

fork()使用了寫時復制(Copy-On-Write,COW)技術,這是一種重要的優化策略。在fork()創建子進程時,父子進程實際上共享物理內存頁,只有當其中一個進程試圖修改某個內存頁時,才會為該進程復制一份該內存頁。這種技術減少了內存的使用,提高了進程創建的效率。

二、進程終止:exit()_exit()的抉擇

2.1 exit()_exit()的區別

函數行為適用場景
exit()清理 I/O 緩沖區,執行atexit()注冊的函數,然后終止進程正常終止進程,需要進行資源清理和執行收尾工作
_exit()立即終止進程,不清理緩沖區,不執行atexit()注冊的函數子進程終止后避免重復清理,或者在需要立即終止進程的場景

2.2 代碼示例與分析

#include <stdlib.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {printf("Child exiting...");// exit(0);      // 會輸出字符串_exit(0);       // 可能不輸出字符串(無緩沖區刷新)} else {wait(NULL);     // 等待子進程結束}return 0;
}

代碼分析

  • 在子進程中,如果使用exit(0)exit()函數會先清理 I/O 緩沖區,將printf()輸出的字符串刷新到標準輸出,然后再終止進程。
  • 如果使用_exit(0),由于_exit()函數不會清理緩沖區,printf()輸出的字符串可能不會顯示在屏幕上。

三、進程替換:exec()函數族的魔法

3.1 exec()函數族的概述

exec()函數族用于用新的程序替換當前進程的映像,替換后,進程的 PID 保持不變,但執行的程序變成了新的程序。常用的exec()函數包括:

  • execl():通過參數列表傳遞新程序的參數。
  • execv():通過參數數組傳遞新程序的參數。
  • execvp():自動搜索PATH環境變量指定的路徑,查找新程序。

3.2 代碼示例與執行過程

#include <unistd.h>int main() {char *args[] = {"ls", "-l", "/tmp", NULL};pid_t pid = fork();if (pid == 0) {// 子進程替換為ls命令execvp("ls", args);  // 參數1:命令名,參數2:參數數組perror("execvp failed");  // 只有失敗時執行_exit(1);} else {wait(NULL);  // 等待子進程結束}return 0;
}

執行過程

  1. 父進程調用fork()創建子進程。
  2. 子進程調用execvp("ls", args)execvp()函數會在PATH環境變量指定的路徑中查找ls命令。
  3. 找到ls命令后,將子進程的映像替換為/bin/ls的映像,子進程開始執行ls -l /tmp命令。
  4. 子進程執行完ls命令后退出。
  5. 父進程調用wait(NULL)等待子進程結束,回收子進程的資源。

四、進程等待:wait()waitpid()的作用

4.1 僵尸進程的危害

在 Linux 系統中,如果子進程先于父進程結束,而父進程沒有及時回收子進程的資源,子進程就會變成僵尸進程。僵尸進程雖然已經終止,但它的進程描述符仍然存在于系統中,會占用系統資源。如果僵尸進程過多,會導致系統資源耗盡,影響系統的正常運行。

4.2 wait()waitpid()的功能

父進程可以通過wait()waitpid()函數回收子進程的資源,防止僵尸進程的產生。

4.3 代碼示例與關鍵函數解讀

#include <sys/wait.h>
#include <unistd.h>int main() {pid_t pid = fork();if (pid == 0) {sleep(2);       // 子進程休眠2秒_exit(42);      // 退出狀態碼42} else {int status;pid_t child_pid = waitpid(pid, &status, 0);  // 阻塞等待if (WIFEXITED(status)) {printf("Child %d exited with status: %d\n", child_pid, WEXITSTATUS(status));  // 輸出42}}return 0;
}

關鍵函數解讀

  • waitpid(pid, &status, options)
    

    • pid:指定要等待的子進程的 PID。如果pid為 - 1,表示等待任意子進程。
    • status:用于存儲子進程的退出狀態。
    • options:可以設置一些選項,如WNOHANG表示非阻塞等待。
  • 狀態宏:

    • WIFEXITED(status):用于判斷子進程是否正常退出。如果子進程正常退出,該宏返回真。
    • WEXITSTATUS(status):用于獲取子進程的退出碼。

五、進程控制綜合應用:多進程任務調度

5.1 代碼示例與工作流程

#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>int main() {for (int i = 0; i < 3; i++) {pid_t pid = fork();if (pid == 0) {// 子進程執行不同任務char *cmds[] = {"./task1", "./task2", "./task3"};execl(cmds[i], cmds[i], NULL);_exit(1);  // exec失敗時退出}}// 父進程等待所有子進程int status;while (wait(&status) > 0) {if (WIFEXITED(status)) {printf("Child exited with %d\n", WEXITSTATUS(status));}}return 0;
}

工作流程

  1. 父進程通過for循環創建 3 個子進程。
  2. 每個子進程使用execl()函數執行不同的任務(./task1./task2./task3)。
  3. 父進程使用while循環和wait(&status)函數等待所有子進程退出。
  4. 每當有子進程退出時,父進程使用WIFEXITED(status)WEXITSTATUS(status)宏判斷子進程是否正常退出,并獲取子進程的退出碼,然后打印出來。

六、進程控制進階技巧

6.1 僵尸進程處理

父進程必須調用wait()waitpid()函數回收子進程的資源,防止僵尸進程的產生。可以通過信號處理機制,使用SIGCHLD信號異步回收子進程,提高系統的效率。

6.2 信號處理

使用signal()sigaction()函數注冊SIGCHLD信號處理函數,當子進程結束時,系統會發送SIGCHLD信號給父進程,父進程在信號處理函數中調用waitpid()函數回收子進程的資源。

6.3 進程組與會話

setpgid()函數用于設置進程組 ID,setsid()函數用于創建新的會話。通過這兩個函數,可以控制進程之間的關系,實現進程的分組管理和會話管理。

6.4 守護進程

守護進程是在后臺運行的服務進程,不與任何終端關聯。可以通過雙重fork()的方式創建守護進程,使進程在后臺持續運行,為系統提供服務。

七、總結

通過深入理解 Linux 的進程控制原語,包括fork()exec()exit()wait()等系統調用,以及掌握一些進階的進程控制技巧,開發者能夠構建高效、穩定的 Linux 應用程序,實現進程管理、任務調度等高級功能。在實際開發中,要根據具體的需求選擇合適的進程控制方法,合理管理系統資源,確保系統的正常運行。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/90167.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/90167.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/90167.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PJSIP 中的 TCP 傳輸配置指南

PJSIP 支持通過 TCP 傳輸 SIP 消息&#xff0c;相比 UDP 提供了更可靠的傳輸機制。以下是關于在 PJSIP 中使用 TCP 的詳細指南。1. 創建 TCP 傳輸基本 TCP 傳輸配置cpjsua_transport_config tcp_cfg; pjsua_transport_config_default(&tcp_cfg); tcp_cfg.port 5060; // SI…

小菜狗的云計算之旅,今天學習MySQL數據庫基礎知識及操作

目錄 一、概述 數據庫概念 數據庫的類型 關系型數據庫模型 關系數據庫相關概念 二、安裝 1、mariadb安裝 2、mysql安裝 3、啟動并開機自啟 4、本地連接&#xff08;本地登錄&#xff09; 三、mysql數據庫配置與命令 yum安裝后生成的目錄 mysql服務器的啟動腳本 數…

為什么是直接在**原型(prototype)上**添加函數

這是一個非常經典、核心的 JavaScript 面向對象編程問題&#xff1a;> 為什么是直接在**原型&#xff08;prototype&#xff09;上**添加函數&#xff0c;而不是在類/構造函數內部直接添加&#xff1f;你提到的代碼中&#xff1a;javascript function TopSearchComponent() …

深入理解 classnames:React 動態類名管理的最佳實踐

在現代前端開發中&#xff0c;我們經常需要根據組件的狀態、屬性或用戶交互來動態切換 CSS 類名。雖然 JavaScript 提供了多種方式來處理字符串拼接&#xff0c;但隨著應用復雜性的增加&#xff0c;傳統的類名管理方式很快就會變得混亂不堪。這時&#xff0c;classnames 庫就像…

C++系列(七):深度探索C++內存 --- 分區、堆棧、new/delete與高效編程實踐

引言 程序運行的本質是對數據的處理&#xff0c;而內存則是程序執行的核心舞臺。理解內存的物理與邏輯分區&#xff0c;是掌握程序底層行為、編寫高效可靠代碼的關鍵基石。內存并非混沌一片&#xff0c;而是被嚴格劃分為代碼區、全局區、棧區和堆區。每個區域擁有獨特的生命周…

微信小程序71~80

1.總結小程序生命周期 小程序冷啟動&#xff0c;鉤子函數執行的順序保留當前頁面&#xff0c;進入下一個頁面&#xff0c;鉤子函數執行的順序銷毀當前頁面&#xff0c;進入下一個頁面&#xff0c;鉤子函數執行的順序小程序熱啟動&#xff0c;鉤子函數執行的順序 2.使用Componen…

[Pytest][Part 3]檢測python package狀態

目錄 實現需求1&#xff1a; 檢查python package狀態——pkg_resource hook實現自動檢測包狀態 conftest.py hook鉤子函數 Part1: https://blog.csdn.net/x1987200567/article/details/144915315?spm1001.2014.3001.5501 從這里開始逐個實現Part1中的需求 實現需求1&a…

自定義時間范圍選擇組件使用教程(基于 Vue 3 + Element Plus)

&#x1f553; 自定義時間范圍選擇組件使用教程&#xff08;基于 Vue 3 Element Plus&#xff09;? 一個靈活實用的時間范圍選擇器&#xff0c;支持開始時間、結束時間、快捷時間選項、本地雙向綁定、插槽擴展等功能。–&#x1f4d8; 一、功能介紹 該組件基于 Element Plus …

YOLOv8 模型轉換 ONNX 后 C# 調用異常:一個參數引發的跨平臺適配難題

一、問題背景&#xff1a;從 Python 訓練到 C# 部署的跨平臺需求 作為一名 C# 開發者&#xff0c;我在完成 YOLOv8 模型訓練&#xff08;使用 Ultralytics 官方框架&#xff0c;訓練數據為自定義目標檢測數據集&#xff0c;輸入尺寸 640x640&#xff0c;訓練輪次 100 輪&#…

Apache Cloudberry 亮相 2025 IvorySQL 生態大會暨 PostgreSQL 高峰論壇

6 月 27 日至 28 日&#xff0c;IvorySQL 2025 生態大會暨 PostgreSQL 高峰論壇在泉城濟南順利召開。本屆大會由 IvorySQL 開源數據庫社區主辦、瀚高基礎軟件股份有限公司承辦&#xff0c;吸引了來自國內外的數據庫技術專家、開發者與開源愛好者齊聚一堂&#xff0c;聚焦數據庫…

CMake之CMakeLists.txt語法規則

本文主要參考正點原子的應用開發手冊&#xff0c;僅作為本人學習筆記使用。 目錄 cmake 的使用方法其實還是非常簡單的&#xff0c;重點在于編寫 CMakeLists.txt&#xff0c;CMakeLists.txt 的語法規則也簡單&#xff0c;并沒有 Makefile的語法規則那么復雜難以理解&#xff01…

Mysql專題復習

重點內容&#xff1a;1. Mysql架構&#xff1a;客戶端 Server層 存儲引擎2. 索引數據結構&#xff1a;B樹4. 索引優化&#xff1a;覆蓋索引、排序、JOIN、分頁&#xff1b; COUNT; 索引下推&#xff1b;單/雙路排序5. 數據庫事務&#xff1b; 鎖&#xff1b;隔離級別&#xff…

CLIP的tokenizer詳解

一、bytes_to_unicodedef bytes_to_unicode():"""Returns list of utf-8 byte and a corresponding list of unicode strings.The reversible bpe codes work on unicode strings.This means you need a large # of unicode characters in your vocab if you wa…

【如何判斷Linux系統是Ubuntu還是CentOS】

要確定您的操作系統是 Ubuntu 還是 CentOS&#xff0c;可以通過以下方法快速檢查&#xff1a; 方法 1&#xff1a;通過終端命令&#xff08;推薦&#xff09; 在終端中執行以下命令之一&#xff1a; 查看 /etc/os-release 文件 cat /etc/os-releaseUbuntu 特征&#xff1a;顯示…

RISCV Linux 虛擬內存精講系列二 -- Linux 入口 head.S

通過 Linux 的構建系統&#xff0c;即 Linux 源代碼的根目錄下的 Makefile&#xff0c;能夠找到 vmlinux 的鏈接文件&#xff0c;從而能夠查看其入口代碼 head.S:_start&#xff0c; 如下&#xff1a; Linux 構建系統主Makefile: vmlinux.lds: head.S: 找到該入口后&#xff0c…

springAI學習:Advisors

spring AI Advisors類似于攔截器&#xff0c;會對請求的prompt做出特定的修改和增強&#xff08;比如傳入歷史溝通記錄、搜索信息等等&#xff09;&#xff0c;以達到完善prompt的目的。通過Advisors API&#xff0c;開發人員可以創建更為復雜、可重用、可維護的AI組件。下面介…

MySQL CDC與Kafka整合指南:構建實時數據管道的完整方案

一、引言&#xff1a;現代數據架構的實時化需求 在數字化轉型浪潮中&#xff0c;實時數據已成為企業的核心資產。傳統批處理ETL&#xff08;每天T1&#xff09;已無法滿足以下場景需求&#xff1a; 實時風險監控&#xff08;金融交易&#xff09;即時個性化推薦&#xff08;電商…

MATLAB | 繪圖復刻(二十一)| 扇形熱圖+小提琴圖

前段時間在小紅書刷到了一個很有特色的熱力圖&#xff0c;由大佬滾筒洗衣機創作&#xff0c;感覺很有意思&#xff0c;嘗試 MATLAB 復刻&#xff1a; 作者使用的是 python 代碼&#xff0c;趕快去瞅瞅。 復刻效果 正文部分 0.數據準備 數據需要一個用來畫熱圖的矩陣以及一個…

批量PDF轉換工具,一鍵轉換Word Excel

軟件介紹 今天為大家推薦一款高效的Office文檔批量轉換工具&#xff0c;能夠快速將Word和Excel文件批量轉換為PDF格式。 軟件特點 這款名為"五五Excel word批量轉PDF"的工具體積小巧&#xff0c;不到2M大小&#xff0c;卻能實現強大的批量轉換功能&#xff0c…

面試150 基本計算器

思路 利用棧&#xff08;stack&#xff09;來保存進入括號前的計算狀態&#xff08;包括當前計算結果和符號&#xff09;&#xff0c;以便在括號結束后正確恢復計算上下文。代碼通過遍歷字符串&#xff0c;識別數字、加號、減號和括號。遇到數字時構造完整數值&#xff1b;遇到…