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

Linux 多執行緒與進程通訊實戰:互斥鎖、條件變數、管道、共享記憶體、信號量完整範例

     編輯:關於C語言

從管道到共享記憶體:Linux IPC 機制全景實作指南

Linux 提供豐富的進程間通訊(IPC)與執行緒同步機制。本文系統整理 8 大 IPC 方式,並提供 生產者-消費者模型(pthread 條件變數)、命名管道、System V 信號量、訊息佇列、共享記憶體、信號 六大完整可編譯執行範例。

IPC 機制快速選型表

機制適用場景資料流向同步支援跨主機效能
管道親緣關係進程單向內建阻塞否中
命名管道 (FIFO)無親緣進程單向內建阻塞否中
信號異步事件通知單向信號處理函數否低(資料量極小)
訊息佇列結構化資料交換雙向阻塞收發否中
共享記憶體大量資料高速共享隨機存取需配合信號量/互斥鎖否最高
記憶體映射 (mmap)檔案/裝置映射共享隨機存取需額外同步否高
信號量同步/互斥控制控制訊號P/V 原語否高(核心態)
Socket網路/跨主機通訊雙向串流/資料包阻塞/非阻塞/epoll是視網路

範例 1:執行緒同步——生產者/消費者 (pthread_mutex + pthread_cond)

環狀緩衝區,notempty/notfull 兩條件變數實現無忙等待同步。

// 關鍵結構
struct prodcons {
    int buffer[16];
    pthread_mutex_t lock;
    pthread_cond_t notempty, notfull;
    int readpos, writepos;
};

// 放入資料
void put(struct prodcons *b, int data) {
    pthread_mutex_lock(&b->lock);
    while ((b->writepos + 1) % 16 == b->readpos)
        pthread_cond_wait(&b->notfull, &b->lock); // 等待非滿
    b->buffer[b->writepos++] = data;
    b->writepos %= 16;
    pthread_cond_signal(&b->notempty); // 通知非空
    pthread_mutex_unlock(&b->lock);
}

// 取出資料
int get(struct prodcons *b) {
    pthread_mutex_lock(&b->lock);
    while (b->writepos == b->readpos)
        pthread_cond_wait(&b->notempty, &b->lock); // 等待非空
    int data = b->buffer[b->readpos++];
    b->readpos %= 16;
    pthread_cond_signal(&b->notfull);
    pthread_mutex_unlock(&b->lock);
    return data;
}

關鍵點:while 而非 if 檢查條件(防止虛假喚醒);pthread_cond_wait 原子釋放鎖並休眠,喚醒後重新獲取鎖。

範例 2:命名管道 (FIFO) 雙向通訊

// 建立 FIFO
mkfifo("fifo.demo", 0666);

// 寫入端 (writefifo.c)
int fd = open("fifo.demo", O_WRONLY); // 讀端未開啟時阻塞
while (fgets(buf, N, stdin)) {
    write(fd, buf, strlen(buf));
    if (strncmp(buf, "exit", 4) == 0) break;
}

// 讀取端 (readfifo.c)
int fd = open("fifo.demo", O_RDONLY); // 寫端未開啟時阻塞
while ((n = read(fd, buf, N)) > 0)
    write(STDOUT_FILENO, buf, n);

非阻塞模式:open(..., O_RDONLY | O_NONBLOCK)。

範例 3:System V 信號量實現互斥列印

// P 操作 (等待/鎖定)
struct sembuf sem_b = {0, -1, SEM_UNDO};
semop(sem_id, &sem_b, 1);

// V 操作 (釋放/解鎖)
sem_b.sem_op = 1;
semop(sem_id, &sem_b, 1);

兩進程交替列印 X、Y,展示關鍵區保護。SEM_UNDO 確保進程異常終止時自動釋放信號量。

範例 4:訊息佇列

key_t key = ftok(".", 'a');
int qid = msgget(key, IPC_CREAT | 0666);
struct msgbuf { long mtype; char mtext[512]; } msg;
msgsnd(qid, &msg, len, 0); // 發送
msgrcv(qid, &msg, 512, 0, 0); // 接收 (mtype=0 取首個)
msgctl(qid, IPC_RMID, NULL); // 刪除

範例 5:共享記憶體 + 信號同步 (雙進程聊天)

結構體包含 pid_t pid 交換進程 ID,透過 SIGUSR1 通知對方資料就緒。

// 關鍵流程
shmid = shmget(key, sizeof(SHM), IPC_CREAT | IPC_EXCL | 0666);
shmaddr = shmat(shmid, NULL, 0);
shmaddr->pid = getpid();
pause(); // 等待對方就緒
// 之後雙方透過 kill(peerpid, SIGUSR1) 互相通知

IPC_EXCL 判斷誰是第一個建立者,避免競爭條件。

範例 6:信號模擬公車司機/售票員協作

// 司機進程
signal(SIGTSTP, driver_handler); // Ctrl+Z: 發車/到站
signal(SIGUSR1, driver_handler); // 售票員通知發車
signal(SIGUSR2, driver_handler); // 售票員通知停車

// 售票員進程
signal(SIGINT, conductor_handler); // Ctrl+C: 發車
signal(SIGQUIT, conductor_handler); // Ctrl+\: 停車
signal(SIGUSR1, conductor_handler); // 司機通知到站

工程實踐建議

  1. 優先選用高層抽象:POSIX 訊息佇列、pthread 條件變數、eventfd/signalfd (Linux 專用) 比 System V IPC 更現代、介面更簡潔。
  2. 共享記憶體必須配合同步:單獨使用會導致資料競爭,搭配信號量、futex 或 RCU。
  3. 清理機制:使用 IPC_RMID、shmctl、semctl、msgctl 及時釋放核心資源,避免洩漏。
  4. 錯誤處理:所有系統呼叫檢查回傳值,errno 區分 EINTR (被信號中斷) 需重試與真正錯誤。
  5. 容器化環境:System V IPC 需 --ipc=host 或適當 namespace 設定。

掌握這些 IPC 原語與同步模式,是開發高效能、高可靠度 Linux 系統軟體(資料庫、訊息中介軟體、即時通訊伺服器)的核心技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved