首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux 系統程式設計:進程管理核心概念與實戰詳解

     編輯:關於C語言

Linux 系統程式設計:進程管理核心概念與實戰詳解

程序與進程的本質區別

在 Linux 系統中,程序 是靜態的二進制可執行文件,佔據磁碟空間但不消耗運行時資源(CPU、記憶體、文件描述符等)。而進程 是程序的動態執行實例,擁有獨立的虛擬地址空間、PCB(進程控制塊)、打開的文件表等內核資源。同一個程序可被加載為多個互不干擾的進程——如同時開啟多個終端,每個終端運行獨立的 bash 進程,擁有不同的 PID。

並發與多道程序設計

並發 是指在一段時間內,多個進程處於「已啟動至運行完畢」的狀態,但在任一時刻點上,單核 CPU 僅執行一個進程。其核心機制是分時復用 CPU:由時鐘中斷強制調度,使進程輪流佔用 CPU 時間片。現代 CPU 每秒執行數十億條指令,遠超人眼毫秒級感知,形成「宏觀並行、微觀串行」的假象。

早期的單道程序設計 導致 CPU 空閒等待 I/O,資源利用率極低。多道程序設計 將多個程序同時載入記憶體,配合時鐘中斷與調度演算法,實現 CPU 高效利用,推動了多媒體時代的到來。

並發示意圖

進程控制塊(PCB)與 task_struct

Linux 內核用 task_struct 結構體描述進程,定義於 /usr/src/linux-headers-.../include/linux/sched.h。關鍵字段包括:

  • 進程 ID(pid_t,非負整數)
  • 進程狀態(就緒、運行、掛起、停止、僵屍等)
  • CPU 寄存器上下文(用於切換恢復)
  • 虛擬地址空間描述符(mm_struct)
  • 控制終端、當前工作目錄、umask
  • 文件描述符表(指向 file 結構體)
  • 信號處理相關信息(阻塞集、未決集)
  • 用戶/組 ID、會話與進程組、資源限制(rlimit)
CPU 與 MMU 架構
MMU 詳細結構
進程狀態轉換圖

環境變量機制

環境變量以 名=值 字符串形式存儲於用戶空間高地址(棧上方),以 NULL 結尾的 char **environ 數組訪問。常見變量:

  • PATH:可執行文件搜索路徑,冒號分隔
  • SHELL:當前 Shell 路徑(通常 /bin/bash)
  • TERM:終端類型(如 xterm)
  • LANG:語言與 locale 設置
  • HOME:用戶主目錄

操作函數:getenv() 讀取、setenv() 設置(overwrite 控制覆蓋)、unsetenv() 刪除。

#include <stdio.h>
extern char **environ;
int main() {
    for (int i = 0; environ[i]; i++)
        printf("%s\n", environ[i]);
    return 0;
}

進程創建:fork() 與寫時復制(COW)

pid_t fork(void) 創建子進程。返回值區分父子:父進程獲取子進程 PID,子進程返回 0,失敗返回 -1。父子進程共享代碼段(.text)、數據段(.data)、堆、棧、環境變量、文件描述符表等,但擁有獨立的 PID、返回值、父進程 ID、運行時間、定時器、未決信號集。

核心優化:讀時共享、寫時復制(COW)。fork 時不複製物理頁面,而是將父進程頁表標記為只讀,共享物理內存。當任一方嘗試寫入,頁面缺失中斷觸發內核複製該頁,實現真正的隔離,極大節省內存。

#include <stdio.h> #include <unistd.h> #include <stdlib.h>
int main() {
    pid_t pid = fork();
    if (pid == -1) { perror("fork"); exit(1); }
    else if (pid == 0) printf("Child: pid=%d, ppid=%d\n", getpid(), getppid());
    else { printf("Parent: pid=%d, child=%d\n", getpid(), pid); sleep(1); }
    return 0;
}

循環創建 N 個子進程 需在子進程分支 break,否則會產生 2^N 個進程(二叉樹擴展)。

fork 進程樹

GDB 調試多進程

GDB 預設跟蹤父進程。在 fork() 前設置:

  • set follow-fork-mode child 跟蹤子進程
  • set follow-fork-mode parent 跟蹤父進程

exec 函數族:程序替換

