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
");
11         return -1;
12     }
13 
14     if (lseek(fd, sizeof(book_t) * 2 - 1, SEEK_SET) < 0){
15         perror("lseek error
");
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
");
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
");
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
");
16         return -1;
17     }
18 
19     close(fd);
20 
21     for (i = 0; i < 4; i++){
22         printf("id:%d, name:%s
", (*(book+i)).id, (*(book+i)).name);
23     }
24 
25     munmap(book, sizeof(book_t) * 4);
26 
27     return 0;
28 }
原文地址:https://www.cnblogs.com/wanpengcoder/p/5299148.html