首页 > linux进程间通信:POSIX 共享内存

linux进程间通信:POSIX 共享内存

文章目录

        • 思维导图
        • 通信原理
        • 优势
        • POSIX 共享内存 编程接口
        • 编程案例

思维导图

在这里插入图片描述

之前学习过sysemV 的共享内存的实现及使用原理,参考linux进程间通信:system V 共享内存

POSIX 同样提供共享内存的接口,基本原理和system V的共享内存是一样的。

通信原理

  • 多个进程共享物理内存的同一块区域(通常称之为“段”:segment)
  • 抛弃了内核态消息转存处理的过程,让两个进程直接通过一块内存进行通信

我们普通的像PIPE,FIFO,消息队列等的通信方式如下图:

在这里插入图片描述

这种方式的通信不论读写,都需要内核态(系统调用 read,write,pipe,mkfifo,msgget,msgsnd,msgrcv等)的介入,而且都需要经过数据从虚拟地址空间到物理地址空间的拷贝。

而共享内存的通信方式则都避免了以上的通信问题,直接为两个进程开辟相同的内存空间进行数据交互。

在这里插入图片描述

优势

  • 减少了内存的拷贝(从用户拷贝到内核,从内核拷贝到用户)
  • 减少了2次系统调用(系统调用比较消耗性能,因为CPU处理系统调用时需要从用户态切换到内核态),提高了系统性能

POSIX 共享内存 编程接口

关于共享内存的接口详细使用就不一一描述,可以通过man shm_open这种类似的方式查看具体如何使用接口

//创建共享内存
int shm_open(const char *name, int oflag, mode_t mode);
//当共享内存引用计数为0时,删除共享内存
int shm_unlink(const char *name);
//获取文件相关的信息,将获取到的信息放入到statbuf结构体中
int fstat(int fd, struct stat *statbuf);
//调整文件大小,通过裁剪指定字节达到对文件大小的精准控制
int ftruncate(int fd, off_t length);
//将进程空间的文件映射到内存,也可以将进程空间的匿名区域映射到内存
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
//解除文件或者匿名映射
int munmap(void *addr, size_t length);

以上接口包含头文件

编程案例

  1. 共享内存基本使用

    shm_read.c 共享内存的读端

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include #define SHM_NAME "/shm"int main() { int shm_fd;//创建共享内存文件标识符shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666);if (shm_fd == -1) { printf("shm_open failed
    ");}//设置共享内存的文件大小ftruncate(shm_fd , 8192);//获取共享内存文件相关属性信息,这里获取的是文件大小struct stat filestat;fstat(shm_fd, &filestat);printf("st_size :%ld
    ",filestat.st_size);//映射共享内存,并获取共享内存的地址char *shm_ptr;shm_ptr = (char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);close(shm_fd);//获取共享内存地址中的内容并打印,最后再解除映射,删除共享内存printf("pid %d:%s
    ",getpid(),shm_ptr);munmap(shm_ptr, filestat.st_size);shm_unlink(SHM_NAME);return 0;
    }
    

    shm_write.c共享内存的写端

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include #define SHM_NAME "/shm"int main() { int shm_fd;//创建和读端相同的文件标识shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666);if (shm_fd == -1) { printf("shm_open failed
    ");}ftruncate(shm_fd , 8192);struct stat filestat;fstat(shm_fd, &filestat);printf("st_size :%ld
    ",filestat.st_size);char *shm_ptr;shm_ptr = (char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);close(shm_fd);//向共享内存中写入数据,这里利用memmove进行内存拷贝写入char buf[] = "hello world";memmove(shm_ptr,buf,sizeof(buf));printf("pid %d:%s
    ",getpid(),shm_ptr);//写入完成后解除映射munmap(shm_ptr, filestat.st_size);return 0;
    }
    

    编译gcc shm_read.c -o read -lrt gcc shm_write.c -o write -lrt

    输出如下:

    在这里插入图片描述

  2. 共享内存和信号量一同使用,内存访问的同步

    当读端能够读出的前提是读的时候信号量的value值为1,否则无法读出

    同样写的时候对信号量进行v操作,将信号量的value值加1,为读提供同步条件

    实现如下

    读端sem_shm_read.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include #define SHM_NAME "/shm"
    #define SEM_NAME "/memmap_sem"int main() { //增加信号量的初始创建int shm_fd;sem_t *sem;shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666);sem = sem_open(SEM_NAME, O_CREAT, 0666, 0);if (shm_fd == -1 || sem == SEM_FAILED) { printf("open failed
    ");_exit(-1);}ftruncate(shm_fd , 8192);struct stat filestat;fstat(shm_fd, &filestat);printf("st_size :%ld
    ",filestat.st_size);char *shm_ptr;shm_ptr = (char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);close(shm_fd);//读的时候对信号量做p操作(-1),如果信号量此时为0时则无法读出sem_wait(sem);printf("pid %d:%s
    ",getpid(),shm_ptr);sem_close(sem);//读完之后删除共享内存,删除信号量munmap(shm_ptr, filestat.st_size);shm_unlink(SHM_NAME);sem_unlink(SEM_NAME);return 0;
    }
    

    写端shm_write.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include #define SHM_NAME "/shm"
    #define SEM_NAME "/memmap_sem"int main() { int shm_fd;sem_t *sem;shm_fd = shm_open(SHM_NAME, O_RDWR | O_CREAT, 0666);sem = sem_open(SEM_NAME, O_CREAT, 0666, 0);if (shm_fd == -1 || sem == SEM_FAILED) { printf("open failed
    ");_exit(-1);}ftruncate(shm_fd , 8192);struct stat filestat;fstat(shm_fd, &filestat);printf("st_size :%ld
    ",filestat.st_size);char *shm_ptr;shm_ptr = (char*)mmap(NULL,filestat.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);close(shm_fd);char buf[] = "hello world";memmove(shm_ptr,buf,sizeof(buf));printf("pid %d:%s
    ",getpid(),shm_ptr);//写的时候对信号量的值执行v(+1)操作, 方便后续的读sem_post(sem);sem_close(sem);munmap(shm_ptr, filestat.st_size);return 0;
    }
    

