semaphore
Prerequisites:
共用記憶體沒有mutex,所以使用semaphore來實現加鎖lock/解鎖unlock/等待wait/通知notify,以達到讀取相同記憶體時,搶到鎖的程序process就可以使用這個記憶體,而其它程序process就在門外排隊等待門的鎖打開,當鎖打開,就輪下一個程序process使用記憶體。
semaphore中文翻譯有太多種,訊號量(大陸),號誌(台灣),所以用英文代替。
linux semaphore指令
列出semaphore
$ ipcs -s
—— 號誌陣列 ——–
鍵值 semid 擁有者 perms nsems
0x00000001 0 cici 666 1
刪除semaphore
在進行本頁範例前,請先手工刪除semaphore,還有刪除共用記憶體,避免產生錯誤。
$ ipcrm sem 0
資源已刪除
0為semid
初始化
init()
1
bool init(key_t key, unsigned short value = 1, short sem_flg = SEM_UNDO);
- key: 使用16進位,0x開頭,後面4個數字,例:0x0001,可跟共用記憶體的key一樣
-
value: value有以下二種功能
- mutex: 1
- condition_variable: 0
-
sem_flg: value有以下二種功能
- mutex: SEM_UNDO為數字1
- condition_variable: 0
wait()
1
bool wait(short value = -1);
預設參數value = -1代表把semid減1,不是設成負1,是減1的意思。
value小於0,程式就卡在wait()這行不動,直到value大於0,執行wait()以後的程式碼
- mutex: 加鎖lock
- condition_variable: 等待wait
post()
1
bool post(short value = 1);
預設參數value = 1代表把semid加1,不是設成1,是加1的意思。
- mutex: 解鎖unlock
- condition_variable: 通知notify
檔名:semaph.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>
using namespace std;
class Semaph {
public:
// 初始化建構子,預設semid為-1,-1代表沒被初始化
// 若已經初始化semid將大於等於0
Semaph() : semid_(-1) {}
// 初始化
// key: 使用16進位,0x開頭,後面4個數字,例:0x0001,可跟共用記憶體的key一樣
// value: 若semaphore沒被建立過,就建立,並把value設為1
// value有二種功能,一種是mutex,一種是condition_variable
bool init(key_t key, unsigned short value = 1, short sem_flg = SEM_UNDO);
bool wait(short value = -1);
bool post(short value = 1);
// 取得semid
int getvalue();
bool destroy();
~Semaph();
private:
// union固定寫法
union semun_ {
int val_;
struct semid_ds *buf_;
unsigned short *arry_;
};
int semid_;
// sem_flg是0為mutex
// sem_flg是SEM_UNDO為condition_variable
short sem_flg_;
Semaph(const Semaph &) = delete; // 禁用拷貝
Semaph& operator=(const Semaph &) = delete; // 禁用assign
};
檔名:semaph.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "semaph.h"
bool Semaph::init(key_t key, unsigned short value, short sem_flg) {
if (semid_ != -1) return false;
sem_flg_ = sem_flg;
if ((semid_ = semget(key, 1, 0666)) == -1) {
if(errno == ENOENT) {
if((semid_ = semget(key, 1, 0666 | IPC_CREAT | IPC_EXCL)) == -1) {
if (errno == EEXIST) {
if ((semid_ = semget(key, 1, 0666)) == -1) {
perror("init 1 semget()");
return false;
}
return true;
} else {
perror("init 2 semget()");
return false;
}
}
union semun_ sem_union;
sem_union.val_ = value;
if (semctl(semid_, 0, SETVAL, sem_union) < 0) {
perror("init semctl()");
return false;
}
} else {
perror(" init 3 semget()");
return false;
}
}
return true;
}
bool Semaph::wait(short value) {
if (semid_ == -1) return false;
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = value;
sem_b.sem_flg = sem_flg_;
if (semop(semid_, &sem_b, 1) == -1) {
perror("p semop()");
return false;
}
return true;
}
bool Semaph::post(short value)
{
if (semid_==-1) return false;
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = value;
sem_b.sem_flg = sem_flg_;
if (semop(semid_, &sem_b, 1) == -1) {
perror("V semop()");
return false;
}
return true;
}
int Semaph::getvalue()
{
return semctl(semid_,0,GETVAL);
}
bool Semaph::destroy()
{
if (semid_==-1) return false;
if (semctl(semid_, 0, IPC_RMID) == -1) {
perror("destroy semctl()");
return false;
}
return true;
}
Semaph::~Semaph(){}
檔名:semaph_test.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "cir_queue.h"
#include "semaph.h"
#include <sys/shm.h>
#include <cstring>
using namespace std;
struct SharedData {
char data[100];
};
int main() {
int shmid = shmget(0x0001, sizeof(SharedData), 0640|IPC_CREAT);
if (shmid == -1) {
cout << "記憶體不足或權限不足,無法建立空間。" << endl;
return -1;
}
cout << "shmid = " << shmid << endl;
SharedData *ptr = (SharedData*)shmat(shmid, nullptr, 0);
if (ptr == (void*)-1) {
cout << "shmat() failed" << endl;
return -1;
}
Semaph mutex;
if (mutex.init(0x0001) == false) {
cout << "mutex fail" << endl;
return -1;
}
cout << "申請鎖" << endl;
mutex.wait();
cout << "申請鎖成功" << endl;
strcpy(ptr->data, "abcdfrere");
cout << "share memory data = " << ptr->data << endl;
sleep(10);
mutex.post();
cout << "解鎖" << endl;
shmdt(ptr);
}
編譯指令
g++ -o semaph_test semaph_test.cpp semaph.cpp
開二個終端機同時測試
一個終端機已經申請到鎖
$ ./semaph_test
shmid = 4
申請鎖
申請鎖成功
share memory data = abcdfrere
一個終端機在等待解鎖
$ ./semaph_test
shmid = 4
申請鎖