信号量分有名和無名信号量。它們的差別和管道及命名管道的差別類似。有名信号量要求建立一個檔案,而無名信号量則直接儲存在記憶體中。
一,posix信号量
posex信号量接口總結(見下圖):
上面一行是有名信号量,可于fifo相類比,其值儲存在檔案中,可用于程序和線程同步;
下面一行是無名信号量,可與pipe相類比,其值儲存在記憶體中,可用于程序和線程同步;
中間部分,是兩者的公用接口。
1.公共接口
1.1 接口函數說明
#include <semaphore.h>
int sem_wait(sem_t *sem);
測試所指定信号量的值,它的操作是原子的。
若sem>0,那麼它減1并立即傳回。
若sem==0,則睡眠直到sem>0,此時立即減1,然後傳回。
int sem_trywait(sem_t *sem);
其他的行為和sem_wait一樣,除了:
若sem==0,不是睡眠,而是傳回一個錯誤eagain。
int sem_post(sem_t *sem);
把指定的信号量sem的值加1;
呼醒正在等待該信号量的任意線程。
int sem_getvalue(sem_t *sem, int *sval);
取回信号量sem的目前值,把該值儲存到sval中。
若有1個或更多的線程或程序調用sem_wait阻塞在該信号量上,該函數傳回兩種值:
1) 傳回0
2) 傳回阻塞在該信号量上的程序或線程數目
linux采用傳回的第一種政策。
注意:在這些函數中,隻有sem_post是信号安全的函數,它是可重入函數。
1.2 接口使用的一般流程
sem_init(&sem);
sem_wait(&sem);
critical area;
sem_post(&sem);
remainder area
2.無名信号量
無名信号量是儲存在變量類型為sem_t的記憶體中。
int sem_init(sem_t *sem, int pshared, unsigned int value);
1)pshared==0 用于同一多線程的同步;
2)若pshared>0 用于多個程序間的同步,此時sem必須放在共享記憶體中。
int sem_destroy(sem_t *sem);
隻能銷毀由sem_init初始化的信号量,否則後果不可預料也。
例1:
多線程使用信号量的簡單例子:
說明:該例子來自于usp。
可以把sem_wait和sme_post調用去掉,看看效果,可以看到出現了交叉輸出的情況。
nanosleep調用隻是為了讓輸出的效果更明顯,沒有其他意義。
更多的例子見mypxsem/prodcons2-4.c
3. 有名信号量
有名信号量是把信号量的值儲存在檔案中,是以它可以用于線程也可以用于程序間的同步。
如下面的形式:
3.1 常用函數說明
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
傳回一個sem_t類型的指針。該指針随後可用作sem_close等的參數。
該函數參數的詳細資訊,可以參考手冊。
int sem_close(sem_t *sem);
關閉sem信号量,并釋放資源。
int sem_unlink(const char *name);
在所有程序關閉信号量後删除name的信号量
3.2 有名信号量的使用
例子:
以上代碼建立了一個程序鍊,若把sem_wait和sem_post調用去掉,可以看到輸出很混亂。
這是由于每個子程序都共享了父程序的檔案表項,而且都指向打開的檔案表項。
system v 信号量
===============
1, 該類信号量,與posix信号量不同。它表示的信号量集,而不是單個信号量。
可用于不同程序間的同步。
核心為每個信号量集,維護一個如下的資訊結構:<sys/sem.h>
2, 信号量操作函數
a. 建立和打開信号量
int semget(key_t key, int nsems, int oflag)
(1) nsems>0 : 建立一個信的信号量集,指定集合中信号量的數量,一旦建立就不能更改。
(2) nsems==0 : 通路一個已存在的集合
(3) 傳回的是一個稱為信号量辨別符的整數,semop和semctl函數将使用它。
(4) 建立成功後一下結構被設定:
.sem_perm 的uid和gid成員被設定成的調用程序的有效使用者id和有效組id
.oflag 參數中的讀寫權限位存入sem_perm.mode
.sem_otime 被置為0,sem_ctime被設定為目前時間
.sem_nsems 被置為nsems參數的值
.而于該集合中的每個信号量不初始化,這些結構是在semctl,用參數set_val,setall初始化的。
b. 設定信号量的值
int semop(int semid, struct sembuf *opsptr, size_t nops);
(1) semid 是semget傳回的semid
(2) nops : 是數組opsptr的個數
(3) opsptr : 是操作結構的數組
(4) 若sem_op 是正數,其值就加到semval上;
若sem_op 是0,那麼調用者希望等到semval變為0,如果semval是0就反回;
若sem_op 是負數,那麼調用者希望等待semval變為大于或等于sem_op的絕對值.
(5) sem_flg
sem_undo 由程序自動釋放信号量
ipc_nowait 不阻塞
c. 對信号量集實行控制操作
int semctl(int semid, int semnum, int cmd, ../* union semun arg */);
其中semid是信号量集合,semnum是信号在集合中的序号,
cmd是控制指令,參數可選
cmd取值如下:
getval, setval : semid集合中semnum信号量目前的semval值
getall,setall :semid集合中所有信号量的值。
ipc_rmid:删除semid信号量集
getpid:傳回最後成功操作該信号的程序号。
ipc_stat:傳回semid集合中的struct semid_ds結構。
例子: