訊號量用一句話來總結就是帶有等待佇列的計數器。訊號量也就是對我們的臨界資源進行計數。
就像我們去車站買票一樣:
當取票機有票的時候,也就是資源計數 >0,那我們就可以直接取票,並且取票機的票數-1;
當取票機沒有票的時候,也就是資源計數 <= 0,那我們就得等待取票機補上票,我們才能取票
當票機補一張票,資源計數也就+1,就是多了乙份資源。
當訊號量s > 0, s表示可用的臨界資源數
當訊號量s == 0,s表示目前無臨界資源數,等待佇列中無等待程序
當訊號量s < 0, |s| 表示等待佇列中的等待資源的程序個數
那麼我們的pv操作又是啥?
p操作表示申請乙份資源,申請了乙份資源代表臨界資源數就減少乙份,訊號量-1;也就是我們取了一張票
v操作表示釋放乙份資源,釋放了乙份資源代表臨界資源數就增加乙份,訊號量+1;也就是補票機補了一張票
要注意:p 操作和 v 操作是搭配使用的,有 p 必有 v,有 v 必有 p
為啥我們的pv操作沒有加鎖也不用考慮執行緒安全問題呢?
因為pv操作是原子性的。
p原語:
p
(s)}
v原語:
v
(s)}
臨界資源(互斥資源):一次只允許乙個程序使用的資源就是臨界資源
臨界區:在程序中涉及到臨界資源的程式**部分就是臨界區
像我們的pv操作都是臨界區,因為是對臨界資源進行控制。
使用訊號量就可以協調多個程序訪問乙份臨界資源。保護臨界資源的使用。任意時刻只能有乙個執行緒進入臨界區。
訊號量其實就是由乙個計數器和乙個等待佇列組成
訊號量結構體:
struct semaphore
;
訊號量集函式:
1、建立/訪問乙個訊號量集
int semget(key_t key, int nsems, int sem***);2、控制訊號量集引數:key:集合的名字
nsems:訊號集中訊號量的個數
sem***:由九個許可權標誌構成
返回值:成功返回該訊號集的標識碼;失敗返回-1
int semctl(int semid, int semnum, int cmd, …);3、建立和訪問乙個訊號量集引數:semid:由semget返回的訊號集標識碼
semnum:訊號集中訊號量的序號
cmd:將要採取的動作(有三個可取值)
最後⼀個引數根據命令不同⽽不同
返回值:成功返回0;失敗返回-1
int semop(int semid, struct sembuf *sops, unsigned nsops);sops指向的結構體引數:semid:是該訊號量的標識碼,也就是semget函式的返回值
sops:是個指向⼀個結構數值的指標
nsops:訊號量的個數
返回值:成功返回0;失敗返回-1
struct sembuf
;
sem_op:
① 如果其值為正數,該值會加到現有的訊號內含值中。通常用於釋放所控資源的使用權;
② 如果sem_op的值為負數,而其絕對值又大於訊號的現值,操作將會阻塞,直到訊號值大於或等於sem_op的絕對值。通常用於獲取資源的使用權;
③ 如果sem_op的值為0,則操作將暫時阻塞,直到訊號的值變為0。
sem_***:
ipc_nowait:對訊號的操作不能滿足時,semop()不會阻塞,並立即返回,同時設定錯誤資訊。
sem_undo:程式結束時(不論正常或不正常),保證訊號值會被重設為semop()呼叫前的值。這樣做的目的在於避免程式在異常情況下結束時未將鎖定的資源解鎖,造成該資源永遠鎖定。
訊號量的生命週期隨核心。
我們來看看訊號量的實際應用。子程序列印aa,父程序列印bb,正常情況列印出什麼?如果沒有pv操作列印出什麼?
comm.h
#ifndef __comm_h_
#define __comm_h_
#include
#include
#include
#include
#include
#include
#define pathname "."
#define proj_id 0x6666
union semun
;int
createsemset
(int nums)
;int
initsem
(int semid,
int nums,
int initval)
;int
getsemset
(int nums)
;intp(
int semid,
int who)
;intv(
int semid,
int who)
;int
destroysemset
(int semid)
;#endif
comm.c
#include
"comm.h"
static
intcommsemset
(int nums,
int flags)
int semid =
semget
(_key, nums, flags);if
(semid <0)
return semid;
}int
createsemset
(int nums)
intgetsemset
(int nums)
intinitsem
(int semid,
int nums,
int initval)
return0;
}static
intcommpv
(int semid,
int who,
int op)
return0;
}intp(
int semid,
int who)
intv
(int semid,
int who)
intdestroysemset
(int semid)
}
test_sem.c
#include
"comm.h"
intmain()
}else
wait
(null);
}destroysemset
(semid)
;return0;
}
此時,顯示器只有乙個,兩個程序同時列印,顯示器成為臨界資源,使用二元訊號量進行保護。
結果如下:
如果沒有pv操作列印就會亂序
在我們重新執行的時候會出現如下問題:
這是因為我們的訊號量已經被建立。我們可以使用 ipcs -s 檢視,然後使用ipcrm -s 刪除即可
訊號量,PV操作
它從整型訊號量 記錄型訊號量,進而發展為 訊號量集 機制 訊號量集,就是訊號量的集合 現在要用的是記錄型訊號量 1,訊號量幹嘛用的?訊號量 解決程序間同步與互斥問題 2.訊號量的組成 訊號量 分很多種,在此寫記錄型訊號量 record semaphore 訊號量組成 每個訊號量s除乙個整數值s.va...
訊號量同步 P V 操作
訊號是 e.w.dijkstra 在二十世紀六十年代末設計的一種程式設計架構。dijkstra 的模型與鐵路操作有關 假設某段鐵路是單線的,因此一次只允許一列火車通過。訊號將用於同步通過該軌道的火車。火車在進入單一軌道之前必須等待訊號燈變為允許通行的狀態。火車進入軌道後,會改變訊號狀態,防止其他火車...
PV操作和訊號量
乙個程序被分為了 就緒 ready 執行 running 和 阻塞 blocking 三個工作狀態,當前用處理器的哪個程序是 執行 狀態,當前已經具備了使用處理器的條件而等待處理器的程序是處於就緒狀態的程序,當執行的程序由於某種原因無法繼續使用處理器的時候就停止他使用處理器使他進入 阻塞 狀態,當他...