更多相关:

  • 更多内容,欢迎关注微信公众号:全菜工程师小辉~前言在笔者上一篇博客,详解了NIO,并总结NIO相比BIO的效率要高的三个原因,彻底搞懂NIO效率高的原理。这篇博客将针对第三个原因,进行更详细的讲解。首先澄清,零拷贝与内存直接映射并不是Java中独有的概念,并且这两个技术并不是等价的。零拷贝零拷贝是指避免在用户态(User-space)...

  • 一、预备知识—程序的内存分配  一个由c/C++编译的程序占用的内存分为以下几个部分  1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈,如果还不清楚,那么就把它想成数组,它的内存分配是连续分配的,即,所分配的内存是在一块连续的内存区域内.当我们声明变量时,那么编译器...

  • 我的爱机是一台ThinkPad T420,原装三星DDR 1333 4G内存一根,还剩一根内存位置,最近趁京东6.18促销,准备增加一根物理内存。为了确保兼容性,觉得仍然选购DDR 1333 4G内存,于是购买了金士顿这款,比如DDR3 1600的还贵。 这个安装过程完全参照该内存的网页提示进行 这里简单记录一下,以备...

  • 陪伴我多年的老本ThinkPad T420渐渐垂垂老矣, 我想更新一下可以更新的部分, 比如将2.5寸HDD更换为SSD, 将单条4G内存再增加一根, 凡此种种想法, 可能最后归结为如何获取该笔记本的硬件配置信息, 在windows下面使用鲁大师之类的检测软件, 也许很好搞定,但是在Ubuntu 14.04平台上如果办到呢? 很简单...

  • 一.内存错误出现的场景 这几天在重构ATS插件代码的过程中遇到了烦人的内存泄露问题, 周五周六连续两天通过走查代码的方法,未能看出明显的导致内存错误的代码, 同时也觉得C和C++混合编程得到一个动态库, 在一个.cpp主文件中,即用new又用malloc来动态分配内存, 可能会导致内存错误.后来网上调研和查资料发现, new和mal...

  • fifo的双向通信的方式如下图: 两个进程间的通信需要两个命名管道,分别处理一个进程的读和写 导致这种通信方式出现的根因还是由于fifo的阻塞读和阻塞写,所以这里需要使用两个管道对读写进行分别处理。 同时因为管道传输的数据为流式数据,则无法对数据进行指定标记(数据的发送者,接受者,大小。。。)。 SERVER端代码如下: /*...

  • 文章目录介绍重定向函数介绍总结 linux terminal输入如下命令,其中"|"符号即为我们上文中所说的无名管道 介绍 正如我们上文中所描述的"|“无名管道提供了具有亲缘关系的进程之间的通信,它由于直接使用系统调用,运行效率较高。则linux系统下可以大批量的使用”|"来提供命令直接输入输出的重定向。 具体sh...

  • 2019独角兽企业重金招聘Python工程师标准>>> fd_set 结构,是一个数字。 文件描述符fd的值 不能超过1024,而不是数量不能超过1024。 在开发打开句柄多的程序时,最好不要使用select。否则就使用多进程开发。 /usr/include/X11 Xpoll.h 文件 #ifndef _XPOLL_H_#...