天天看點

XSI程序間通信---信号量

1. 基本特點

 1) 相當于計數器,用于限制多個程序對有限共享資源的通路。

 2) 多個程序擷取有限共享資源的操作模式

   A. 測試控制該資源的信号量;

   B. 若信号量大于0,則程序可以使用該資源, 為了表示此程序已獲得該資源,需将信号量減1;

   C. 若信号量等于0,則程序休眠等待該資源, 直到信号量大于0,程序被喚醒,執行步驟A;

   D. 當某程序不再使用該資源時,信号量增1,  正在休眠等待該資源的其它程序将被喚醒。

 3)核心維護一個semid_ds結構體

struct semid_ds {
    struct ipc_perm sem_perm;  // 權限資訊
    time_t          sem_otime; // 上次執行 semop 的時間
    time_t          sem_ctime; // 最後更新時間
    unsigned short  sem_nsems; // 在信号量集合裡的索引
};
           
struct ipc_perm {
    key_t          __key; // 鍵值
    uid_t          uid;   // 有效屬主ID
    gid_t          gid;   // 有效屬組ID
    uid_t          cuid;  // 有效建立者ID
    gid_t          cgid;  // 有效建立組ID
    unsigned short mode;  // 權限字
    unsigned short __seq; // 序列号
};
           

2. 常用函數

1) 建立/擷取信号量

int semget (key_t key, int nsems, int semflg);
           

A. 該函數以key參數為鍵值建立一個信号量集合  (nsems參數表示集合中的信号量數 ,或擷取已有的信号量集合(nsems取0)。

B. semflg取值:

  0         - 擷取,不存在即失敗。

  IPC_CREAT - 建立,不存在即建立,已存在即擷取,除非...

  IPC_EXCL  - 排斥,已存在即失敗。

C. 成功傳回信号量集合辨別semid,失敗傳回-1。

2) 操作信号量:它的作用是改變信号量的值

int semop (int semid, struct sembuf* sops,unsigned nsops);
           
struct sembuf {
    unsigned short sem_num; // 信号量下标
    short          sem_op;  // 操作數
    short          sem_flg; // 操作标記
};
           

A. 該函數對semid參數所辨別的信号量集合中, 由sops參數所指向的包含nsops個元素的, 結構體數組中的每個元素,依次執行如下操作:

   a) 若sem_op大于0, 則将其加到第sem_num個信号量的計數值上,以表示對資源的釋放;

   b) 若sem_op小于0,則從第sem_num個信号量的計數值中減去其絕對值,以表示對資源的擷取;

   c) 若第sem_num個信号量的計數值不夠減(信号量不能為負), 則此函數會阻塞,直到該信号量夠減為止,以表示對資源的等待;

   d) 若sem_flg包含IPC_NOWAIT位,則當第sem_num個信号量的計數值不夠減時, 此函數不會阻塞,而是傳回-1,errno為EAGAIN,以便在等待資源的同時還可做其它處理;

   e) 若sem_op等于0,則直到第sem_num個信号量的計數值為0時才傳回, 除非sem_flg包含IPC_NOWAIT位。

B. 成功傳回0,失敗傳回-1。

3) 銷毀/控制信号量

int semctl (int semid, int semnum, int cmd);
int semctl (int semid, int semnum, int cmd,union semun arg);
           

函數描述:semctl() 在 semid 辨別的信号量集上,或者該集合的第 semnum 個信号量上執行 cmd 指定的控制指令。(信号量集合索引起始于零。)

A. senum :信号集的索引,用來存取信号集内的某個信号

union semun { 
         int val;               // SETVAL使用的值   
         struct semid_ds *buf;  // IPC_STAT、IPC_SET 使用緩存區
         unsigned short *array; // GETALL,、SETALL 使用的數組 
         struct seminfo *__buf; // IPC_INFO(Linux特有) 使用緩存區 
}; 
           

B. cmd取值:

   IPC_STAT - 擷取信号量集合的屬性,通過arg.buf輸出。

   IPC_SET  - 設定信号量集合的屬性,通過arg.buf輸入,僅三個屬性可設定

   IPC_RMID - 立即删除信号量集合。  此時所有阻塞在對該信号量集合的,semop函數調用,都會立即傳回失敗,errno為EIDRM。

   GETALL   - 擷取信号量集合中每個信号量的計數值,  通過arg.array輸出。

   SETALL   - 設定信号量集合中每個信号量的計數值,通過arg.array輸入。

   GETVAL   - 擷取信号量集合中, 第semnum個信号量的計數值, 通過傳回值輸出。

   SETVAL   - 設定信号量集合中,第semnum個信号量的計數值, 通過arg.val輸入。

   注意:隻有針對信号量集合中具體某個信号量的操作,才會使用semnum參數。針對整個信号量集合的操作,會忽略semnum參數。

