以struct和union爲線索來觀察信號量數組
第一部分 semid_ds ipc_permui
內核爲每一個信號量集合維護一個結構體semid_ds:進程
struct semid_ds {事件
struct ipc_perm sem_perm;ip
unsigned short sem_nsems; /* # of semaphore in set */資源
time_t sem_otime; /* last-semop() time */get
time_t sem_ctime; /* last-change time */cmd
…it
};io
semid_ds的一個結構體成員ipc_perm是XSI IPC爲每個IPC結構設置的, 定義以下:
struct ipc_perm {
uid_t uid; /* owner’s effective user id */
gid_t gui; /* owner’s effective group id */
uid_t cuid; /* creator’s effective user id */
gid_t cgid; /* creator’s effective group id */
mode_t mode; /* access mode */
…..
};
好, 有了以上兩個結構體, 咱們先認識semget
#include <sys/sem.h>
int semget (key_t key; int nsems, int flag);
返回值: 若成功則返回信號量ID, 若出錯則返回-1
先說semget和這兩個struct的關係:
若是是建立一個新的信號量集合, 內核爲這個信號量集合維護一個結構體semid_ds, 同時對semid_ds的成員進行初始化:
ipc_perm結構賦初值, ipc_perm中的mode被設置爲flag中的相應權限位.
sem_nsems設置爲nsems
sem_otime設置爲0.
sem_ctime設置爲當前時間.
另外, 注意若是建立新集合, 則必須指定nsems, 若是引用一個集合, 則nsems設定爲0;
一句話歸納, 就是semget建立信號量集合的時候初始化了semid_ds.
第二部分 semun和一個無名結構體
semctl參數中使用了一個union, 定義以下
union semun {
int val; /* for SETVAL */
struct semid_ds *buf; /* for IPC_STAT and IPC_SET */
unsigned short *array; /* for GETALL and SETALL */
};
還要認識一個無名結構體. (我總以爲無名結構體聽起來就很cool). 每一個信號量由這樣一個結構體表示. 定義以下:
struct {
unsigned short semval; /* semaphore value, always >= 0 */
pid_t sempid; /* pid for last operation */
unsigned short semncnt; /* # processes awaiting semval>curval */
unsigned short semzcnt; /* # processes awaiting semval == 0 */
….
};
有必要說明一下, semget建立的是一個信號量集合, 因此semid_ds是針對這個信號量集合的. 而上面這個無名結構體是針對的一個信號量.
好, 有了以上的一個union和一個struct, 咱們引出semctl的定義.
#include <sys/sem.h>
int semctl (int semid, int semnum, int cmd, … /* union semun arg */);
返回值: 有點複雜, 下面詳細說明
先說semctl和semun的關係.
semctl的第四個參數爲可選參數, 若是使用, 則應該爲semun類型, 要注意的是semun必須顯式的定義在用戶的程序中. 具體用法和cmd有關係.
再說semctl和無名結構體的關係. 具體用法仍是和cmd有關係.
因此, 咱們必須介紹一下cmd的用法.
cmd包括十種命令, 而針對的信號量用semnum指定. 範圍爲[0, nsems-1]
IPC_STAT
讀取semid_ds到arg.buf指向的struct中
IPC_SET
將arg.buf指向的struct設置到sem_perm.uid, sem_perm.gid和sem_perm.mode
IPC_RMID
刪除信號量集合
GETVAL
返回semnum信號量的無名結構體的成員semval值
SETVAL
arg.val設置到由semnum信號量的無名結構體成員semval中
GETPID
返回無名結構體成員sempid
GETNCNT
返回無名結構體成員semncnt
GETZCNT
返回無名結構體成員semzcnt
GETALL
取該集合中全部信號量(無名結構體)的值, 存放在arg.array指向的數組中
SETALL
將arg.array指向的數組的值設置該集合全部信號量的值(無名結構體)
總結一下.
圍繞union semun來講:
1. int val由SETVAL使用, semctl將會把arg.val設置到信號量的semval中.
2. struct semid_ds *buf由IPC_STAT和IPC_SET使用, 讀取時將信號量集合的semid_ds讀取到arg.buf中. 設置時使用arg.buf的三項內容.
3. unsigned short *array由GETALL和SETALL使用, 將讀取和設置集合中的全部信號量
圍繞無名結構體來講:
1. semval爲R/W, 讀取時直接爲semop的返回值, 設置時將arg.val設置給semval
2. sempid, semncnt, semzcnt爲只讀, 讀取時職位爲semop的返回值.
一句話歸納. semctl讀取和設置整個信號量集合, 讀取和設置信號量集合中的每一個信號量, 刪除信號量集合
注意. semget只是建立了信號量集合, 在使用以前必須使用semctl設置你要使用的信號量
第三部分 sembuf
semop的一個參數爲struct sembuf類型, 定義以下:
struct sembuf {
unsigned short sem_num; /* member # in set [0, nsems-1] */
short sem_op; /* operation (negative, 0, or positive) */
short sem_flg; /* IPC_NOWAIT, SEM_UNDO */
};
#include <sys/sem.h>
int semop (int semid, struct sembuf semoparray[], size_t nops);
返回值: 若成功返回0, 若出錯則返回
參數nops規定該數組中操做的數量(元素數)
先說明一下, 第二個參數之因此定義爲當前形式, 而沒有定義成struct sembuf *semoparray. 是由於semop能夠執行數組中的nops個操做.
整個semop圍繞sembuf來進行不一樣的操做.
成員sem_num指定了信號量
成員sem_op和sem_flg聯合指定了semop的行爲. 根據書上的描述, 以下:
1. sem_op爲正, 則將sem_op加到semval上.
2. sem_op爲負,
semval大於或等於sem_op的絕對值, 則從semval減去sem_op的絕對值.
semval小於sem_op的絕對值, 由於信號量爲非負值, 則根據sem_flg有以下行爲:
(a) 設定了IPC_NOWAIT, 則semop返回EAGAIN.
(b) 沒設定IPC_NOWAIT, 則semncnt值加1, 而後進程掛起直到下列時間之一發生.
(i) semval變成大於或等於sem_op的絕對值. semncnt值減一.
(ii) 信號量被刪除, semop返回EIDRM
(iii) 進程捕獲到一個信號, 並從信號處理程序返回.semncnt減1, semop返回EINTR.
3. sem_op爲0的狀況
semval爲0, 正常返回.
semval非0, 則:
(a) 指定了IPC_NOWAIT, sem_op返回EAGAIN
(b) 未指定IPC_NOWAIT, semzcnt加1, 進程掛起, 直到下列事件之一發生
(i) semval變爲0, semzcnt減1
(ii) 信號量被刪除, semop返回EIDRM
(iii) 進程捕獲到一個信號, 並從信號處理程序返回,semzcnt減1,semop返回EINTR
4. 若是設置了SEM_UNDO, 則在調用semop時, 對semval的操做將會記錄到信號量調整值上, 當前進程退出後, 內核將按調整值對信號量進行處理. 可是若是使用帶有SETVAL或SETALL的semctl設置某一信號量, 則在全部進程中, 該信號量的調整值都設置爲0.
書上說的很嚴謹, 可是帶來的問題就是羅嗦. 其實咱們簡單的理解, 就是, semop用來得到和釋放資源, 釋放資源時比較簡單, 得到資源時, 若是資源不足, 則根據IPC_NOWAIT標誌, 掛起或者直接返回. 取消掛起有三個條件, 有足夠的資源了, 信號量被刪除了, 捕獲並處理完了一個信號. 進程醒來後semop返回相應的值.
一句話歸納, semop根據semoparray進行nops個獲取和釋放資源的動做.
第四部分 信號量和記錄鎖
書中最後對比了信號量和記錄鎖. 給出的結論爲若是隻需鎖一個資源, 而且不須要使用XSI信號量的全部花哨(fancy)的功能, 則寧肯使用記錄鎖, 儘管記錄鎖比信號量耗時, 可是記錄鎖使用簡單, 且進程終止時系統會處理任何遺留下來的鎖.