当前位置: 首页 > news >正文

【LINUX操作系统】通过System V看内核管理IPC资源

1. 从应用层看shm msg sem的区别与联系

操作\通信方式共享内存消息队列信号量
申请资源使用shmget函数申请共享内存段,指定内存段的大小和访问权限等参数。使用msgget函数创建或获取一个消息队列,指定消息队列的标识符和访问权限等参数。使用semget函数创建或获取一个信号量集,指定信号量集的标识符和信号量数量等参数。
操作资源使用shmat函数将共享内存段附加到当前进程的地址空间,之后可以通过指针进行常规读写操作。操作完成后,使用shmdt函数将共享内存段从当前进程的地址空间分离。使用msgsnd函数向消息队列发送消息,指定消息队列的标识符、消息的类型和内容等参数。使用msgrcv函数从消息队列接收消息,指定消息队列的标识符、消息的类型和接收缓冲区等参数。使用semop函数对信号量集进行操作,可以执行P操作(等待信号量)、V操作(释放信号量)等。
释放资源使用shmctl函数对共享内存段进行控制操作,如删除共享内存段(指定IPC_RMID命令)。使用msgctl函数对消息队列进行控制操作,如删除消息队列(指定IPC_RMID命令)。使用semctl函数对信号量集进行控制操作,如删除信号量集(指定IPC_RMID命令)。
因为遵循着同一个标准,所以三者的操作都大差不。
并且,Linux底层对整个资源的管理也有很多相似之处。

我们一直强调“先描述、再组织”,现在来看一下这三种通信方式的组织结构是不是差不多。 

可以直接在机器上通过man xxxctl 查看!

man shmctl

共享内存对应属性的数据模块

消息队列:

信号量:

 三者的第一个成员都是struct ipc_perm结构,这个结构用于存放当前资源的相关权限以及标识符!例如其中的__key表示资源在系统中的编号,另外还有对应的uid表示持有当前资源的用户,三者除了有struct ipc_perm结构外,还有一些其他成员也基本类似。

在单独学习某一种资源的时候,xxxget就是用key_t 类型的 key去生成一块资源。

同时,key也变成了区分ipc资源的唯一标识符。

由此不难看出,整个操作系统几乎都是以同样的方式在管理整个IPC,三种不同的资源遵守同样的组织方法。

在之前的共享内存的代码中,可以通过如下来获取各个IPC资源的信息:

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

int _shmid; // 假设已经定义并初始化

void ShmMeta()
{
    struct shmid_ds buffer; // 系统提供的数据类型
    int n = shmctl(_shmid, IPC_STAT, &buffer);
    if (n < 0) return;

    std::cout << "Last attach time: " << buffer.shm_atime << std::endl;
    std::cout << "Process ID of creator: " << buffer.shm_cpid << std::endl;
    std::cout << "Last change time: " << buffer.shm_ctime << std::endl;
    std::cout << "Number of current attaches: " << buffer.shm_nattch << std::endl;
    std::cout << "Key: " << buffer.shm_perm.key << std::endl; // 使用标准的成员名
}

2. 从内核角度看三者的联系与区别

kernel_thread identifier - Linux source code v6.12.6 - Bootlin Elixir Cross Referencer

可用以上链接查linux源码。

现在来学习一下管理IPC资源的内部结构

在Linux 2.6版本的内核中,使用ipc_ids:

          

ipc_ids的名字是否与shm_ids,sem_ids,msg_ids联系很大?

每个ipc_ids里有一个ipc_id_ary,这个结构体的定义也被清晰写出,是一个size+一个柔性数组构成,柔性数组里记录的主要是ipc_perm的内容。

这个结构中的struct kern_ipc_perm实际上就是应用层的struct ipc_perm: 

struct kern_ipc_perm {
    key_t key;
    spinlock_t lock;
    int deleted;
    uid_t uid;
    gid_t gid;
    uid_t cuid;
    gid_t cgid;
    mode_t mode;
    unsigned long seq;
    void *security;
};

可以说,内核的ipc_id_ary数组就就记录下了所有ipc资源。

                                     

