C/C++ Linux sem_wait 與 sem_post 用法範例

今天 ShengYu 來介紹 C/C++ Linux sem_wait 與 sem_post 用法範例,Semaphore 跟 Mutex 相比,雖然 Semaphore 同樣可以用來保護 Critical section,不過它更常被用來確保多執行緒的執行順序。在 Semaphore 中是用 sem_wait 減少與 sem_post 增加 Semaphore 號誌,不像是 Mutex 解鎖還需要同一個人解的 owner ship 特性,也就像解鈴還需繫鈴人概念,以下就來看看 sem_wait 與 sem_post 用法範例吧!

sem_wait() 函式原型:

1
int sem_wait(sem_t *sem);

解釋:若 semaphore 為非 0,則 semaphore 值減 1;若 semaphore 為 0,則呼叫此 function 的 thread 會被 block ,直到 semaphore 值大於 0。

sem_post() 函式原型:

1
int sem_post(sem_t *sem);

解釋:對 semaphore 值加 1。

同行程內的 sem_wait() 跟 sem_post() 用法

以下範例是在一個行程內使用 sem_wait() 跟 sem_post(),在 main 函式 會產生 producer 執行緒跟 consumer 執行緒,producer 執行緒負責產生 1-20 之間的亂數並呼叫 insert_item 放入 buffer 中,而 consumer 執行緒負責用 remove_item 來消耗這 中的亂數。

如果 consumer 都沒有消耗任何變數的情況下,在 producer 會因為 sem_wait(&empty) 從初始值 5 一直減 1 直到 0 不能再減的情況下就會被 block 卡住,直到大於 0 為止才會繼續往下執行。

反之,如果 producer 都沒有生產任何變數的情況下,在 consumer 會因為 sem_wait(&full) 從初始值 0 一直減 1 直到 0 不能再減的情況下就會被 block 卡住,直到大於 0 為止才會繼續往下執行。

linux-sem_wait.c
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// gcc linux-sem_wait.c -pthread
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];

pthread_mutex_t mutex;
sem_t empty;
sem_t full;

int insertPointer = 0, removePointer = 0;

void *producer(void *param);
void *consumer(void *param);

void display_int_array(char *prog, int *arr, int n) {
printf("%s: ", prog);
for (int i = 0; i < n; i++) {
printf("%d%c", arr[i], ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}

int insert_item(int item) {
/* Acquire Empty Semaphore */
sem_wait(&empty);

/* Acquire mutex lock to protect buffer */
pthread_mutex_lock(&mutex);

buffer[insertPointer++] = item;
insertPointer = insertPointer % BUFFER_SIZE;
display_int_array("prod", buffer, BUFFER_SIZE);

/* Release mutex lock and full semaphore */
pthread_mutex_unlock(&mutex);
sem_post(&full);

return 0;
}

int remove_item(int *item) {
/* Acquire Full Semaphore */
sem_wait(&full);

/* Acquire mutex lock to protect buffer */
pthread_mutex_lock(&mutex);

*item = buffer[removePointer];
buffer[removePointer++] = -1;
removePointer = removePointer % BUFFER_SIZE;
display_int_array("cons", buffer, BUFFER_SIZE);

/* Release mutex lock and empty semaphore */
pthread_mutex_unlock(&mutex);
sem_post(&empty);

return 0;
}

int main(int argc, char *argv[]) {
int sleepTime = 10, producerThreads, consumerThreads;

/* Initialize the synchronization tools */
pthread_mutex_init(&mutex, NULL);
sem_init(&empty, 0, BUFFER_SIZE);
sem_init(&full, 0, 0);
srand(time(0));

/* Create the producer and consumer threads */
{
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&tid, &attr, producer, NULL);
}

{
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&tid, &attr, consumer, NULL);
}

/* Sleep for user specified time */
sleep(sleepTime);
return 0;
}

