今天 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 為止才會繼續往下執行。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
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
22prod: 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 這兩種方式都可以成功。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
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 用法範例