C. 成功傳回值因cmd而異,失敗傳回-1。

3. 程式設計模型

XSI程式間通信---信号量

這裡模拟一個簡單的借書還書的案例:

#include <stdio.h>
#include <errno.h>
#include <sys/sem.h>

int pleft (int semid) {
	int val = semctl (semid, 0, GETVAL);
	if (val == -1) {
		perror ("semctl");
		return -1;
	}

	printf ("還剩%d冊。\n", val);
	return 0;
}

int main (void) {
	printf ("建立信号量...\n");

	key_t key = ftok (".", 100);
	if (key == -1) {
		perror ("ftok");
		return -1;
	}

	int semid = semget (key, 1, 0644 | IPC_CREAT | IPC_EXCL);
	if (semid == -1) {
		perror ("semget");
		return -1;
	}

	printf ("初始信号量...\n");

	if (semctl (semid, 0, SETVAL, 5) == -1) {
		perror ("semctl");
		return -1;
	}

	int quit = 0;
	while (! quit) {
		printf ("--------\n");
		printf ("三國演義\n");
		printf ("--------\n");
		printf ("[1] 借閱\n");
		printf ("[2] 歸還\n");
		printf ("[0] 退出\n");
		printf ("--------\n");
		printf ("請選擇:");

		int sel = -1;
		scanf ("%d", &sel);

		switch (sel) {
			case 0:
				quit = 1;
				break;

			case 1: {
//				printf ("請稍候...\n");

				struct sembuf sops = {0, -1, /*0*/IPC_NOWAIT};
				if (semop (semid, &sops, 1) == -1) {
					if (errno == EAGAIN) {
						printf ("暫時無書,下回再試。\n");
						break;
					}
					else {
						perror ("semop");
						return -1;
					}
				}

				printf ("恭喜恭喜,借閱成功。\n");
				pleft (semid);
				break;
			}
			case 2: {
				struct sembuf sops = {0, 1, 0};
				if (semop (semid, &sops, 1) == -1) {
					perror ("semop");
					return -1;
				}

				printf ("好借好還,再借不難。\n");
				pleft (semid);
				break;
			}
			default:
				printf ("無效選擇!\n");
				scanf ("%*[^\n]");
				scanf ("%*c");
				break;
		}
	}

	printf ("銷毀信号量...\n");

	if (semctl (semid, 0, IPC_RMID) == -1) {
		perror ("semctl");
		return -1;
	}
	printf ("大功告成!\n");
	return 0;
}
           
#include <stdio.h>
#include <errno.h>
#include <sys/sem.h>

int pleft (int semid) {
	int val = semctl (semid, 0, GETVAL);
	if (val == -1) {
		perror ("semctl");
		return -1;
	}
	printf ("還剩%d冊。\n", val);
	return 0;
}

int main (void) {
	printf ("擷取信号量...\n");

	key_t key = ftok (".", 100);
	if (key == -1) {
		perror ("ftok");
		return -1;
	}

	int semid = semget (key, 0, 0);
	if (semid == -1) {
		perror ("semget");
		return -1;
	}

	int quit = 0;
	while (! quit) {
		printf ("--------\n");
		printf ("三國演義\n");
		printf ("--------\n");
		printf ("[1] 借閱\n");
		printf ("[2] 歸還\n");
		printf ("[0] 退出\n");
		printf ("--------\n");
		printf ("請選擇:");

		int sel = -1;
		scanf ("%d", &sel);

		switch (sel) {
			case 0:
				quit = 1;
				break;

			case 1: {
//				printf ("請稍候...\n");

				struct sembuf sops = {0, -1, /*0*/IPC_NOWAIT};
				if (semop (semid, &sops, 1) == -1) {
					if (errno == EAGAIN) {
						printf ("暫時無書,下回再試。\n");
						break;
					}
					else {
						perror ("semop");
						return -1;
					}
				}

				printf ("恭喜恭喜,借閱成功。\n");
				pleft (semid);
				break;
			}
			case 2: {
				struct sembuf sops = {0, 1, 0};
				if (semop (semid, &sops, 1) == -1) {
					perror ("semop");
					return -1;
				}

				printf ("好借好還,再借不難。\n");
				pleft (semid);
				break;
			}
			default:
				printf ("無效選擇!\n");
				scanf ("%*[^\n]");
				scanf ("%*c");
				break;
		}
	}
	printf ("大功告成!\n");
	return 0;
}
           
XSI程式間通信---信号量
XSI程式間通信---信号量

也可以結合這篇博文來一起學習 點選打開連結

繼續閱讀