void *producer(void *param) {
int random;
int r;

while (1) {
r = rand() % 3;
sleep(r);
random = rand() % 20 + 1;

if (insert_item(random))
fprintf(stderr, "Error");

printf("Producer produced %d \n", random);
}
}

void *consumer(void *param) {
int random;
int r;

while (1) {
r = rand() % 3;
sleep(r);

if (remove_item(&random))
fprintf(stderr, "Error Consuming");
else
printf("Consumer consumed %d \n", random);
}
}

輸出如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
prod: 7 0 0 0 0 
Producer produced 7
cons: -1 0 0 0 0
Consumer consumed 7
prod: -1 14 0 0 0
Producer produced 14
cons: -1 -1 0 0 0
Consumer consumed 14
prod: -1 -1 3 0 0
Producer produced 3
cons: -1 -1 -1 0 0
Consumer consumed 3
prod: -1 -1 -1 8 0
Producer produced 8
cons: -1 -1 -1 -1 0
Consumer consumed 8
prod: -1 -1 -1 -1 10
Producer produced 10
cons: -1 -1 -1 -1 -1
Consumer consumed 10
prod: 10 -1 -1 -1 -1
Producer produced 10

跨行程的 sem_wait() 跟 sem_post() 用法

跨行程的 sem_wait() 跟 sem_post() 用法,用 fork 分離出兩個行程,並在這父行程與子行程兩行程間用共享記憶體傳遞資料,使用 mmap 共享記憶體來當作 mutex,原理跟上述範例類似,只是這是跨行程。

shm_open / ftruncate / mmap 跟 sem_open 這兩種方式都可以成功。

linux-sem_wait-2.c
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
72
73
74
75
76
77
78
79
80
81
82
83
// gcc linux-sem_wait-2.c -lrt -pthread
#include <semaphore.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>

void display(char *prog, char *bytes, int n) {
printf("%s: ", prog);
for (int i = 0; i < n; i++) {
printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}

int main(int argc, char **argv) {
int fd, i, count = 0,nloop = 2,zero = 0, *ptr;
sem_t *mutex;

int shm;
if ((shm = shm_open("myshm", O_RDWR | O_CREAT, S_IRWXU)) == 0) {
perror("shm_open");
exit(1);
}

if (ftruncate(shm, sizeof(sem_t)) < 0 ) {
perror("ftruncate");
exit(1);
}

if ((mutex = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE,
MAP_SHARED, shm, 0)) == MAP_FAILED) {
perror("mmap");
exit(1);
}

// open a file and map it into memory
fd = open("tmp.txt",O_RDWR | O_CREAT, S_IRWXU);
write(fd, &zero, sizeof(int));
ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);

/*if ((mutex = sem_open("mysemaphore", O_CREAT, 0644, 1)) == SEM_FAILED) {
perror("semaphore initilization");
exit(1);
}*/

// create, initialize semaphore
if (sem_init(mutex, 1, 1) < 0) {
perror("semaphore initilization");
exit(0);
}

if (fork() == 0) { // child process
for (i = 0; i < nloop; i++) {
sem_wait(mutex);
printf("child entered crititical section: %d\n", (*ptr)++);
display("child", (char *)ptr, 16);
sleep(2);
printf("child leaving critical section\n");
sem_post(mutex);
sleep(1);
}
exit(0);
}

// back to parent process
for (i = 0; i < nloop; i++) {
sem_wait(mutex);
printf("parent entered critical section: %d\n", (*ptr)++);
display("parent", (char *)ptr, 16);
sleep(2);
printf("parent leaving critical section\n");
sem_post(mutex);
sleep(1);
}

return 0;
}

以上就是 C/C++ Linux sem_wait 與 sem_post 用法範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它參考
Semaphores on Linux - sem_init() vs sem_open() - Superpatterns
https://blog.superpat.com/semaphores-on-linux-sem_init-vs-sem_open
在這篇當中提到 shm_open / ftruncate / mmap 跟 sem_open 這兩種方式

其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ Linux shared memory 與 mmap 用法範例