• 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 }
  • 相关阅读:
    Java实现“睡排序”——线程池Executors的使用
    浅谈HashMap与线程安全 (JDK1.8)
    Ubuntu 16 Java Develop环境快速搭建
    Spring Boot在反序列化过程中:jackson.databind.exc.InvalidDefinitionException cannot deserialize from Object value
    Java 8 – Map排序
    vue指令优化网络图片加载速度
    如何实现小于12px的字体效果
    两种以上方式实现已知或者未知宽度的垂直水平居中
    C# winform窗体间传值(使用委托或事件)
    C#栈Stack的使用
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/5299148.html
Copyright © 2020-2023  润新知