exec 族函數將當前進程的用戶空間代碼/數據(.text/.data)替換為新程序,從新程序入口開始執行,進程 ID 不變(「換核不換殼」)。六大成員:

  • execl(path, arg, ..., NULL):列表參數,指定完整路徑
  • execlp(file, arg, ..., NULL):列表參數,搜索 PATH
  • execle(path, arg, ..., NULL, envp[]):指定環境變量
  • execv(path, argv[]):數組參數,指定路徑
  • execvp(file, argv[]):數組參數,搜索 PATH
  • execve(path, argv[], envp[]):唯一真正的系統調用,其餘均封裝之

成功不返回,失敗返回 -1。常配合 perror() 和 exit() 使用。

exec 函數關係圖
// 子進程執行 ls -l -a
execlp("ls", "ls", "-l", "-a", NULL);
// 等價於
char *argv[] = {"ls", "-l", "-a", NULL};
execvp("ls", argv);

進程回收:wait()、waitpid() 與僵屍/孤兒進程

子進程終止後,PCB 滯留內核成為僵屍進程,佔用 PID。父進程需調用 wait() 或 waitpid() 回收資源、獲取退出狀態。若父進程先結束,子進程成為孤兒進程,被 init(PID 1)收養。

  • wait(&status):阻塞回收任意子進程
  • waitpid(pid, &status, options):可指定 PID、非阻塞(WNOHANG)、進程組回收

狀態宏:WIFEXITED 正常退出、WEXITSTATUS 取退出碼;WIFSIGNALED 信號終止、WTERMSIG 取信號號;WIFSTOPPED 暫停、WSTOPSIG 取停止信號。

// 非阻塞回收多個子進程
while ((wpid = waitpid(-1, NULL, WNOHANG)) > 0) n--;
sleep(1);

進程間通信(IPC)概覽

進程地址空間隔離,需借助內核機制交換數據。常用方式:

  • 管道:半雙工、血緣關系、內核環形緩衝區(4KB)
  • 命名管道(FIFO):文件系統路徑、無血緣關系、多讀多寫
  • 共享內存映射(mmap):將文件映射至虛擬地址,MAP_SHARED 實現進程間高速共享
  • 信號:異步通知、開銷最小
  • Unix 域套接字:本地雙向通信、最穩定
IPC 架構圖

管道詳細機制

int pipe(int fd[2]) 創建管道,fd[0] 讀端、fd[1] 寫端。數據從寫端流入、讀端流出。讀寫行為:

  • 讀端:有數據返回字節數;無數據且寫端全關閉返回 0(EOF);有寫端打開則阻塞
  • 寫端:讀端全關閉觸發 SIGPIPE 異常終止;管道未滿寫入返回字節數;管道滿則阻塞

典型應用:ls | wc -l 父子/兄弟進程通信,配合 dup2() 重定向標準輸入輸出。

共享內存映射(mmap)

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) 將文件映射至進程地址空間。關鍵參數:

  • prot:PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
  • flags:MAP_SHARED(修改回寫磁盤,進程間共享)、MAP_PRIVATE(寫時復制,私有)
  • offset:必須 4KB 對齊

映射區釋放用 munmap(addr, len)。注意事項:文件大小不能為 0;映射權限不超過文件打開權限(MAP_SHARED);文件關閉不影響已建立映射;嚴禁指針算術運算;必須檢查返回值(MAP_FAILED)。

mmap 原理圖

匿名映射與無血緣進程通信

使用 MAP_ANONYMOUS | MAP_SHARED 配合 fd=-1 創建無文件依賴的共享映射區,適用於父子進程。無血緣進程通過映射同一文件、設置 MAP_SHARED 實現高速數據交換。

信號機制深度解析

信號是軟中斷,異步通知進程處理事件。生命周期:產生 → 未決(阻塞導致) → 遞達 → 處理。PCB 包含阻塞信號集(屏蔽字)與未決信號集(位圖)。

  • 產生源:終端按鍵(Ctrl+C→SIGINT)、硬件異常(除零→SIGFPE、非法訪問→SIGSEGV)、系統調用(kill()、alarm())、軟件條件(定時器)
  • 處理動作:默認(Term/Core/Stop/Cont/Ign)、忽略、捕捉(註冊處理函數)
  • SIGKILL(9)、SIGSTOP(19) 不可捕捉、阻塞、忽略
