信號燈不同於其他進程間通信方式,信號燈主要負責控制進程對共享資源的訪問,除了用於對共享資源的訪問控制外,信號燈還可以用於進程間同步。
信號燈的相關函數聲明在"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纔會繼續運行,這就是一個通過信號量同步的過程,謝謝觀看。