[Linux]-进程间通信之共享内存

文章详细介绍了Linux系统中内存映射和共享内存的概念、API用法以及它们在进程间通信中的应用。内存映射通过mmap函数实现,允许磁盘文件与进程地址空间的直接映射,支持全双工通信。共享内存则是直接将物理内存映射到虚拟内存,提供快速的进程间通信方式,涉及的关键函数包括shmget、shmat和shmdt等。
摘要由CSDN通过智能技术生成

目录

【内存映射】

1.内存映射的概述

2.内存映射API

2.1建立文件和内存的映射

2.磁盘文件大小的扩展

3.断开内存映射

4.建立内存映射进行读写

【共享内存】

1.共享内存的概述

2.共享内存的API

2.1获取唯一标识符IPC键值

2.2通过IPC键值创建或打开共享内存区

2.3创建虚拟内存和存在的共享内存区的映射

2.4解除当前进程映射

2.5共享内存的控制

3.创建并从共享内存中读写数据


Linux的通信方式之共享内存,总共有两种映射的方法。第一种就是内存映射,通过mmap将文件或者其他对象映射到内存中。还有一种就是共享内存,把物理内存映射到虚拟内存中,进程间便可以通过这块共享的内存进行通信。


【内存映射】

1.内存映射的概述

内存映射I/O (Memory-mapped lO)使一个磁盘文件与存储空间中的一个缓冲区相映射

当将一个磁盘文件通过内存映射映射到内存(进程的地址空间)之后,文件磁盘地址和进程的虚拟地址空间会存在对映关系,于是进程就可以听过指针的方式来对这一段内存进行读写了。

于是当从这段内存中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件

看着似乎和管道很相似,都是通过一个媒介往媒介中间写入或者读取,看着像,但是本质还是有区别。无名管道本质就是一块内存,通过文件描述符往里面读写。有名管道就是一个抽象出来的特殊文件,使用文件IO进行读写。内存映射是把磁盘中的文件映射到内存当中去,也就是说,文件本来就已经是存在得了。

还有两点不同的是,管道是半双工的,只能在同一时刻有一个流向,而内存映射是全双工的,也就是同一时刻可读可写,另外,管道读取读取到数据后,立刻从管道中删除,但是内存映射由于是把磁盘文件映射出来,所以数据还是存在的。

fdd666e794f64e5eaf7e52b14c266ccf.png

关于这个内存映射的一个疑问:

磁盘文件是存放在硬盘的,那么为什么要叫做内存映射呢。因为这个磁盘文件本质是物理内存抽象在磁盘生成的零大小的文件,其实本质还是内存而不是磁盘文件。


2.内存映射API

2.1建立文件和内存的映射

#include <sys/mman.h>

void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);

功能

        建立磁盘文件和内存之间的映射

参数

        addr:地址,填NULL(因为这个是用来手动分配文件映射在内存的地址的,NULL自动分配)

        len:申请的映射区长度

        prot:权限

                PROT_READ:可读

                PROT_WRITE:可写

        flags:标志位

                MAP_SHARED:共享的,对映射区的修改会修改源文件

                MAP_PRIVATE:私有的,对映射区修改不会修改源文件

        fd:文件描述符,需要打开一个文件(也就是被映射的磁盘文件)

        offset:指定一个偏移位置,从该位置开始映射(一般为0)

返回值

        成功:返回内存中映射区的首地址(有了这个地址才可以进行操作)

        失败:-1


2.磁盘文件大小的扩展

问什么要扩展文件的大小呢?

假如一开始我们想要选择一个磁盘文件来进行映射,参数使用了create选项,这个参数的作用是,如果文件存在,清空文件,如果文件不存在,创建一个空的文件。不管是哪一个情况都会导致文件是空的,如果文件是空的,映射到内存中的空间也为0,那么没有任何意义。

#include <unistd.h>

int truncate(const char *path, off_t length);

功能

        给指定的文件扩展指定的长度

参数

        path:扩展的文件路径

        length:扩展的长度


3.断开内存映射

#include <sys/mman.h>

int munmap(void *addr, size_t len);

功能

        断开当前进程中的内存映射

参数

        addr:映射区首地址(mmap返回值)

        len:映射区的长度(扩展文件大小的时候设置的值)


4.建立内存映射进行读写

其实建立内存映射后,两个进程的本质区别就是:一个进程是用来从映射的内存中读取数据,一个是从映射的内存中写入数据。

a785c4c6f8c44ecfa2c1fc72da429237.png

 使用条件编译和宏合二为一

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    
    //建立内存映射
    //打开文件
    int fd = open("temp.txt",O_RDWR|O_CREAT,0666);

    //填充文件
    truncate("temp.txt",16);

    //建立内存映射
    char *add = (char *)mmap(NULL, 16, PROT_READ|PROT_WRITE, MAP_SHARED,fd, 0);

