信號量
信號量是一個特殊的整數值,主要用來控制多個進程對臨界資源的互斥訪問,進程根據信號量來判斷是否有 訪問的資源,這與前面所講的信號是不同的,信號是一種處理異步事件的方法,而信號量是一種進程同步機制,信號與信號量是兩個不同的東西。
信號量是一個計數器,可用於同步多進程對共享數據對象得訪問,爲了獲得共享資源,進程需要執行以下操作:
1、測試控制該資源的信號量
2、若此信號量的值爲正,則進程可以使用該資源,進程將信號量值減1,表示它使用了一個資源單位
3、若此信號量的值爲0,則進程進入睡眠狀態,直至信號量值大於0。當進程被喚醒後,它返回至第1步。
當進程不再使用由一個信號量控制得共享資源時,該信號量值增1.如果有進程正在睡眠以等待此信號量,則喚醒他們,爲了正確地實現信號量,信號量值得測試及減1操作應當是原子操作,爲此,信號量通常是在內核中實現得。
常用的信號量一般初始值爲1,只控制單個資源,有時也稱互斥鎖,但是,信號量得初值可以是任意一正值,該值說明有多少個共享資源單位可供共享應用,信號量有以下3個特性:
1、信號量並非是一個非負值,而必須將信號量定義爲含有一個或多個信號量值得集合,當創建一個信號量時,要指定該集合中的各個值。(linux/sem.h struct semid_ds)
struct sem
{
ushort_t semvl;
short sempid;
ushort semncnt;
ushort semzcnt;
};
2、創建信號量對其賦初值分開,這是一個致命弱點,因爲不能原子地創建一個信號量集合,並且對該集合中的所有值賦初值。
3、即使沒有進程使用,但他們仍然存在,因此必須考慮在進程終止時有沒有釋放得信號量。
以上的三個特性就導致了信號使用的複雜性。
信號量的值通過P、V原語來進行操作改變的。
在Linux中,系統提供了信號量的操作函數,主要有以下函數:
◆key_t ftok(char *pathname, char proj);
根據參數pathname和proj 來創建一個關鍵字,成功時返回與路徑pathname相對應的一個鍵值,具有唯一性,失敗時返回值爲-1.
◆int semget (key_t key, int nsems , int semflg);
創建一個新信號量或者取得一個現有的信號量,key是一個關鍵字,可以是用ftok()函數創建的,也可以是IPC_PRIVATE(/usr/include/bits$ vi ipc.h
),nsems表明創建的信號量個數,semflg是設置信號量的訪問權限標誌,函數調用成功時返回信號量ID,失敗則返回-1.
◆int semop (int semid, struct sembuf *spos, int nspos);
對信號量進行操作的函數,用於改變信號量的鍵值,semid是信號量的標誌,spos是指向一個結構體數組的指針,表明要進行什麼操作,nspos表明數組的元素個數,調用成功則返回0,失敗則返回-1.
/usr/include/linux$ vi sem.h (sembuf \ semun)
Struct sembuf (/usr/include/sys$ vi sem.h)
{
Unsigned short sem_num; /*sem index in array*/
Short sem_op; /* sem operation */
Short sem_flg;/* operation flags */ sem_flg&IPC_RND 0
};
其中,如果sem_op大於0,那麼操作值加入到信號量的值中,並喚醒等待信號增加的進程,如果sem_op爲0,當信號量的值是0的時候,函數返回,否則阻塞直到信號量的值爲0,如果sem_op小於0,函數判斷信號量的值加上這個負值,如果結果爲0喚醒等待信號量爲0的進程,如果小於0函數阻塞,如果大於0,那麼從信號量裏面減去這個值並返回。
◆int semctl (int semid, int semnum, int cmd, union semun arg);
該函數得作用是對信號量進行一系列得控制,semid是要操作得信號量標誌,semnum是信號量得下標,cmd是操作的命令,經常用的兩個命令是:SETVAL、IPC_RMID,arg用於設置或返回信號量信息。
Union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
void *__pad;
}
消息隊列
消息隊列是將消息按隊列的方式組織成的鏈表,每個消息都是其中的一個節點。
消息隊列的運行方式與命名管道非常相似。欲與其他進程通信的進程只要將消息發送到消息隊列中,目的進程就從消息隊列中讀取需要的消息。需要注意的是,消息隊列的長度以及每個消息的大小都是有限制的。
Linux系統提供的消息隊列操作函數主要有以下幾個:
int msgget(key_t key,int msgflg);
int msgsnd(int msqid, const void *msgptr, int msgsz,int msgflg);
int msgrcv(int msqid, void *msgptr, int msgsz, long msgtyp, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgget()函數與信號量的semget()函數相似,作用是創建一個消息隊列。參數key是一個鍵值,可由用戶設定也可通過ftok()函數獲得。Msgflg參數設置的是一些標誌位,可以是IPC_CREAT、IPC_EXCL、IPC_NOWAIT中的一個或者他們的組合。創建成功則返回消息隊列ID;否則返回-1。
Msgsnd()函數的作用是將消息發送到消息隊列中去。Msqid爲消息隊列ID。Msgptr是指想要發送的消息的指針,並且指向的緩衝區的第一個字段應爲長整形,指定消息類型,消息內容存放在該緩衝區的緊跟消息類型字段的區域中。Msgsz是要發送的消息的長度。Msgflg與msgget()函數中的msgflg參數設置類似,設置當消息隊列滿等情況出現時的處理方式,如果msgflg設置爲IPC_NOWAIT,則不發送消息並且立即返回-1;否則發送進程掛起等待。
如果msgsnd()函數調用成功,就會把消息複製到消息隊列中去並返回0;否則返回-1。
Msgrcv()函數的作用是從消息隊列中讀取一個消息。Msqid是消息隊列的ID。Msgptr保存從消息隊列中讀到的消息。Msgsz是msgptr指向的消息的長度。Msgtyp指定要求的消息類型,見表
Msgrcv()函數msgtyp參數說明
Msgtyp取值 | 說明 |
大於0 | 接收消息隊列中類型爲msgtyp的第一個可用報文 |
等於0 | 接收消息隊列中的第一個可用報文 |
小於0 | 接收消息隊列中小於或等於msgtyp絕對值的第一個可用報文 |
Msgflg的設置與msgsnd()函數中 的參數msgflg設置類似,用於設置如何處理當前消息隊列中沒有滿足條件的消息的情況。
如果msgrcv()函數調用成功,則返回讀出的實際字節數;否則返回-1.
Msgctl()函數是消息隊列的控制函數,類似於信號量的控制函數semctl()。Msqid是消息隊列的ID。Cmd是要採取的控制操作,有3個可取值,見表
參數值 | 說明 |
IPC_SET | 設置消息隊列的屬性,將buf指向的結構體中的數值設置爲消息隊列的相關性 |
IPC_STAT | 獲取消息隊列的屬性信息並保存到buf指向的結構體中 |
IPC_RMID | 移除ID爲msqid的消息隊列 |
共享內存
共享內存是系統創建的特殊地址空間,允許不相關的多個進程使用這個內存空間,即多個進程能夠使用同一塊內存中的數據。
共享內存與其他進程通信方式相比較,不需要複製數據,直接讀寫內存,是一種效率非常高的進程通信方案。但它本身不提供同步訪問機制,需要我們自己控制。在LINUX中,只要把共享內存段連接到進程的地址空間中,這個進程就可以訪問共享內存中的地址了。
LINUX系統提供的共享內存操作函數與信號量、消息隊列等類似,主要有以下幾個:
(1) int shmget(key_t key,int shmsz,int shmflg);
(2) void *shmat(int shmid,const void *shmaddr, int shmflg);
如果shmaddr爲0 則此段連接到由內核選擇的第一個可用地址上,這是推薦的使用方式
如果shmaddr非零,並且沒有指定SHM_RND,則此段鏈接到addr所指的地址上
如果shmaddr非零且指定SHM_RND,則此段鏈接到shmaddr - (addr mod ulus SHMLBA)所表示的地址上。SHM_RND的意思是低邊界地址倍數,它總是2的乘方。該算式是將地址向下取最近的一個SHMLBA的倍數
(3) int shmdt (const void *shmaddr);
(4) int shmctl(int shmid, int cmd,struct shmid_ds *buf);
以上各函數含義如下:
l Shmget()函數分配一塊新的共享內存。Shmsz指明共享內存的大小,以字節爲單位,shmflg的設置與信號量的semget()函數中的參數semflg類似。
如果shmget()函數調用成功則返回共享內存的ID;否則返回-1.
l Shmat()函數的作用是連接共享內存與某個進程的地址空間。Shmid是shmget()函數返回的共享內存ID。Shmaddr是共享內存連接到進程中的存放地址,一般設置爲空指針,表示交由系統完成這個工作。Shmflg設置共享內存的控制選項,有兩個可能取值:SHM_RND(與shmaddr參數相關)與SHM_RDONLY(只允許讀)。如果shmat()函數調用成功則返回指向共享內存的指針;否則返回-1.
l Shmdt()函數用來解除進程與共享內存區域的關聯,使當前進程不能繼續訪問共享內存。參數shmaddr是shmat()函數返回的指針。如果操作成功則返回0;失敗則返回-1.
l Shmctl()函數實現對共享內存區域的控制操作。其用法與消息隊列的msgctl()函數類似。
sem. msg. shm 都是創建在/dev/shm目錄下
ipcs
ipcs -s ipcs -q ipcs -m
ipcrm
ipcs -s -i sem_id