Linux進程學習之:信號燈

信號燈不同於其他進程間通信方式,信號燈主要負責控制進程對共享資源的訪問,除了用於對共享資源的訪問控制外,信號燈還可以用於進程間同步。

信號燈的相關函數聲明在"sys/sem.h"頭文件中

信號燈的創建需要用到semget函數,聲明如下:

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

第一個參數可以是IPC_PRIVATE宏,或者是ftok函數生成的key,IPC_PRIVATE宏用於具有親緣關係的進程間通信,ftok函數生成的key用於非情緣關係的進程間通信

第二個參數是生成的信號量的個數

第三個參數是對生成的信號燈讀寫權限,一般使用0777

返回值:

If  successful,  the  return  value  will be the semaphore set identifier (a nonnegative integer), 
otherwise, -1 is returned, with errno indicating the error.

成功返回信號燈的id,失敗返回-1

信號燈控制用的是semctl函數,聲明如下:

int semctl(int semid, int semnum, int cmd, ...);

第一個參數是信號燈id

第二個參數是要設置的信號燈序號,如果要刪除信號燈的話,輸入0就可以,因爲這個參數在執行刪除命令的時候會被忽略

第三個參數是對信號燈執行的命令,刪除命令是IPC_RMID

返回值:

On failure, semctl() returns -1 with errno indicating the error.
return 0 on success.

成功返回0,失敗返回-1

接下來貼一個創建信號燈又刪除信號燈的代碼:

#include <iostream>
#include <sys/sem.h>
#include <stdlib.h>

using namespace std;

int main() {
	int semid = semget(IPC_PRIVATE, 3, 0777);
	if (semid == -1) {
		cout << "Create semaphore failed" << endl;
		return -1;
	}
	else {
		cout << "Create semaphore successfully, semid = " << semid << endl;
	}
	system("ipcs -s");
	int ret = semctl(semid, 0, IPC_RMID);
	if (ret == -1) {
		cout << "destroy semaphore failed" << endl;
	}
	else {
		cout << "destroy semaphore successfully" << endl;
	}
	system("ipcs -s");
	return 0;
}

信號燈的初始化,使用semctl函數,第一個參數是shmid,第二個參數是操作的信號燈集合中的信號量編號,第三個使用SETVAL命令,使用了SETVAL命令需要定義一個結構體,這個官方有要求,照着寫一個就好了,初始化的時候,只需要初始化semval就好了

信號量的操作使用的是semop函數,這個函數比較複雜,不過官方文檔寫的很清楚,大家自己去看吧,在命令行輸入man semop就可以看到很詳細的函數介紹啦

貼一個非血緣關係進程間通過信號燈通信的代碼:

首先是server端

#include <iostream>
#include <sys/sem.h>
#include <stdlib.h>

using namespace std;

union semun {
   int              val;    /* Value for SETVAL */
   struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
   unsigned short  *array;  /* Array for GETALL, SETALL */
   struct seminfo  *__buf;  /* Buffer for IPC_INFO
                               (Linux-specific) */
};

int main() {
	semun mysemun;
	struct sembuf mysembuf;
	int key = ftok("./a", 1);
	if (key < 0) {
		cout << "Create key failed" << endl;
		return -1;
	}
	else {
		cout << "Create key successfully, key = " << key << endl;
	}
	int semid = semget(key, 3, IPC_CREAT | 0777);
	if (semid == -1) {
		cout << "Create semaphore failed" << endl;
		return -1;
	}
	else {
		cout << "Create semaphore successfully, semid = " << semid << endl;
	}
	system("ipcs -s");
	for (int i = 0; i < 5; i++) {
		cout << "server print: " << i << endl;
	}
	mysembuf.sem_num = 0; // 表示對信號量集合中0號信號量操作
	mysembuf.sem_op = 1;  // 如果是sem_op是正整數,將會把這個值加到semval這個變量上
	mysembuf.sem_flg = 0; // 以阻塞方式運行
	semop(semid, &mysembuf, 1); // 第三個參數代表對數量爲1的信號量操作
	int ret = semctl(semid, 0, IPC_RMID);
	if (ret == -1) {
		cout << "destroy semaphore failed" << endl;
	}
	else {
		cout << "destroy semaphore successfully" << endl;
	}
	system("ipcs -s");
	return 0;
}

然後是client端

#include <iostream>
#include <sys/sem.h>
#include <stdlib.h>

using namespace std;

union semun {
   int              val;    /* Value for SETVAL */
   struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
   unsigned short  *array;  /* Array for GETALL, SETALL */
   struct seminfo  *__buf;  /* Buffer for IPC_INFO
                               (Linux-specific) */
};

int main() {
	semun mysemun;
	struct sembuf mysembuf;
	int key = ftok("./a", 1);
	if (key < 0) {
		cout << "Create key failed" << endl;
		return -1;
	}
	else {
		cout << "Create key successfully, key = " << key << endl;
	}
	int semid = semget(key, 3, IPC_CREAT | 0777);
	if (semid == -1) {
		cout << "Create semaphore failed" << endl;
		return -1;
	}
	else {
		cout << "Create semaphore successfully, semid = " << semid << endl;
	}
	system("ipcs -s");
	// 初始化信號量的值需要用到SETVAL命令,下面的參數意思是:
	// 給信號燈id等於semid的信號燈中0號信號量設置一個值,這裏設置
	// mysemun.val = 0, 代表的意思就是把0號信號量的值設爲0
	mysemun.val = 0;
	semctl(semid, 0, SETVAL, mysemun);
	mysembuf.sem_num = 0; // 表示對信號量集合中0號信號量操作
	mysembuf.sem_op = -1; // sem_op值的絕對值是1,大於semval=0,進程睡眠並等待semval的值大於或等於sem_op的絕對值
	mysembuf.sem_flg = 0; // 以阻塞方式運行
	semop(semid, &mysembuf, 1); // 第三個參數代表對數量爲1的信號量操作
	for (int i = 0; i < 5; i++) {
		cout << "Client print: " << i << endl;
	}
	system("ipcs -s");
	return 0;
}

以上代碼編譯好後,先運行client端代碼,可以看到client端進程被阻塞,等server端運行起來後,client纔會繼續運行,這就是一個通過信號量同步的過程,謝謝觀看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章