Linux-進程間通信(三): 共享內存


1. 共享內存:

共享內存方式可以在多個進程直接共享數據,因為其直接使用內存,不要多余的拷貝,是速度最快的IPC方式;

共享內存有兩種實現方式,使用mmap和shm方式,如下圖:

(1) mmap方式是將文件與進程地址空間進行映射,對實際物理內存影響小;

 

(2) shm方式是將每個進程的共享內存與實際物理存儲器進行映射,對實際物理內存影響大;

 

由於XSI IPC自身缺點,所以建議使用mmap來實現共享內存;

 

2. mmap相關函數原型:

#include <sys/mman.h>

void *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off);

ret = 成功返回映射區域開始地址,失敗返回MAP_FAILED

addr-參數用來指定映射區域的起始地址。通常設置為0,表示有系統選擇映射區域的起始位置;

filedes-要映射文件的描述符。在映射文件到地址空間之前,先要打開文件;

len-映射的字節數;

off-要映射字節在文件中的起始偏移量;

prot-對映射存儲區的保護要求,指定為PROT_NONE(不可訪問), PROT_READ(可讀), PROT_WRITE(可寫), PROT_EXEC(可執行)的任意組合或者按位或;

但是對於映射存儲區的保護要求不能超過文件open的權限,比如文件是只讀打開的,則不能指定PROT_WRITE;

flag-MAP_FIXED 返回值必須等於addr,如果未制定此標志,addr非0,則作為一種建議,內核不保證會使用該地址。為了獲得最大的可移植性,建議addr指定為0;

MAP_SHARED 說明了本進程對映射區域進行儲存操作的配置。此標志指定存儲操作修改映射文件,也就是相當於對該文件執行write;

MAP_PRIVATE 本標志說明對映射區存儲操作會創建該映射的一個副本。所以后續操作都會引用該副本,而不是原始文件;

MAP_SAHRED 和 MAP_PRIVATE 必須指定其一,但是不能同時指定;

 

修改現有映射區的權限,與上面mmap中的prot字段相同;

#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);

ret = 成功返回0,失敗返回-1

 

如果共享內存中的頁已被修改,則可以調用msync將該頁沖洗到映射的文件中;

如果映射是私有的,那么不修改映射文件;

#include <sys/mman.h>

int msync(void *addr, size_t len, int flags);

ret = 成功返回0 失敗返回-1

flags- MS_ASYNC和MS_SYNC必選其一;

   MS_ASYNC-異步sync,無需等待,系統調度;

        MS_SYNC-同步sync,需要等到操作完成;

        MS_INVALIDATE-可選標志,通知操作系統丟棄沒有同步的頁;--一般不使用;

 

進程終止,或者調用munmap,內存映射就被自動解除,關閉文件描述符filedes並不會解除映射;

調用munmap不會是映射內存內容寫到磁盤文件,share類型寫磁盤是內核根據算法自動執行,private則丟棄修改;

#include <sys/mman.h>

int munmap(caddr_t addr, size_t len);

ret-成功返回0 失敗返回-1

 

3.  文件映射到進程位置:

文件映射到進程地址空間中堆和棧直接的一段內存。

 

4. 子進程影響:

fork之后,因為子進程復制父進程的地址空間,而存儲映射是該地址空間的一部分,所以子進程繼承了該存儲映射區域,但是當調用了exec函數組之后的新程序則不繼承該映射區;

 

測試代碼:--測試兩個進程通過mmap映射信息,測試共享時,數據情況,和共享后數據情況;

common.h

 1 #include <sys/mman.h>
 2 #include <stdlib.h>
 3 #include <stdio.h>
 4 #include <fcntl.h>
 5 #include <string.h>
 6 
 7 #define MMAP_FILE "/var/tmp/test_mmap"
 8 #define NAME_LEN 64
 9 
10 typedef struct book {
11     int id;
12     char name[NAME_LEN];
13 }book_t;

 

mmap_proc1.c -- 進程1,做映射,映射超過文件長度的區域

 1 #include "common.h"
 2 
 3 int main(int argc, char *argv[])
 4 {
 5     int fd = -1;
 6     int i = 0;
 7     book_t *book = NULL;
 8 
 9     if ((fd = open(MMAP_FILE, O_RDWR | O_CREAT | O_TRUNC)) < 0){
10         perror("open file error\n");
11         return -1;
12     }
13 
14     if (lseek(fd, sizeof(book_t) * 2 - 1, SEEK_SET) < 0){
15         perror("lseek error\n");
16         close(fd);
17         return -1;
18     }
19 
20     write(fd, "", 1);
21 
22     if ((book = (book_t *)mmap(NULL, sizeof(book_t) * 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED){
23         perror("mmap error\n");
24         return -1;
25     }
26 
27     close(fd);
28 
29     for (i = 0; i < 4; i++){
30         char name[16] = { 0 };
31         snprintf (name, 15, "book-%d", i);
32 
33         (*(book + i)).id = i;
34         strncpy((*(book + i)).name, name, NAME_LEN - 1);
35     }
36 
37     sleep(10);
38 
39     munmap(book, sizeof(book_t) * 4);
40 
41     return 0;
42 }

 

mmap_proc2.c -- 進程2,做映射,映射與進程1相同區域

 1 #include "common.h"
 2 
 3 int main(int argc, char *argv[])
 4 {
 5     int fd = -1;
 6     int i = 0;
 7     book_t *book = NULL;
 8 
 9     if ((fd = open(MMAP_FILE, O_RDWR | O_CREAT)) < 0){
10         perror("open file error\n");
11         return -1;
12     }
13 
14     if ((book = (book_t *)mmap(NULL, sizeof(book_t) * 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED){
15         perror("mmap error\n");
16         return -1;
17     }
18 
19     close(fd);
20 
21     for (i = 0; i < 4; i++){
22         printf("id:%d, name:%s\n", (*(book+i)).id, (*(book+i)).name);
23     }
24 
25     munmap(book, sizeof(book_t) * 4);
26 
27     return 0;
28 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM