【Linux】system V 共享内存

system V是一套标准,专门设计出来为了通信。

之前谈到匿名管道和命名管道都涉及文件操作。共享内存不涉及到文件,无需借助内存交互,是IPC最快的形式。

system V共享内存原理

利用共享内存实现通信,同样需要再内存中,让不同进程A 和进程B看到同一份资源。

在进程A创建的时候,会通过在进程地址空间确定位置,形成缺页中断,在进程实际需要访问时,会在物理内存申请空间。通过页表映射起来。如果能让进程A和进程B在申请内存的时候都申请在同一个位置上,都能实现看到同一份资源,实现进程通信。

因此在物理内存上,有一块属于"大家的内存"就是共享内存。

共享内存是在堆栈之间。俩个的部分代码进程会分布在物理内存的同一块空间,将物理内存通过页表映射到mm_struct的共享内存区,并返回起始地址给用户,就能实现对一块资源的读写。

不止一对进程能在共享内存中,可以有多对同时参与。管理共享内存的方式就是先描述再组织。

共享内存=共享内存结构体shm+实际内存

共享内存的函数

shmget创建共享内存

函数原型

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);

  • 作用:向内存申请一块共享内存
  • 返回值:

申请成功返回用户级标志符

申请失败返回 -1 错误码被设置

  • 参数:
  1. key :需要创建的内存在系统的唯一标志
  2. size:需要创建的内存大小 一般为n*4096
  3. shmflg:创建方式

是一个宏,位图的操作,与文件open选项类似

有俩种选项:
1)IPC_CREAT 创建一个共享内存,不存在就创建,存在就返回已创建的地址

2)IPC_CREAT | IPC_EXCL 创建一个共享内存,已存在就失败返回

换句话说 IPC_CREAT 无法确保内存是当前调用创建

IPC_CREAT | IPC_EXCL 确保内存是新建的

shmid是用户标识,key是内核级的标志

就类似于fd 和inode的关系


为什么key需要用户提供?
为了让用户在这俩个通信的进程之间知道。假设server中输入key,那么只有通过唯一的标识符才能找到共享内存。client和server是俩个独立的进程,因此在通信之前必须用户提供key。

获取唯一的key

ftok函数

是一个算法函数,根据路径名和项目id创建一个key,它的冲突概率及低(有点hash算法的感觉了)

创建key的意义

利用标识符创建标记唯一的共享内存,提供给后续的进程寻找到这块共享内存

举例:创建一个key

创建出来的key会很大,转成十六进制,更有利于观察

利用snprintf函数转化


创建共享内存

利用shmget函数

因为共享内存有俩种情况

1.还没有被创建,创建共享内存。

2. 已经被创建,用于查找,返回内存地址

由于这俩种情况的差别就在于flag上,利用子函数调用减少代码的冗余。

int CreateShmgetHelp(const key_t &key, int flag)
{
    int shmid = shmget(key, size, flag);
    if(shmid<0)
    {
        std::cerr<<"cerr: "<<errno<<",errstring: "<<strerror(errno)<<std::endl;
        return 1;
    }
    return shmid;
}

int Creatshmid(const key_t &key)
{
    return CreateShmgetHelp(key,IPC_CREAT|IPC_EXCL);
}

int Getshmid(const key_t &key)
{
    return CreateShmgetHelp(key,IPC_CREAT);
}

IPC资源

共享内存的生命周期不随进程,而是跟谁OS,这是system V的特性

下面介绍查看IPC资源的指令

perms表示权限  bytes内存大小 nattch表示有几个进程和内存关联 

  • ipcs -m 查看共享内存的资源

  • ipcs -q 查看消息队列的资源

  • ipcs -s 查看信号量

  • ipcs 三个一起查看

挂起 shmat

将地址空间和内存关联

man shmat

函数有三个参数 

  • shmid为用户要关联的 id   
  • shmaddr为用户要放置在内存区哪一块位置,nullptr表示让系统帮忙选择
  • shmflag 可以设置为SHM_RDONLY 表示权限只读   一般为0表示只读

返回值为内存起始地址

起始地址为void* 一般都需要强制转化为需要的类型


去关联shmdt

与shmat相反,将页表的映射取消掉,就相当于物理内存还是,地址空间的使用被取消

参数是void* 从shmat接收到的返回值


删除共享内存

因为是指令删除 是用户层 故删除的时候需要记住 shmid

ipcrm -m shmid 就能ipc资源

ipcrm -m 7


系统调用shmctl

共享内存是需要管理的,在内存的起始地址,是存放描述的结构体,里面会包含属性信息

shmctl用于操作共享内存

参数介绍:
shmid:控制共享内存的标识符。
cmd:控制的种类,主要用的是IPC_RMID(立即移除共享内存)
buf:控制共享内存的数据结构,设置为空即可。
返回值:0表示返回成功,-1表示失败。


效果展示

如何显示同步?

但是共享内存是不提供同步机制的

为了实现同步--写端每次输入一个,读端就会等待。

利用命名管道的辅助实现同步

共享内存的效率

是所有通信中,速度最快的一种通信方式

和管道比起来,至少减少几次拷贝?

凡是数据迁移都需要拷贝!

带硬件

假设从数据键盘文件缓冲区 1次 ,数据到调用writr写到文件缓冲区需要1次

从文件缓冲区通过read调用读到缓冲区需要1次 缓冲区到显示器1次

不考虑C语言接口的缓冲区 2 次

那么一共就有4次。

共享内存是通过映射,对于内存的同一个位置。假设从键盘读到内存 1次。从内存读到写到显示器1次。 共俩次。

所以至少减少了2次拷贝。


内核如何看待IPC资源

IPC资源是通过数组管理起来的    IPC_id_arrary

通过下标就能找到消息队列、共享内存、信号量等等

每一种资源都是被结构体stmid_ds描述起来,它们结构体的第一个内容是ipc_perm

struct Ipc_Perm
{
    //key
    //权限
}

OS通过ipc_ids管理这些字段,ipc_ids里面有一个柔性数组,柔性数组的内容是存放指针

指针指向ipc_perm 。当我们想申请贡献内存的时候,OS会创建shmid_ds结构体对象,并且添加ipc_id _arrary的指针。柔性数组的好处我们可以在使用时,再考虑申请多大的内存

通过指针数组的强转结构体的第一个段内容,就是多态的思想!

通过这样的方式就能统一管理数据!
 

信号量

本质是一个计数器,用来保护资源。

当资源被使用,计数器就会--,但资源被释放,计数器++。

当信号量为1时,表现为具有互斥性,我们称之二元信号量,信号量表现只有0和1,是锁的体现

如何理解信号量

信号量是标记资源是否被不同进程使用,那么势必要让进程看到信号量,那么信号量也是资源,也需要计数器保护,这不是矛盾吗?
信号量必须是原子的!就是要么完成,要么没做。

信号量的操作

semop -- (申请资源) P操作

semop ++  (释放资源)V操作

信号量的接口与共享内存相近,不作介绍。


共享内存,是映射到物理内存中。获取shmget(),key size flag都是有讲究,返回的shmid是需要被记录。创建后就需要挂起shmat 返回地址空间的起始地址,取消关联shmdt,在物理内存取消共享空间shmctl(id,IPC_RMID,nullptr)

共享内存是可以提供大容量的,不支持同步机制,是效率速度最快的通信方式。

  • 35
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

深度搜索

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

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

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

打赏作者

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

抵扣说明:

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

余额充值