#ifdef WRITE 
    strcpy(add,"hello mem");
#endif
#ifdef READ
    printf("read文件读取到的内容为%s\n",add);
#endif
    //断开映射
    munmap(add,16);
    return 0;
}

【共享内存】

内存映射依赖的是磁盘文件,映射到内存当中,通过操作内存来操作文件。不同进程也可以通过读写文件来进行通信

共享内存映射的就不是磁盘文件了,而是直接映射物理内存


1.共享内存的概述

共享内存依靠的是物理内存,通过物理内存映射到虚拟内存当中

c8055f23c16f4178b23d42b5f3ba2d3c.png

 共享内存具有以下特点

1.共享内存是进程间通信最快的一种方式,当一个进程向内存中写入数据之后,其他共享这块内存的进程可以立即看到这些内容

2.使用共享内存需要注意不同进程对这块内存访问的互斥关系,比如一个进程在往这里面写,那么其他进程此时就不应该有其他操作。(可以考虑互斥锁的使用)


2.共享内存的API

2.1获取唯一标识符IPC键值

这一块是和消息队列中IPC键值的获取是一样的

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);

功能

        获取项目相关唯一Key值

参数

        pathname:路径名

        proj_id:项目ID(非0整数)

返回值

        成功返回键值,失败-1

pathname这个参数可以随便设置的,因为Key值是通过所设置文件信息以及proj_id所合成Key值的,而proj_id这个数也是可以随便设置的。但是由于它只有8bit,也就是0-255.


2.2通过IPC键值创建或打开共享内存区

这一块和消息队列是很相似的(记住使用IPC键值的地方只有消息队列、共享内存以及信号量),都是获取唯一IPC键值,然后再通过这个键值创建或者打开需要操作的进程间通信方法。

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

功能

        创建或者打开一块共享内存区

参数

        key:唯一IPC键值

        size:共享存储段的字节

        shmflg:标识函数的行为以及共享内存的权限

                shmflg参数

                                IPC_CREAT:共享内存区不存在创建

                                IPC_EXCL:如果已经存在返回失败

                                位或权限位:位或权限位可以设置共享内部的权限,格式和open的mode_t一样

返回值

        成功:共享内存标识符

        失败:-1

2.1如何通过命令查看共享内存

ipcs -m                        查看共享内存

ipcrm -m 标识符          删除共享内存区


2.3创建虚拟内存和存在的共享内存区的映射

在使用shmget生成共享内存区后,需要建立每个进程和共享内存区的映射关系。就像消息队列中使用IPC键值生成消息队列后,也需要对消息队列进行读写操作。

#include <sys/types.h>
#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

功能

        将共享内存映射到进程中的内存

参数

        shmid:共享内存标识符

        shmaddr:共享内存在进程内存中映射的地址,使用NULL(推荐)系统自动分配

        shmflg:共享内存段的访问权限和映射条件

                0:具有可读可写的权限

                SHM_RDONLY:只读

                SHM_RND:(shmaddr非空时才有效):没有指定SHM_RND则此段连接到shmaddr所指定的地址上(shmaddr必需页对齐)。指定了SHM_RND则此段连接到shmaddr- shmaddr%SHMLBA所表示的地址

                


2.4解除当前进程映射

#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void *shmaddr);

功能

        断开当前进程的共享内存

参数

        shmaddr:共享内存映射的地址

返回值

        成功:0

        失败:-1


2.5共享内存的控制

#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能

        共享内存空间控制

参数

        shmid:共享内存标识符

        cmd:函数功能控制

                IPC_RMID:删除。
                IPC_SET:设置shmid_ds参数。

                IPC_STAT:保存shmid_ds参数。
                SHM_LOCK:锁定共享内存段(超级用户)。

                SHM_uNLOCK:解锁共享内存段。

        buf:shmid_ds数据类型结构体,用来存放修改共享内存的结构体


3.创建并从共享内存中读写数据

c63adf468eaa4a7a876d5e86f959793e.png

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    //获取唯一Key值
    key_t key = ftok("/",2023);

    //生成物理内存,获得物理内存唯一标识符
    int shm_id = shmget(key, 16, IPC_CREAT | 0666);


    //虚拟内存映射物理内存
    char *add = (char *)shmat(shm_id, NULL, 0);


    //操作虚拟内存操作物理内存
#ifdef WRITE
    strcpy(add,"Hello!!!!!!!!!!!\n");
#endif // WRITE

#ifdef READ
    printf("读到的数据为%s\n",add);
#endif // READ

    //断开映射
    shmdt(add);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leviiil

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值