1. 程式人生 > >程序間通訊之——訊號量(一)(system V)

程序間通訊之——訊號量(一)(system V)

system v訊號量又被稱為system v訊號量集。它的本質就是一個計數器,用來為多個程序共享的資料結構提供受控訪問。

訊號量支援的操作有:


使用最廣泛的訊號量為二元訊號量,它控制單個資源,對於這種訊號量而言,它只有兩種合法值: 0 和 1 ,對應一個可用的資源。若當前有資源可用,則與之對應的二值訊號量的值為 1 ;若資源已被佔用,則與之對應的二值訊號量的值為 0 。當程序申請資源時,如果當前訊號量的值為 0 ,那麼程序會陷入阻塞,直到有其他程序釋放資源,將訊號量的值加 1 才能被喚醒。

核心為每個訊號量集維護者一個semid_ds結構,該結構定義了此訊號量的許可權、指標、最近修改時間和佇列中訊號量佇列資訊:/usr /src/kernels/2.6.32-431.el6.i686/include/Linux/sem.h


——sem_perm許可權;

——sem_otime最近semop時間;

——sem_ctime最近修改時間;

——sem_base佇列第一個訊號量;

——sem_pending阻塞訊號量;

——sem_pending_last最後一個阻塞訊號量;

——undo表示undo佇列;

——sem_nsems表示訊號量個數;

每一個訊號量還有一個結構:


——semval表示訊號量值;

——sempid表示最近一個操作的程序號pid;

當然訊號量仍然存在系統限制


——SEMMNI系統容許的訊號量集的上限;

——SEMMSL 單個訊號量集中訊號量的上限;

——SEMMNS 系統容許的訊號量的上限

——SEMOPM 單次 semop 呼叫能夠操作的訊號量的最大值;

——SEMVMX 訊號量值的上限;

訊號量集函式

semget

功能:用來建立或開啟一個訊號量集;

原型:int semget(key_t key, int nsems, int semflg);

引數:key表示訊號集的名字,一般由ftok函式產生;

         nsems表示訊號集中訊號量的個數;

         semflg用來標識訊號量集合的許可權,和建立檔案類似,由九個許可權標誌為構成如0644,他還可以和以下引數一起使用:


——IPC_CREAT表示如果key不存在就建立;

——IPC_EXCL表示如果key存在就返回失敗;

——IPC_NOWAIT表示如果需要等待,則直接返回錯誤;

返回值成功

返回一個非負整數即該訊號量的標識碼;失敗返回-1;

semctl

功能:用來控制訊號量集;

原型:int semctl(int semid, int semnum, int cmd, ...);

引數:semid由semget返回的訊號量標識;

          semnum訊號集中訊號量的序號;

          cmd表示將要採取的操作;

          最後一個引數根據命令不同而不同,他是一個型別為senum的聯合(下文會有介紹);

cmd可採取的操作有:IPC_RMID、IPC_SET、IPC_STAT和IPC_INFO,定義在:/usr/include/linux/ipc.h中


IPC_RMID、IPC_SET、IPC_STAT和IPC_INFO這四個引數都是system v的通用引數;

——IPC_RMID表示刪除訊號集

——IPC_STAT表示獲取ipc_perm的引數

——IPC_INFO表示獲取系統資訊

——IPC_SET表示設定ipc_prem的引數,對於這個引數,semctl有單獨的規定引數


——GETPID獲取訊號量擁有者的pid的值;

——GETVAL獲取訊號量的值;

——GETALL獲取所有訊號量的值;

——GETNCNT獲取等待訊號量的值遞增的程序數;

——GETZCNT獲取等待訊號量的值遞減的程序數;

——SETVAL設定訊號量的值;

——SETALL設定所有訊號的值;

第四個引數的senum的聯合體定義如下:


接下來介紹幾個常用的操作

|cmd操作為GETVAL

semctl第二個引數為訊號量編號,若執行成功,semctl返回當前訊號量的值,失敗返回-1;

|cmd操作為SETVAL

semctl第二個引數為訊號量編號,第四個引數為要設定的val

返回值:成功返回0;失敗返回-1;

semop

功能:修改集合中一個或多個訊號量值;

原型:int semop(int semid, struct sembuf *sops, unsigned nsops);

引數:semid訊號領=量標識碼;

          sops是一個sembuf型別的指標;

          nsops標識訊號量個數;

sembuf結構體定義如下:


——sem_num標識訊號量編號;

——sem_op訊號量一次pv操作時加減的數值,一般會用到兩個值:

-1,p操作,等待訊號量變得可用

 +1,v操作,發出的訊號量變得可用

——sem_flg操作標識,有以下值: IPC_NOWAIT和SEM_UNDO

       IPC_NOWAIT對某一訊號量操作,即使其中一個操作失敗,也不會導致修改其他訊號量的值;

SEM_UNDO當程序退出後,該程序對sem進行的操作將被撤銷;


返回值成功返回0;失敗返回-1;

測試案例一:哲學家就餐問題

問題描述:

(由Dijkstra首先提出並解決)5個哲學家圍繞一張圓桌而坐,桌子上放著5支筷子,每兩個哲學家之間放一支;哲學家的動作包括思考和進餐,進餐時需要同時拿起他左邊和右邊的兩支筷子,思考時則同時將兩支筷子放回原處。如何保證哲學家們的動作有序進行?如:不出現相鄰者同時要求進餐;不出現有人永遠拿不到筷子;

解決思路:五隻筷子相當於資源,保證每次會有兩位哲學家先進餐,進餐完後在換其他哲學家,能夠保證五位哲學家都能進餐

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<string.h>

int semid;

union semun{
	int value;
};
//獲得資源
void get_sour(int num)
{
	struct sembuf sb[2]={
		{num,-1,0},
		{(num+1)%5,-1,0}
	};
	semop(semid,sb,2);
}

//釋放資源
void free_sour(int num)
{
	struct sembuf sb[2]={
		{num,1,0},
		{(num+1)%5,1,0}
	};
	semop(semid,sb,2);

}

void phil(int num)
{
	while(1){
		printf("%d is thinking...\n",num);
		sleep(rand()%5);
		get_sour(num);
		printf("%d start eating...\n",num);
		sleep(rand()%3);
		printf("%d end eating...\n",num);
		free_sour(num);
	}
}

int main()
{
	srand(getpid());
	semid=semget(1234,5,IPC_CREAT|0600);//建立訊號量
	if(semid == -1){
		perror("semget");
		exit(1);
	}
	union semun s;
	s.value=1;
	//五個訊號量分別設初值
	int i=0;
	for(i=0;i<5;i++){
		semctl(semid,i,SETVAL,s);
	}
	//建立五個程序——一個父程序+4個子程序
	int num=0;//為每個程序編號,父程序初始化編號為0
	for(i=1;i<5;i++){
		pid_t pid=fork();
		if(pid == 0){
			num=i;
			break;
		}
	}


	phil(num);
}

執行結果:


本文主要介紹了system v訊號量的一些基本概念,目的是能夠對訊號量有一個基本的初步認識,文中如有不當之處,歡迎大家指正。