今天 ShengYu 來介紹 Linux 跨行程通訊 IPC 中的其中一種方式:共享記憶體 Shared Memory 以及 named semaphore,這通常也是 OS (operating system)作業系統或linux系統程式課程中的一部分,以下範例分為生產者 producer 與消費者 consumer,基本上會使用到 mmap 與 shm_open 這幾個函式。
共享記憶體傳遞 string 字串的範例
生產者 producer 寫入 string 字串的範例如下,
這邊的 shm_open 是使用 O_CREAT | O_RDWR
能建立且能讀取寫入,同樣的 mmap 也使用 PROT_READ | PROT_WRITE
能讀取跟寫入。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// gcc shm-posix-producer-string.c -o produce -lrt && ./produce
void display(char *prog, char *bytes, int n) {
printf("display: %s\n", prog);
for (int i = 0; i < n; i++) {
printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}
int main() {
const int SIZE = 4096;
const char *name = "OS";
const char *message0= "Studying ";
const char *message1= "Operating Systems ";
const char *message2= "Is Fun!";
int shm_fd;
void *ptr;
/* create the shared memory segment */
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
/* configure the size of the shared memory segment */
ftruncate(shm_fd,SIZE);
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed\n");
return -1;
}
/**
* Now write to the shared memory region.
*
* Note we must increment the value of ptr after each write.
*/
char *str = (char *)ptr;
sprintf(str,"%s",message0);
str += strlen(message0);
sprintf(str,"%s",message1);
str += strlen(message1);
sprintf(str,"%s",message2);
str += strlen(message2);
display("prod", ptr, 64);
return 0;
}
producer 輸出如下:1
2
3
4
5display: prod
53 74 75 64 79 69 6e 67 20 4f 70 65 72 61 74 69
6e 67 20 53 79 73 74 65 6d 73 20 49 73 20 46 75
6e 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
消費者 consumer 讀取 string 字串的範例如下,
這邊的 shm_open 是使用 O_RDONLY
只能讀取,同樣的 mmap 也使用 PROT_READ
只能讀取。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// gcc shm-posix-consumer-string.c -o consume -lrt && ./consume
void display(char *prog, char *bytes, int n) {
printf("display: %s\n", prog);
for (int i = 0; i < n; i++) {
printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}
int main() {
const char *name = "OS";
const int SIZE = 4096;
int shm_fd;
void *ptr;
/* open the shared memory segment */
shm_fd = shm_open(name, O_RDONLY, 0666);
if (shm_fd == -1) {
printf("shared memory failed\n");
exit(-1);
}
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed\n");
exit(-1);
}
/* now read from the shared memory region */
display("cons", ptr, 64);
printf("%s",(char *)ptr);
/* remove the shared memory segment */
if (shm_unlink(name) == -1) {
printf("Error removing %s\n",name);
exit(-1);
}
return 0;
}
consumer 輸出如下:1
2
3
4
5
6
7display: cons
53 74 75 64 79 69 6e 67 20 4f 70 65 72 61 74 69
6e 67 20 53 79 73 74 65 6d 73 20 49 73 20 46 75
6e 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Studying Operating Systems Is Fun!
共享記憶體傳遞 char 字元的範例
生產者 producer 寫入 char 字元的範例如下,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// gcc shm-posix-producer-char.c -o produce -lrt && ./produce
void display(char *prog, char *bytes, int n) {
printf("display: %s\n", prog);
for (int i = 0; i < n; i++) {
printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}
int main() {
const int SIZE = 4096;
const char *name = "OS";
int shm_fd;
void *ptr;
/* create the shared memory segment */
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
/* configure the size of the shared memory segment */
ftruncate(shm_fd,SIZE);
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed\n");
return -1;
}
/* Now write to the shared memory region. */
sprintf(ptr,"%c", 'A');
display("prod", ptr, 32);
return 0;
}
producer 輸出如下:1
2
3display: prod
41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
消費者 consumer 讀取 char 字元的範例如下,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// gcc shm-posix-consumer-char.c -o consume -lrt && ./consume
void display(char *prog, char *bytes, int n) {
printf("display: %s\n", prog);
for (int i = 0; i < n; i++) {
printf("%02x%c", bytes[i], ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}
int main() {
const char *name = "OS";
const int SIZE = 4096;
int shm_fd;
void *ptr;
/* open the shared memory segment */
shm_fd = shm_open(name, O_RDONLY, 0666);
if (shm_fd == -1) {
printf("shared memory failed\n");
exit(-1);
}
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed\n");
exit(-1);
}
/* now read from the shared memory region */
display("cons", ptr, 32);
printf("%c\n", *(char *)ptr);
/* remove the shared memory segment */
if (shm_unlink(name) == -1) {
printf("Error removing %s\n",name);
exit(-1);
}
return 0;
}
consumer 輸出如下:1
2
3
4
5display: cons
41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
A
共享記憶體傳遞 int 整數的範例
生產者 producer 寫入 int 整數的範例如下,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// gcc shm-posix-producer-int.c -o produce -lrt && ./produce
void display(char *prog, char *bytes, int n) {
printf("display: %s\n", prog);
for (int i = 0; i < n; i++) {
printf("%02x%c", bytes[i] & 0xff, ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}
int main() {
const int SIZE = 4096;
const char *name = "OS";
int shm_fd;
void *ptr;
/* create the shared memory segment */
shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
/* configure the size of the shared memory segment */
ftruncate(shm_fd,SIZE);
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed\n");
return -1;
}
/* Now write to the shared memory region. */
int *num = (int *)ptr;
num[0] = 1;
num[1] = 2;
num[2] = 3;
num[3] = 456;
display("prod", ptr, 32);
return 0;
}
producer 輸出如下:1
2
3display: prod
01 00 00 00 02 00 00 00 03 00 00 00 c8 01 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
消費者 consumer 讀取 int 整數的範例如下,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// gcc shm-posix-consumer-int.c -o consume -lrt && ./consume
void display(char *prog, char *bytes, int n) {
printf("display: %s\n", prog);
for (int i = 0; i < n; i++) {
printf("%02x%c", bytes[i] & 0xff, ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}
int main() {
const char *name = "OS";
const int SIZE = 4096;
int shm_fd;
void *ptr;
/* open the shared memory segment */
shm_fd = shm_open(name, O_RDONLY, 0666);
if (shm_fd == -1) {
printf("shared memory failed\n");
exit(-1);
}
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed\n");
exit(-1);
}
/* now read from the shared memory region */
display("cons", ptr, 32);
int *num = (int *)ptr;
printf("%d\n", num[0]);
printf("%d\n", num[1]);
printf("%d\n", num[2]);
printf("%d\n", num[3]);
/* remove the shared memory segment */
if (shm_unlink(name) == -1) {
printf("Error removing %s\n",name);
exit(-1);
}
return 0;
}
consumer 輸出如下:1
2
3
4
5
6
7
8display: cons
01 00 00 00 02 00 00 00 03 00 00 00 c8 01 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
1
2
3
456
共享記憶體傳遞 int 整數陣列的範例
剛剛前述的範例都是 producer 單向地寫入資料,consumer 只是 read only 讀取資料,這邊示範 producer 在一個整數陣列中不斷地產生亂數循序放入陣列中,之後 consumer 會從該陣列中循序消耗變數且會將其位置寫回成 -1,其中會用到 semaphore 的 sem_wait 跟 sem_post 技巧。
如下 producer 範例程式,producer 函式在一個整數陣列中不斷地產生亂數用 insert_item 循序放入陣列中,直到滿了以後會因為 empty sem_t 是 0 的關係無法減 1 而 block 卡住等待,直到有人用 sem_post(empty)
將 empty sem_t 加 1 以後 sem_wait(empty)
才會繼續往下執行。
要注意的是,在一開始 main 函式中 sem_init(empty, 1, BUFFER_SIZE);
的第二個參數是指定這個 semaphore 是不是跨行程的,如果是跨行程要設定成 1,如果只是在同一行程內用的話就設定成 0。
1 | // gcc shm-posix-producer-int-array.c -o produce -lrt -pthread && ./produce |
producer 輸出如下: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./produce
display: prod
05 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0 0 0 0 0
Producer produced 4
display: prod
04 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4 0 0 0 0
Producer produced 7
display: prod
03 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4 7 0 0 0
Producer produced 18
display: prod
02 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4 7 18 0 0
Producer produced 16
display: prod
01 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
04 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4 7 18 16 0
Producer produced 14
display: prod
00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
4 7 18 16 14
Producer produced 16
display: prod
00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
16 7 18 16 14
Producer produced 7
display: prod
00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
16 7 18 16 14
Producer produced 13
display: prod
00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
16 7 13 16 14
Producer produced 10
display: prod
00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
16 7 13 10 14
Producer produced 2
display: prod
00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
16 7 13 10 2
Producer produced 3
display: prod
00 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 80 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
3 7 13 10 2
^C
在下面 consumer 範例程式中,consumer 不斷地用 remove_item 在整數陣列中循序消耗變數,並將其位置寫回成 -1。
跟前述的 consumer 程式不同處是 shm_open 要改成 O_RDWR
,mmap 要改成 PROT_READ | PROT_WRITE
這樣才能寫入資料。
要注意的是 consumer 的 empty 跟 full 指向 mmap 映射後的共享記憶體空間後,就可以存取到 producer 行程中的 empty 與 full 兩個 semaphore 的值了,不用在 sem_init 初始化 empty 跟 full 這兩個 semaphore 了。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// gcc shm-posix-consumer-int-array.c -o consume -lrt -pthread && ./consume
int *buffer = NULL;
sem_t *empty;
sem_t *full;
int removePointer = 0;
void *ptr;
void display(char *prog, char *bytes, int n) {
printf("display: %s\n", prog);
for (int i = 0; i < n; i++) {
printf("%02x%c", bytes[i] & 0xff, ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}
void display_int_array(int *arr, int n) {
for (int i = 0; i < n; i++) {
printf("%d%c", arr[i], ((i+1)%16) ? ' ' : '\n');
}
printf("\n");
}
int remove_item(int *item) {
/* Acquire Full Semaphore */
sem_wait(full);
*item = buffer[removePointer];
buffer[removePointer++] = -1;
removePointer = removePointer % BUFFER_SIZE;
/* Empty semaphore */
sem_post(empty);
return 0;
}
void consumer() {
int random;
int r;
srand(time(0));
while (1) {
//r = rand() % 5;
//sleep(r);
sleep(1);
if (remove_item(&random))
fprintf(stderr, "Error Consuming");
else
printf("Consumer consumed %d\n", random);
display_int_array(buffer, BUFFER_SIZE);
}
}
int main() {
const char *name = "OS";
const int SIZE = 4096;
int shm_fd;
/* open the shared memory segment */
shm_fd = shm_open(name, O_RDWR, 0666);
if (shm_fd == -1) {
printf("shared memory failed\n");
exit(-1);
}
/* now map the shared memory segment in the address space of the process */
ptr = mmap(0,SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
if (ptr == MAP_FAILED) {
printf("Map failed\n");
exit(-1);
}
/* make empty, full and buffer pointer to ptr
* empty sem_t | full sem_t | buffer
*/
empty = (sem_t *)ptr;
full = ptr + sizeof(sem_t);
buffer = (int *)(ptr + sizeof(sem_t) + sizeof(sem_t));
/* now read from the shared memory region */
/*display("cons", ptr, 96);
display_int_array(buffer, BUFFER_SIZE);
for (int i = 0; i < 2;i++) {
int item;
remove_item(&item);
printf("Consumer consumed %d\n", item);
display("cons", ptr, 96);
}*/
consumer();
/* remove the shared memory segment */
if (shm_unlink(name) == -1) {
printf("Error removing %s\n",name);
exit(-1);
}
return 0;
}
consumer 輸出如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24./consume
Consumer consumed 4
-1 7 18 16 14
Consumer consumed 7
16 -1 18 16 14
Consumer consumed 18
16 7 -1 16 14
Consumer consumed 16
16 7 13 -1 14
Consumer consumed 14
16 7 13 10 -1
Consumer consumed 16
-1 7 13 10 2
Consumer consumed 7
3 -1 13 10 2
Consumer consumed 13
3 -1 -1 10 2
Consumer consumed 10
3 -1 -1 -1 2
Consumer consumed 2
3 -1 -1 -1 -1
Consumer consumed 3
-1 -1 -1 -1 -1
^C
以上就是 C/C++ Linux shared-memory mmap 用法與範例介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!
linux 共享内存 shm_open
,mmap的正确使用_大飞飞鱼的博客-CSDN博客_shm_open
https://blog.csdn.net/ababab12345/article/details/102931841
Semaphores on Linux - sem_init() vs sem_open() - Superpatterns
https://blog.superpat.com/semaphores-on-linux-sem_init-vs-sem_open
文中提到用 mmap 映射記憶體來當作 mutex 的作法可以參考看看。
其它相關文章推薦
如果你想學習 C++ 相關技術,可以參考看看下面的文章,
C/C++ 新手入門教學懶人包
C/C++ Linux sem_wait 與 sem_post 用法範例