信號處理流程

信號編程接口

  • kill(pid, sig):發送信號,pid>0 指定進程、pid=0 同進程組、pid<0 進程組、pid=-1 所有可達進程
  • raise(sig):自我發送,等價 kill(getpid(), sig)
  • alarm(sec):設置實時定時器,到期發送 SIGALRM
  • setitimer():微秒精度、支持周期定時(ITIMER_REAL/VIRTUAL/PROF)

信號集操作與阻塞控制

sigset_t 位圖類型,操作函數:sigemptyset/fillset/addset/delset/ismember。sigprocmask(how, set, oldset) 修改阻塞集:SIG_BLOCK 添加、SIG_UNBLOCK 移除、SIG_SETMASK 替換。sigpending(set) 讀取未決集。

信號捕捉:sigaction() 替代 signal()

struct sigaction 核心字段:sa_handler(處理函數)、sa_mask(處理期間臨時阻塞集)、sa_flags(屬性標誌)。捕捉特性:處理函數執行期間自動阻塞本信號;常規信號不排隊(多次產生僅記錄一次);實時信號支持排隊。

內核信號捕捉實現

競態條件與 sigsuspend()

pause() 掛起等待信號,但存在時序競態:alarm() 後 pause() 前若發生中斷調度,信號可能被錯過。解決方案:sigsuspend(mask) 原子性地「設置臨時屏蔽集 + 掛起」,消除窗口期。

// 改進版 mysleep:阻塞 SIGALRM → alarm → sigsuspend(解除阻塞) → 恢復
sigprocmask(SIG_BLOCK, &newmask, &oldmask);
alarm(seconds);
sigdelset(&suspmask, SIGALRM);
sigsuspend(&suspmask);

可重入函數與異步 I/O 安全

信號處理函數必須為可重入:無全局/靜態變量、不調用 malloc/free、不使用標準 I/O。man 7 signal 列出安全函數列表。全局變量在異步信號上下文中被修改會導致競態,需用鎖保護(後續課程講解)。

SIGCHLD 與子進程自動回收

子進程終止/停止/繼續產生 SIGCHLD(默認忽略)。捕捉該信號並在處理函數中循環 waitpid(-1, &status, WNOHANG) 可非阻塞回收所有僵屍子進程。注意:信號不排隊,處理函數必須用 while 循環而非 if。

信號傳值:sigqueue() 與 SA_SIGINFO

sigqueue(pid, sig, value) 攜帶整數或指釖參數(union sigval)。捕捉端設置 sa_flags = SA_SIGINFO,使用 sa_sigaction(int, siginfo_t*, void*) 獲取豐富信息(siginfo_t 包含發送者 PID、UID、用戶數據等)。

系統調用中斷與重啟

慢速系統調用(read/write/pause/wait 等)被信號中斷後返回 -1、errno=EINTR。sa_flags 控制行為:SA_RESTART 自動重啟、SA_INTERRUPT 不重啟(歷史殘留)。

終端、進程組、會話與守護進程

  • 控制終端:登錄 Shell 綁定的終端(/dev/tty),子進程繼承。ttyname(fd) 查詢設備名
  • 進程組:相關進程集合,getpgrp()/getpgid()/setpgid() 操作。Shell 管道命令各屬一組
  • 會話:進程組集合,setsid() 創建新會話、脫離控制終端、成為會話首進程(需非組長)
  • 守護進程:無控制終端、後台長期運行。創建七步法:fork()→父退出→setsid()→chdir("/")→umask(0)→關閉文件描述符→重定向到 /dev/null→核心邏輯
終端架構
網絡終端流程

階段綜合實戰項目

  1. 多進程文件拷貝:mmap 映射源/目標文件,fork() N 個子進程分塊拷貝(最後一個處理餘數),MAP_SHARED 回寫磁盤
  2. 簡易 Shell:解析命令行、支持重定向(>/<)、管道(|)、多重管道,fork()+dup2()+exec() 實現
  3. 本地聊天室:服務端監聽、協議包處理(登錄/聊天/退出),客戶端交互,IPC 轉發數據
多進程拷貝示意
聊天室協議格式
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved