日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

共享内存简介和mmap 函数

發布時間:2025/7/25 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 共享内存简介和mmap 函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、共享內存簡介

共享內存區是最快的IPC形式,這些進程間數據傳遞不再涉及到內核,換句話說是進程不再通過執行進入內核的系統調用來傳遞彼此的數據。


即每個進程地址空間都有一個共享存儲器的映射區,當這塊區域都映射到相同的真正的物理地址空間時,可以通過這塊區域進行數據交換,例如共享庫就是這么實現的,很多進程都會使用同一個函數如printf,也許在真正的物理地址空間中只存在一份printf.o ,然后所有進程都映射到這一份printf.o 就實現了共享。


用管道或者消息隊列傳遞數據:


用共享內存傳遞數據:


即使用共享內存傳遞數據比用消息隊列和管道來說,減少了進入內核的次數,提高了效率。


二、mmap 函數

#include <sys/mman.h>

功能:將文件或者設備空間映射到共享內存區。
原型?void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
參數
addr: 要映射的起始地址,通常指定為NULL,讓內核自動選擇
len:映射到進程地址空間的字節數
prot:映射區保護方式
flags:標志
fd:文件描述符
offset:從文件頭開始的偏移量,必須是頁大小的整數倍(在32位體系統結構上通常是4K)
返回值:成功返回映射到的內存區的起始地址;失敗返回-1


prot 參數取值:

PROT_EXEC 表示映射的這一段可執行,例如映射共享庫

PROT_READ 表示映射的這一段可讀

PROT_WRITE 表示映射的這一段可寫

PROT_NONE 表示映射的這一段不可訪問


flag參數有很多種取值,這里只講兩種,其它取值可查看mmap(2)

MAP_SHARED 多個進程對同一個文件的映射是共享的,一個進程對映射的內存做了修改,另一個進程也會看到這種變化。

MAP_PRIVATE 多個進程對同一個文件的映射不是共享的,一個進程對映射的內存做了修改,另一個進程并不會看到這種變化,也不會真的寫到文件中去。


內存映射文件示意圖:


如果mmap成功則返回映射首地址,如果出錯則返回常數MAP_FAILED。當進程終止時,該進程的映射內存會自動解除,也可以調用munmap解除映射:

功能:取消mmap函數建立的映射
原型?int munmap(void *addr, size_t len);
參數
addr: 映射的內存起始地址
len:映射到進程地址空間的字節數
返回值:成功返回0;失敗返回-1


下面寫兩個程序測試一下:

mmap_write.c

?

C++ Code?
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
? #include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/mman.h>

#define?ERR_EXIT(m)?\
????do?{?\
????????perror(m);?\
????????exit(EXIT_FAILURE);?\
????}?while(0)

typedef?struct?stu
{
????char?name[4];
????int?age;
}?STU;

int?main(int?argc,?char?*argv[])
{
????if?(argc?!=?2)
????{
????????fprintf(stderr,?"Usage:?%s?<file>\n",?argv[0]);
????????exit(EXIT_FAILURE);
????}

????int?fd;
????fd?=?open(argv[1],?O_CREAT?|?O_RDWR?|?O_TRUNC,?0666);
????if?(fd?==?-1)
????????ERR_EXIT("open");

????lseek(fd,?sizeof(STU)?*?5?-?1,?SEEK_SET);
????write(fd,?"",?1);

????STU?*p;
????p?=?(STU?*)mmap(NULL,?sizeof(STU)?*?5,?PROT_READ?|?PROT_WRITE,
????????????????????MAP_SHARED,?fd,?0);

????if?(p?==?-1)
????????ERR_EXIT("mmap");

????char?ch?=?'a';
????int?i;
????for?(i?=?0;?i?<?5;?i++)
????{
????????memcpy((p?+?i)->name,?&ch,?1);
????????(p?+?i)->age?=?20?+?i;
????????ch++;
????}

????printf("initialize?over\n");

????munmap(p,?sizeof(STU)?*?5);
????printf("exit...\n");
????return?0;
}

?

mmap_read.c

?

C++ Code?
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
? #include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/mman.h>

#define?ERR_EXIT(m)?\
????do?{?\
????????perror(m);?\
????????exit(EXIT_FAILURE);?\
????}?while(0)

typedef?struct?stu
{
????char?name[4];
????int?age;
}?STU;

int?main(int?argc,?char?*argv[])
{
????if?(argc?!=?2)
????{
????????fprintf(stderr,?"Usage:?%s?<file>\n",?argv[0]);
????????exit(EXIT_FAILURE);
????}

????int?fd;
????fd?=?open(argv[1],?O_RDWR);
????if?(fd?==?-1)
????????ERR_EXIT("open");


????STU?*p;
????p?=?(STU?*)mmap(NULL,?sizeof(STU)?*?5,?PROT_READ?|?PROT_WRITE,
????????????????????MAP_SHARED,?fd,?0);

????if?(p?==?-1)
????????ERR_EXIT("mmap");

????int?i;
????for?(i?=?0;?i?<?5;?i++)
????{
????????printf("name?=?%s?age?=?%d\n",?(p?+?i)->name,?(p?+?i)->age);
????}
????munmap(p,?sizeof(STU)?*?5);
????printf("exit...\n");
????return?0;
}

?

先運行mmap_write ,然后用od -c 查看文件內容:

?

simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_write test?
initialize over
exit...
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ od -c test?
0000000 ? a ?\0 ?\0 ?\0 024 ?\0 ?\0 ?\0 ? b ?\0 ?\0 ?\0 025 ?\0 ?\0 ?\0
0000020 ? c ?\0 ?\0 ?\0 026 ?\0 ?\0 ?\0 ? d ?\0 ?\0 ?\0 027 ?\0 ?\0 ?\0
0000040 ? e ?\0 ?\0 ?\0 030 ?\0 ?\0 ?\0
0000050 注意od -c 輸出的是八進制,024即20,即對內存的操作寫入了文件。
再嘗試運行mmap_read,輸出如下: simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_read test?
name = a age = 20
name = b age = 21
name = c age = 22
name = d age = 23
name = e age = 24
exit...
simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$?

再次將文件test 映射到內存,然后從內存讀取到了文件的內容。


mmap 編程注意點:

1、映射不能改變文件的大小;
2、可用于進程間通信的有效地址空間不完全受限于被映射文件的大小;
3、文件一旦被映射后,所有對映射區域的訪問實際上是對內存區域的訪問。映射區域內容寫回文件時,所寫內容不能超過文件的大小;


對于1,3點,將mmap_write.c 中40行以后的代碼中的5改成10,即映射的內存大于文件的大小,這樣寫入是不會出錯的,因為是向內存寫入,但用od 查看時發現文件還是40 個字節,即只有前5個STU才被真正寫入到了文件。

對于第2點,將mmap_write.c 和 mmap_read.c 都按上面說的更改成10,然后在mmap_write.c 中munmap 函數之前sleep(10); 先運行mmap_write,再在另一終端運行mmap_read,觀察結果:

simba@ubuntu:~/Documents/code/linux_programming/UNP/system_v$ ./mmap_read test?
name = a age = 20
name = b age = 21
name = c age = 22
name = d age = 23
name = e age = 24
name = f age = 25
name = g age = 26
name = h age = 27
name = i age = 28
name = j age = 29
exit...

即在mmap_write 對映射內存區域寫入之后尚未取消映射時,mmap_read 也映射了test 文件,兩個虛擬進程地址空間的映射區域都指向了同一塊物理內存,所以也能讀到write 進程對內存的修改,但進程結束后查看test 文件,還是40個字節而已。內存的映射是以頁面為單位的,一般為4k,所以才有第2條的說法,其實這才是真正體現共享內存可以進程間通信的所在。


最后一點,與write 類似,將文件映射到內存后對內存進行寫入,不一定會馬上寫回文件,有可能內核也會產生一個緩沖區,找個適當的時間內核再寫回設備文件,write 之后可以調用fsync 進行同步,同樣地,mmap 可以調用msync 進行同步。


?

參考:

《linux c 編程一站式學習》

《UNP》

總結

以上是生活随笔為你收集整理的共享内存简介和mmap 函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。