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

System V 信號量實戰:從 API 到完整同步程式的完整實作

     編輯:關於C語言

實作步驟總覽

  1. semget:建立/獲取信號量集
  2. semctl + SETVAL:初始化信號量值
  3. semop (P操作):進入臨界區
  4. 執行臨界區程式碼
  5. semop (V操作):離開臨界區,喚醒等待者
  6. semctl + IPC_RMID:刪除信號量 (僅最後一個進程)

關鍵程式碼結構

1. 定義 union semun (Linux 必須)

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

2. 封裝核心操作

// 初始化
int init_sem(int semid, int value) {
    union semun arg = {.val = value};
    return semctl(semid, 0, SETVAL, arg);
}

// P 操作
int sem_p(int semid) {
    struct sembuf op = {0, -1, SEM_UNDO};
    return semop(semid, &op, 1);
}

// V 操作
int sem_v(int semid) {
    struct sembuf op = {0, +1, SEM_UNDO};
    return semop(semid, &op, 1);
}

// 刪除
int del_sem(int semid) {
    return semctl(semid, 0, IPC_RMID);
}

3. 主程式邏輯 (偽代碼轉實作)

int main(int argc, char *argv[]) {
    int semid = semget(KEY, 1, 0666 | IPC_CREAT);
    
    if (argc > 1 && strcmp(argv[1], "-s") == 0) {
        // 初始化/刪除模式
        init_sem(semid, 1);
        printf("[pid: %d] init semaphore\n", getpid());
        
        // 模擬工作
        sem_p(semid);
        printf("[pid: %d] enter critical area\n", getpid());
        printf("[pid: %d] running critical code\n", getpid());
        sleep(2);
        printf("[pid: %d] leave critical area\n", getpid());
        sem_v(semid);
        
        del_sem(semid);
        printf("[pid: %d] delete semaphore\n", getpid());
    } else {
        // 僅 P/V 測試模式
        sem_p(semid);
        printf("[pid: %d] enter critical area\n", getpid());
        printf("[pid: %d] running critical code\n", getpid());
        sleep(1);
        printf("[pid: %d] leave critical area\n", getpid());
        sem_v(semid);
    }
    return 0;
}

執行驗證

$ ./a.out -s &          # 後台:初始化 + 佔用鎖
[1] 2713
[pid: 2713] init semaphore
[pid: 2713] running nocritical code

$ ./a.out                 # 前台:嘗試獲取鎖,阻塞等待
[pid: 2714] running nocritical code
[pid: 2713] enter critical area
[pid: 2713] running critical code
[pid: 2713] leave critical area

[pid: 2714] enter critical area   # 獲取到鎖
[pid: 2714] running critical code
[pid: 2714] leave critical area

$ [pid: 2713] delete semaphore
[1]+  Done                    ./a.out -s

關鍵觀察點

  • ✅ 互斥生效:前台進程必須等待後台進程釋放鎖才能進入
  • ✅ 同步正確:臨界區程式碼嚴格串行執行
  • ✅ 資源清理:最後由初始化進程刪除信號量

進階擴充:多進程生產者-消費者

// 三個信號量
sem_t mutex = 1;      // 保護緩衝區
sem_t empty = N;      // 空槽位
sem_t full = 0;       // 滿槽位

// 生產者
while (1) {
    produce_item();
    sem_p(empty); sem_p(mutex);
    put_to_buffer();
    sem_v(mutex); sem_v(full);
}

// 消費者
while (1) {
    sem_p(full); sem_p(mutex);
    take_from_buffer();
    sem_v(mutex); sem_v(empty);
    consume_item();
}

常見錯誤與修正

錯誤現象原因修正
編譯錯誤:union semun 未定義Linux 內核頭檔移除自行定義 union semun
死鎖:進程崩潰後信號量未釋放未使用 SEM_UNDOsembuf.sem_flg = SEM_UNDO
信號量值異常增長重複 V 操作或邏輯錯誤嚴格配對 P/V,加入斷言檢查
權限被拒絕semflg 權限不足使用 0666 或檢查 umask

下一步學習方向

  • POSIX 信號量 (sem_t, sem_init, sem_wait, sem_post) - 更現代、可移植
  • 執行緒同步 (pthread_mutex, pthread_cond) - 進程內同步更輕量
  • 無鎖程式設計 (atomic operations, RCU) - 極高效能場景
  • 分散式鎖 (Redis, etcd, ZooKeeper) - 微服務架構同步
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved