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

System V 信號量 API 深度解析:semget、semop、semctl 完全指南

     編輯:關於C語言

信號量核心概念

信號量是 Dijkstra 提出的同步原語,本質是一個 非負整數計數器,支援兩種 原子操作:

  • P 操作 (wait):計數器 > 0 時遞減;= 0 時阻塞進程
  • V 操作 (signal):有進程等待則喚醒;否則計數器遞增

三大核心函數詳解

semget - 獲取/建立信號量集

int semget(key_t key, int nsems, int semflg);
參數說明
keyIPC 鍵值,通常由 ftok() 產生
nsems信號量個數,單一信號量通常為 1
semflg權限標誌(如 0666 \| IPC_CREAT)

返回:成功返回信號量集識別碼 (semid),失敗返回 -1

semop - 信號量操作 (P/V 原語)

int semop(int semid, struct sembuf *sops, size_t nsops);

sembuf 結構體:

struct sembuf {
    unsigned short sem_num;  // 信號量編號 (0-based)
    short          sem_op;   // 操作值:+1 (V), -1 (P)
    short          sem_flg;  // 通常為 SEM_UNDO
};
  • 原子性保證:單次呼叫中的所有操作要么全做要么全不做
  • SEM_UNDO:進程異常終止時自動撤銷操作,防止死鎖

semctl - 信號量控制

int semctl(int semid, int semnum, int cmd, ...);

常用命令:

完整使用流程

// 1. 定義聯合體 (Linux 必須)
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

// 2. 建立/獲取信號量
int semid = semget(ftok(".", 's'), 1, 0666 | IPC_CREAT);

// 3. 初始化為 1 (互斥鎖)
union semun arg;
arg.val = 1;
semctl(semid, 0, SETVAL, arg);

// 4. P 操作
struct sembuf p_op = {0, -1, SEM_UNDO};
semop(semid, &p_op, 1);

// 臨界區程式碼...

// 5. V 操作
struct sembuf v_op = {0, +1, SEM_UNDO};
semop(semid, &v_op, 1);

// 6. 刪除 (僅最後一個使用者調用)
semctl(semid, 0, IPC_RMID);

實戰封裝建議

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

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

// 封裝初始化/刪除
int sem_init(int semid, int value) {
    union semun arg = {.val = value};
    return semctl(semid, 0, SETVAL, arg);
}

int sem_destroy(int semid) {
    return semctl(semid, 0, IPC_RMID);
}

執行結果驗證

$ ./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

常見陷阱與避坑

  1. 忘記定義 union semun:Linux 內核頭檔未提供,必須自行定義
  2. 信號量值溢出:短整型限制最大值 32767,高併發場景需注意
  3. 未使用 SEM_UNDO:進程崩潰導致信號量洩漏,造成永久阻塞
  4. 重複刪除:多進程競爭刪除,應由最後一個使用者清理
  5. 鍵值衝突:使用 ftok() 而非硬編碼 key_t
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
命令用途第四參數
SETVAL初始化信號量值union semun { int val; }
IPC_RMID刪除信號量集不需要
GETVAL獲取當前值不需要