linux程序通訊-訊號量
訊號量:是一個計數器,用於為多個程序提供對共享資源的訪問。訊號量是用來調協程序對共享資源的訪問的。為了防止出現因多個程式同時訪問一個共享資源而引發的一系列問題,通過生成並使用令牌來授權,在任一時刻只能有一個執行執行緒訪問程式碼的臨界區域。臨界區域是指執行資料更新的程式碼需要獨佔式地執行,而訊號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個執行緒在訪問它。
pv操作:訊號量S的物理意義,S>=0時表示某資源的可用數,s<0時其絕對值表示阻塞佇列中等待該資源的程序數。P、V操作是實現程序同步與互斥的常用方法。P操作表示申請一個資源,V操作表示釋放一個資源。 P操作的定義:S=S-1,若S>=0,則執行P操作的程序繼續執行;若S<0,則置該程序為阻塞狀態,並將其插入阻塞佇列。
1.函式semget 我們使用函式semget來建立一個訊號量
#include<sys/sem.h>
int semget(key_t key,int nsems,int flag);
返回值:成功返回訊號量ID,失敗返回-1
引數nsems:是該集合中的訊號數量,如果是引用現有集合,nsms設定為0. 引數flags:許可權。
2.函式semctl
#include<sys/sem.h>
int semctl(int semid,int semnum,int cmd,.......)
引數semid:是之前建立的訊號量集的ID; 引數semnum:訊號量中的某個成員,0和nsems之間的一個值,包括0和nsems-1 引數:cmd是命令,有10個。常用的SETVAL表示設定訊號量集中的一個單獨的訊號量的值,IPC_RMID將訊號量集從記憶體中刪除。 第四個引數是可選的,是否使用跟cmd命令有關。它的型別是一個聯合semun
union semun{
int val; //使用SETVAL設定訊號量的值,就使用它賦值
struct semids_ds *buf; //為命令IPC_STAT和IPC_SET
unsigned short *array; //為命令GETALL 和SETALL
}
3函式semop semop函式自動執行訊號量集合上的運算元組,pv操作就是在這裡實現的。
#inlcude<sys/sem.h> int semop(int semid,struct sembuf semoparray[],size_t nops); 返回值:成功返回0;出錯返回-1;
引數semoparray是一個指標,指向一個由sembuf結構表示的訊號量陣列;
struct sembuf{
unsigned short sem_num;
short sem_op;
short sem_flag;
};
undo標誌:(這個標誌對應sembuf結構體中sem_flag成員的SEM_UNDO位) SEM_UNDO用於將修改的訊號量值在程序正常退出(呼叫exit退出或main執行完)或異常退出(如段異常、除0異常、收到KILL訊號等)時歸還給訊號量。
對集合中訊號的量的操作由sem_op值確定,它可以是負數,0或正數。 sem_op為正值:程序釋放佔用的資源數。sem_op的值加到訊號量的值上。 sem_op為負值:則表示要獲取該訊號量控制的資源。如果該訊號量的值大於sem_op的絕對值:則從該訊號量中減去sem_op的值。 如果訊號量值小於sem_op的絕對值(資源不能滿足要求): 1)若指定了IPC_NOWAIT,則semop出錯返回 2)如果沒有指定IPC_NOWAIT,程序進入休眠,直到條件滿足
例程
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
}
struct sem_buf sem_b;
int main()
{
int sem_id;
union semun sem_union;
char message='c';
sem_id=semget((key_t)1234,1,0666|IPC_CREAT); //建立一個訊號量
sem_union.val=1;
semctl(sem_id,0,SETVAL,sem_union); //初始化一個訊號量
for(int i=0;i<10;i++)
{
sem_b.sem_num=0; //進入臨界區,p操作
sem_b.sem_op=-1;
sem_semflg=SEM_UNDO;
semop(sem_id,&sem_b,1);
printf("hello");
fflush(stdout);
sleep(1);
sem_b.sem_op=1; //離開臨界區,v操作
semop(sem_id,&sem_b,1);
sleep(2);
}
semctl(sem_id, 0, IPC_RMID, sem_union); //刪除訊號量
return 0;
}
程式碼若錯誤,請指正,謝謝!