我们使用kern_ipc_perm就可以去类似的构建shm_perm等等

struct shmid_kernel {
    struct kern_ipc_perm shm_perm;
    struct file *shm_file;
    int id;
    unsigned long shm_nattch;
    unsigned long shm_segsz;
    time_t shm_atim;
    time_t shm_dtim;
    time_t shm_ctim;
    pid_t shm_cprid;
    pid_t shm_lprid;
};

 因为每一个struct ipc_ids对象都有一个struct kern_ipc_perm成员,这个指针指向着一个动态开辟的空间(柔性数组)

又因为C语言中首元素地址就是整个结构体的地址

故:假设p就是ipc_id_ary

此时操作系统就可以通过管理struct kern_ipc_perm *p统一对三个资源进行管理,而因为struct kern_ipc_perm中含有相关的成员,例如key,所以这个key实际上是一个三个资源共有的成员,也就是说三个资源管理的是同一种key

其实,在CPP的角度,这就是多态的表现。其中struct kern_ipc_perm就是基类(父类),struct shmid_kernelstruct msg_queuestruct sem_array就是派生类(子类) 

3. 再次理解共享内存 

在shm_kernel中,有一个特殊的变量

为什么内存中也会有这个接口?

这是因为,共享内存的本质也是打开一个文件,但是不调用文件相关的接口,所以快。

既然如此,被 共享内存内存 中打开的文件就需要将对应的地址映射到进程的虚拟地址空间,在进程地址空间结构的vm_area_struct中也存在一个结构如下:

这就是我们之前聊的在mm_struct中管理具体的_start和_end的结构体,原来它里面的确存在一个对应的file*结构体,用于接受共享内存打开的内容文件。

通过这个file*指针,就可以将共享内存这个文件映射到进程地址空间 ,因此在shmat的时候,挂接就是挂接到这个file*指针上,进程可以直接使用这个指针!

同样,如果需要将用户打开的文件映射到进程地址空间,可以使用mmap接口:

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

需要注意,如果文件已经映射到了进程的地址空间,但是文件在取消映射之前已经关闭,此时不会自动取消映射,所以关闭文件后还需要手动解除映射,可以使用munmap接口取消映射 

http://www.dtcms.com/a/106707.html

相关文章:

  • 经典算法 最大子段和
  • UE5学习笔记 FPS游戏制作37 蓝图函数库 自己定义公共方法
  • uni-app 框架 调用蓝牙,获取 iBeacon 定位信标的数据,实现室内定位场景
  • 求解传递闭包
  • 花洒洗澡完毕并关闭后过段时间会突然滴水的原因探究
  • 快速在 Windows 平台上高效安装flash_attn库
  • 【C++重点】std::map
  • STM32入门学习笔记(持续更新)
  • 如何使用Python通过STOMP协议接收ActiveMQ消息
  • The Rust Programming Language 学习 (九)
  • zkTLS 工作原理
  • 【C++初阶】--- string类
  • 23种设计模式-结构型模式-代理
  • jvm 的attach 和agent机制
  • 小白编程教程,编程设计中的三大程序控制结构,扣子平台的循环节点如何使用?扣子免费系列教程(26)
  • 质量和工艺之间的区别与联系?
  • 介绍 Docker 的基本概念和优势,以及在应用程序开发中的实际应用及数组讲解
  • 玛卡巴卡的k8s知识点问答题(七)
  • 2025年2月一区SCI-壮丽细尾鹩莺算法Superb Fairy-wren Optimization-附Matlab免费代码
  • 【C++继承】关于继承的细节分析
  • yolo11参数信息
  • 学习总结 网格划分+瞬态求解设置
  • vector模拟实现2
  • Windows系统服务器安装Office Online Server
  • C语言复习笔记--指针(2)
  • 详解 MySQL InnoDB 引擎中的聚簇索引和非聚簇索的区别
  • OpenCV边界填充方法详解
  • Python入门(6):面向对象编程(OOP)
  • Smith Chart阻抗匹配
  • elasticsearch 7.17 索引模板