1. 程式人生 > >Linux進程間通信-信號量

Linux進程間通信-信號量

它的 linux進程 通信 集中 rac data 整數 sdn size

當多個進程表同一時候訪問系統上的某個資源的時候,比方同一時候寫一個數據庫的某條記錄,或者同一時候改動某個文件,就須要考慮進城的同步問題,以確保任一時刻僅僅有一個進程能夠擁有對資源的獨占式訪問。

通常。程序對共享資源的訪問的代碼僅僅是非常短的一段。你就是這一段代碼引發了進程之間的競態條件。我們稱這段代碼為關鍵代碼段,或者臨界區。

信號量是一種特殊的變量,它僅僅能取自然數並僅僅支持兩種操作:等待(wait)和信號(signal),這兩種操作更常見的稱呼是P、V操作。

如果有信號量SV。則對它的P、V操作含義例如以下:

l P(SV),假設SV的值大於0。就將它減1:;假設SV的值為0。則掛起進程的運行。

l V(SV),假設有其它進程由於等待SV二掛起,,則換星之。假設沒有,則將SV加1。

信號量API主要包括3個系統調用:semget、semop和semctl。

它們都被設計為操作一組信號量,即信號量集,而不是單個信號量。

#include <sys/sem.h>
int semget(key_t key, int num_sems, int sem_flags);
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);
int semctl(int sem_id, int sem_num, int command, ...);

l semget函數調用創建一個新的信號量集或者獲得一個存在的信號量。key參數是一個鍵值,用來標識一個全局唯一的信號量集。就像文件名稱全局唯一的標識一個文件一樣。

要通過信號量通信的進程須要使用同樣的鍵值來創建或獲取該信號量。

num_sems參數指定要創建或獲取的信號量集中信號量的數目。假設是創建信號量。則該值必須指定;假設是獲取已存在的信號量,則能夠設置為0

sem_flags參數指定一組標誌。它低端的9個比特是該信號量的權限,其格式和含義都與系統調用open的mode參數同樣。我們能夠和IPC_CREAT標誌做按位“或”運算,此時即使信號量存在。semget也不會報錯。我們還能夠用IPC_CREAT和IPC_EXCL標誌來確保創建一組新的、唯一的信號量集,此時若信號量存在,semget返回錯誤並設置errno為EEXIST。

semget調用成功時返回一個正整數,它是信號量集的標識符;失敗時返回-1,並設置errno


l semop函數改變信號量的值。即運行P和V操作

sem_id參數是由semget調用返回的信號量集標識符,用以指定被操作的目標信號量集。

sem_ops參數指向一個sembuf結構體的數組,定義例如以下:

struct sembuf
{
    unsigned short int sem_num;
    short int sem_op;
    short int sem_flg;
};

sem_num是信號量集中信號量的編號,像數組一樣,從0開始。

sem_op指定操作類型,可選值為正整數、0、負整數。每種類型的操作行為又受到sem_flg的影響。

sem_flg的可選值是IPC_NOWAIT、SEM_UNDO。IPC_NOWAIT指不管信號量操作是否成功,semop調用都將馬上返回,類似於非堵塞。

SEM_UNDO指當進程退出時取消正在進行的semop操作。

num_sem_ops參數指定要運行的操作個數,即sem_ops數組中無素的個數。semop對數組中的每一個成員按數組順序依次運行,該過程是原子操作。

semop成功時返回0,失敗時返回-1並設置errno


l semctl函數同意調用者直接對信號量直接操作

sem_id參數是由semget調用返回的信號量集標識符,用以指定被操作的信號量集。

sem_num參數指定被操作的信號量在信號量集中的編號。

command參數指定要運行的命令。

有的命令須要第4個參數。

其由用戶自定義。但sys/sem.h中給出了推薦格式:

union semun
{
    int val;                         //用於SETVAL命令
    struct semid_ds *buf;             //用於IPC_STAT和IPC_SET命令
    unsigned short *array;            //用於GETALL和SETALL命令
    struct seminfo *__buf;            //用於IPC_INFO命令
};
sem_ctl成功時的返回值取決於command參數,失敗時返回-1並設置errno


特殊鍵值 IPC_PRIVATE(semget函數的key參數)

semget調用者能夠給其Key參數傳遞一個特殊的鍵值IPC_PRICATE(其值為0)。這樣不管該信號量是否已經存在,semget都將創建一個新的信號量。使用該鍵值創建的信號量並不是像其它的名字聲稱的那樣是進程私有的。其它進程,尤其是子進程,也有方法來訪問這個信號量

使用IPC_PRIVATE的程序演示樣例

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

#define err_sys(msg) 	do { perror(msg); exit(-1); } while(0)

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo * __buf;
};

void pv(int sem_id, int op)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = op;
	sem_b.sem_flg = SEM_UNDO;
	semop(sem_id, &sem_b, 1);
}

int main(void)
{
	int sem_id = semget(IPC_PRIVATE, 1, 0666); /* 該信號量集大小為1 */

	union semun sem_un;
	sem_un.val = 1; /* 該該值為2時,表示能夠同一時候兩個進程運行P操作(即進行-1操作) */
	semctl(sem_id, 0, SETVAL, sem_un); /* 對信號量集中第一個信號進行操作 */

	pid_t pid = fork();
	if(pid < 0)
		err_sys("fork");
	else if(pid == 0)
	{
		pv(sem_id, -1);
		printf("child get the sem and would sleep 5s.\n");
		sleep(5);
		pv(sem_id, 1);
		exit(0);
	}
	else
	{
		pv(sem_id, -1);
		printf("parent get the sem and would sleep 5s.\n");
		sleep(5);
		pv(sem_id, 1);
	}

	waitpid(pid, NULL, 0);
	semctl(sem_id, 0, IPC_RMID, sem_un);

	return 0;
}

參考:

1、《Linux高性能server編程》 第13章 多進程編程/信號量

2、Linux多進程編程

Linux進程間通信-信號量