共享内存简介

一.共享内存

         共享内存区是最快的IPC形式,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据.

上图需要经过四次数据复制

使用共享内存,只需要两次系统调用.



二、mmap 函数

#include <sys/mman.h>

功能:将文件或者设备空间映射到共享内存区。
原型
    void *mmap(void *addr,   //要映射的起始地址,通常指定为NULL,让内核自动选择
                size_t len,             //映射到进程地址空间的字节数
                int prot,                 //映射区保护方式
                int flags,             //标志
                int fd,               //文件描述符
                off_t offset);      //从文件头开始的偏移量,必须是页大小的整数倍(在32位体系统结构上通常是4K)

    返回值:    成功返回映射到的内存区的起始地址;
            
    
prot 参数取值
    PROT_EXEC   页面可读
    PROT_READ   页面可写
    PROT_WRITE  页面可执行
    PROT_NONE   页面不可访问


flag参数有很多种取值
    MAP_SHARED   多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。变动是共享的

    MAP_PRIVATE   多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。 变动是私有的

    MAP_FIXED    准确解释addr参数

    MAP_ANONYMOUS  建立匿名映射区,不涉及文件


三.munmap函数

功能:取消mmap函数建立的映射
原型 int munmap(void *addr,    //映射的内存起始地址
                size_t len);                  //映射到进程地址空间的字节数

返回值:成功返回0;失败返回-1
    
四.msync函数
    功能:对映射的共享内存执行同步操作
    原型:
    int msync(    void *addr,         //起始地址
                          size_t length,     //长度
                          int flags);            //选项
    返回值:    
        成功返回0
        失败返回-1
    flags:
        MS_ASYNC          执行异步写
        MS_SYNC             执行同步写
        MS_INVALIDATE  使高速缓存的数据失效

写入共享内存mmap_write.c

#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;  // 结构体大小为8个字节

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);  //定位到39位置
    write(fd, "", 1);// 写入空字符,为40个字节的文件

    STU *p;
    //将文件或者设备空间映射到共享内存区。
    p = (STU *)mmap(NULL, sizeof(STU) * 5, PROT_READ | PROT_WRITE,
                    MAP_SHARED, fd, 0);

    if (p == NULL)
        ERR_EXIT("mmap err");

	// 现在写入数据,对文件的操作,现在变成了直接对内存的操作
    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);  //取消mmap函数建立的映射
    printf("exit...\n");
   
    return 0;
}



从共享内存中读取 mmap_read.c

#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 == NULL)
        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注意点

    1、 映射不能改变文件的大小;(共享内存映射多少,不影响文件的大小,即文件的大小是固定的)
    2、 可用于进程间通信的有效地址空间不完全受限于被映射文件的大小;(共享内存的大小可以大于文件)
    3、 文件一旦被映射后,所有对映射区域的访问实际上是对内存区域的访问。映射区域内容写回文件时,所写内容不能超过文件的大小;


================================================================

实验:

          修改mmap_write.c 和 mmap_read.c 源文件, 修改映射区的大小,使得原来的5修改为8,这样8*5=40的大小的文件,映射成8*8=64大小的共享内存区.同时在mmap_write.c 中munmap(p, sizeof(STU) * 10); 之前添加一句sleep(5); 

上图可以看出实际文件的大小为40byte,虽然有两行半的数据,但是实际只有 5*8=40byte的内容.


下图是5s内读取的数据




下图是5s后读取的数据


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值