【Linux】system V共享内存与信号量

news2024/10/6 2:32:15

目录

一、前言

二、共享内存

1、基本原理

2、实现代码

2.1、创建共享内存

2.2、释放共享内存 

2.3、关联共享内存

2.4、与共享内存去关联

2.5、进程间通信

2.6、补充内容 

三、system V信号量

1、概念

2、信号量

3、相关接口

3.1、获取信号量

3.2、释放信号量 

3.3、信号量的PV接口

四、IPC资源管理方式


一、前言

 在上篇文章《管道》中,已经介绍了通过管道实现进程间通信的方式。本篇文章主要着重于 system V共享内存进行讲解。

 system V 是一套独立于操作系统外的标准,是一个专门为了通信设计出的内核模块,我们称之为 system V 的 IPC 通信机制。

 因为进程具有独立性,所以任何进程间通信的方式,首先要做的就是让不同的进程看到同一份资源。

二、共享内存

1、基本原理

 在物理内存中开辟一块空间,并在进程A和进程B的地址空间中分别通过页表与这一块空间建立映射关系,从而实现进程A和进程B共享一块内存。

 当进程通信结束后,只需要通过修改页表,取消掉进程A和进程B与共享内存的映射关系,并释放这块内存就可以了。

 操作系统中可能同时会有多对进程在通信。这就说明在任意时刻,可能有多个共享内存被用来进行通信。系统中有多个 shm 同时存在,就需要把他们根据先描述、再组织的方式管理起来。

 所以共享内存并不是只需要在内存中开辟空间就可以了,系统也要为了管理共享内存,构建描述共享内存的结构体struct shm。struct shm中存放共享内存的全部属性。共享内存 = 共享内存的内核数据结构 + 真正开辟的内存空间。

2、实现代码

2.1、创建共享内存

创建共享内存的接口:

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

 shmget 函数的参数列表中, key 是任意具有标定唯一性的数字。 size 表示所申请共享内存的大小。 shmflg 是创建共享内存的选项,常用以下两种选项:

  • IPC_CREAT:创建一个共享内存。如果共享内存不存在,就创建。如果已经存在,则获取已经存在的共享内存并返回。
  • IPC_EXCL:不能单独使用,一般都要配合IPC_CREAT使用。如果共享内存不存在,就创建。如果已经存在,则立刻出错返回。一旦创建成功,则对应的共享内存一定是最新的。

 如果想要对共享内存设置权限,则也可以在 shmflg 选项中,按位或 指定权限。

对于 key 值,我们一般使用 ftok 函数来设置。

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

 ftok 函数会结合参数列表中的路径字符串 pathname 与项目id proj_id ,形成一个重复概率非常低的key值。这样互相通信的两个进程就可以通过相同的参数,得到一个唯一的key值,从而找到同一块共享内存,进而实现进程间通信的前提:让不同的进程看到同一份资源。key本质是在内核中使用的。

 使用如下代码创建共享内存:

//comm.h

#ifndef __COMM_HPP__
#define __COMM_HPP__

#include <iostream>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
using namespace std;

#define PATHNAME "."
#define PROJID 0x0001

const int gsize = 4096;

key_t GetKey()
{
    key_t k = ftok(PATHNAME, PROJID);

    if(k == -1)
    {
        cerr << errno << " : " << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

string toHex(int x)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "0x%x", x);
    return buffer;
}

int creatShmHelper(key_t k, int size, int flag)
{
    int shmid = shmget(k, gsize, flag);
    if(shmid == -1)
    {
        cerr << errno << " : " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

int creatShm(key_t k, int size)
{
    return creatShmHelper(k, size, IPC_CREAT | IPC_EXCL);
}

int getShm(key_t k, int size)
{
    return creatShmHelper(k, size, IPC_CREAT);
}

#endif


//server.cc

#include "comm.hpp"

int main()
{
    //创建key
    key_t k = GetKey();
    cout << "server key: " << toHex(k) << endl;

    //创建共享内存
    int shmid = creatShm(k, gsize);
    cout << "server shmid: " << shmid << endl;
    return 0;
}


//client.cc

#include "comm.hpp"

int main()
{
    key_t k = GetKey();
    cout << "client key: " << toHex(k) << endl;

    int shmid = getShm(k, gsize);
    cout << "client shmid: " << shmid << endl;
    return 0;
}

编译运行,运行 server 程序,并且等待 server 进程结束后,再次运行 server 程序,会发现以下现象:

 原因是,共享内存的生命周期不随进程,随OS。进程结束后,所创建的共享内存不会被自动释放。

查看进程间通信内存资源的指令:

ipcs -m

 可以看到共享内存依然存在。

2.2、释放共享内存 

释放共享内存的指令:

ipcrm -m [shmid]

 此时就可以再次运行 server 程序创建共享内存了:

 共享内存除了使用指令释放外,还可以使用系统调用来释放

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

 shmctl 函数的参数列表中, shmid 指定共享内存。 cmd 表示想对共享内存做什么操作。 buf 可以获取该共享内存的属性,并存放在 buf 中。

 其中常用的 cmd 参数如下:

  • IPC_RMID:直接释放共享内存。
  • IPC_STAT:获取共享内存的属性。

1)IPC_STAT

获取共享内存的属性时,需要具备相应的权限,这里先设置一下:

 

编写代码: 

 编译运行:

 函数获取到了共享内存的属性。

2)IPC_RMID

 编译运行:

 进程结束,共享内存已经被释放。

2.3、关联共享内存

挂接共享内存的系统调用:

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

 shmat 函数的参数列表中, shmid 指定共享内存。 shmaddr 为共享内存挂接成功后得到的共享内存的虚拟地址的起始地址,一般设置为 NULL ,让系统自己挂接。 shmflg 为共享内存的选项,可以设置为只读属性,一般我们设置为 0 ,为读写属性。

新增代码:

 编译运行:

 观察到我们创建出的共享内存的 nattch 属性从 0 变为了 1 。这代表该共享内存的挂载数增加了。有几个进程与该共享内存相关联, nattch 就是几。

2.4、与共享内存去关联

与共享内存去关联的系统调用:

int shmdt(const void *shmaddr);

 shmdt 的函数参数列表中,ahmaddr shmat 函数的返回值,即共享内存挂载到虚拟内存中的起始地址。找到起始地址后,自然也就找到了共享内存的大小等属性,从而通过偏移量把虚拟地址对应的地址解关联。

新增代码:

2.5、进程间通信

为了使代码更加整洁,先把创建、关联、释放共享内存的代码封装成类:

#define SERVER 1
#define CLIENT 0
class Init
{
public:
    Init(int t):type(t)
    {
        key_t k = GetKey();
        if(type == SERVER)
            shmid = creatShm(k, gsize);
        else
            shmid = getShm(k, gsize);

        start = attachShm(shmid);
    }

    char* getStart() { return start; }

    ~Init()
    {
        detachShm(start);
        if(type == SERVER)
            delShm(shmid);
    }

private:
    char* start;
    int type;
    int shmid;
};

由于进程已经通过共享内存看到同一份资源了,接下来就借助共享内存实现进程间通信:

 获取共享内存的起始地址,并通过起始地址读写共享内存中的数据。

2.6、补充内容 

  •  关于共享内存的大小:共享内存的大小是以 PAGE 页(4KB)为单位分配的。即OS所分配的共享内存的大小一定使 4KB 的倍数。但是,OS给分配了这么多,并不代表进程就可以使用这么多。比如,进程申请了 4097 字节的共享内存,因为超出了 4KB 大小,OS给该进程分配了 8KB 大小的共享内存,但是该进程只能使用其中的 4097 个字节,其他字节使用不了。
  •  关于共享内存使用:可以看到上面的通信代码中并没有使用任何接口,这是因为一旦共享内存映射到进程的地址空间,该共享内存就直接被所有的进程直接看到了,无需使用系统调用接口。
     因为共享内存的这种特性,可以让进程通信的时候,减少拷贝次数,所以共享内存是所有进程间通信最快的。也因此,共享内存没有任何的保护机制。

三、system V信号量

1、概念

  • 互斥:任何一个时刻,都只允许一个执行流进行共享资源的访问。
  • 临界资源:任何一个时刻,都只允许一个执行流进行访问的共享资源。
  • 临界区:凡是访问临界资源的代码存放的地方,叫做临界区。对临界资源进行保护,实际上是对临界区进行保护,也是对代码进行保护。
  • 原子性:要么不做,要么做完,这种只有两种确定状态的属性,被称为原子性。

2、信号量

 信号量又被叫做信号灯,本质上是一个描述资源数量的计数器。

 任何一个执行流,想访问临界资源中的一个资源的时候,不能直接访问,而要先申请信号量资源。如果申请到了,信号量执行 "--" 操作,表示一个临界资源已经被占用,这个过程称为 P 操作。这是一个预定机制,只要申请成功,那么这个执行流就一定能够拿到一个子资源,在需要的时候就能够进入临界区,访问对应的临界资源。如果信号量为 0 ,就表示已经没有临界资源了,后面再申请的执行流会进入阻塞状态。当执行流访问临界资源结束后,信号量执行 "++" 操作,表示将对应的资源进行了归还,这个过程称为 V 操作。

 互斥功能本质就是将临界资源独立使用,即将信号量设置为 1

 因为进程在访问临界资源时,都要申请信号量,这就意味着所有的进程都得看到同一个信号量,即信号量本身也是一个共享资源。为了保护自身的安全,就需要信号量自己的 "++" 与 "--" 操作都是原子性的。这部分内容在后面讲进程信号时会着重讲解。

3、相关接口

3.1、获取信号量

获取信号量的接口:

int semget(key_t key, int nsems, int semflg);

 semget 函数的参数列表中, key 是一个具有唯一性的数字。 nsem 为申请信号量的个数(与一个信号量是几进行区分),称为信号量集。 semflg 是选项,常用的有 IPC_CREAT IPC_EXCL ,不再重复介绍。返回值是信号量标示符。

查看信号量指令:

ipcs -s

3.2、释放信号量 

 释放信号量的指令:

ipcrm -s [信号量shmid]

释放信号量的系统调用:

int semctl(int semid, int semnum, int cmd, ...);

 semctl 函数的参数列表中, semid 表示要对哪一个信号量集进行操作。 semnum 表示要对哪一个信号量进行操作。 cmd 是操作选项。 "...是可变参数,用来指定获取信号量的相关属性。

3.3、信号量的PV接口

int semop(int semid, struct sembuf *sops, unsigned nsops);

 semop 函数的参数列表中, semid 表示要对哪一个信号量集进行操作。 sops 是一个结构体指针,需要自己定义,结构体由如下部分构成:

 sem_num 表示哪一个信号量。 sem_op 表示进行什么操作(比如设置为 1 或 -1 ,表示加与减操作)。 sem_flg 是选项,设置为默认就可以。

 nsops  sem_num 相同,表示哪一个信号量。

四、IPC资源管理方式

 无论是共享内存、消息队列还是信号量,它们的结构体虽有很多不同,但是都在结构体第一个字段包含了另一个结构体 IPC_perm

共享内存:

 消息队列:

 信号量:

 在OS中,是以 ipc_id_ary 数组的方式来管理所有的 ipc 资源的。因为所有 ipc 资源结构体的起始字段的类型都一样,都是 ipc_perm 。其简化模型与简化原理是下面这样的:

 ipc_id_arr 是一个存储 struct ipc_perm* 类型数据的指针数组,因为每一个ipc资源的起始字段都是 struct ipc_perm* 类型,所以使用数组中的指针寻找到结构体起始地址,就相当于找到了整个结构体的地址,只需要找到起始地址后,把该指针强转成对应ipc资源的结构体类型就可以了。

 其中数组的下标就是IPC资源的返回值,也叫做标示符。之所以我们所看到的标示符很大,是因为标示符是递增的,数组下标是循环使用的,当数组越界后,会回到下标为 0 的位置,但是标示符不会清零。

 以上原理是对多态的应用。


 关于system V共享内存与信号量的相关内容就讲到这里,希望同学们多多支持,如果有不对的地方,欢迎大佬指正,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/510134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习模型————分类模型

K近邻&#xff08;KNN&#xff09; 逻辑斯提回归 Sigmoid函数&#xff08;压缩函数&#xff09; 梯度下降法求解

叶工好容2-云原生网络

前言 IT行业中网络技术本身就是最枯燥但又最复杂的一块&#xff0c;kebernetes用到的这些虚拟网络技术更是难上加难&#xff0c;许多知识点容易记混淆&#xff0c;或者说能背过概念但不清楚它出现的前因后果和应用场景&#xff0c;本篇博文会按照底层协议->上层技术->顶…

工业园区的智慧安监方案:AI视频边缘计算技术的应用场景剖析

一、方案背景 针对工业园区化工企业多且安全及环保等方面存在风险高、隐患多、精细化管控复杂的情况&#xff0c;需要全面整合并优化园区现有基础设施、系统平台等信息化资源&#xff0c;建立园区的智能化风险预警管理平台&#xff0c;利用信息化手段&#xff0c;增强园区安全…

Baklib知识库搭建平台产品操作手册

产品概述 Baklib是一款专业的知识库搭建平台&#xff0c;它帮助客户搭建内部知识库和对外帮助中心。在今天的信息时代&#xff0c;知识已经成为组织的核心竞争力&#xff0c;而Baklib正是为了帮助组织构建完整的知识体系&#xff0c;提高组织的核心竞争力而生。 Baklib具有以…

刷题之另类加法与走方格的方案数

目录 一、编程题 1.另类加法 2.走方格的方案数 二、选择题 一、编程题 1.另类加法 链接&#xff1a;另类加法_牛客题霸_牛客网 (nowcoder.com) 描述 给定两个int A和B。编写一个函数返回AB的值&#xff0c;但不得使用或其他算数运算符。 测试样例&#xff1a; 1,2 返回&am…

GitHub简单使用

一、安装git客户端(查看版本git --version)&#xff1a; Linux&#xff1a;yum install -y git Mac:brew install git windows:https://git-scm.com/downloads 配置git(可通过git config --list查看配置的内容)&#xff1a; git config --global user.name "用户名&qu…

计算机视觉——day89 Restormer:高效的高分辨率图像恢复变压器(CVPR简读)

Restormer&#xff1a;高效的高分辨率图像恢复变压器&#xff08;CVPR简读&#xff09; 1. Introduction3. Method4. 实验与分析5. 结论 Restormer: Efficient Transformer for High-Resolution Image Restoration 1. Introduction 摘要部分简要总结了一下当前transformer的广…

(附源码)计算机毕业设计Java对外汉语教学辅助平台

项目运行 &#x1f345;包售后&#xff0c;包调试&#xff0c;包讲解&#x1f345; &#x1f345;获取方式1:文章末尾获取联系&#x1f345; &#x1f345;获取方式2:点我进入&#xff0c;文章末尾获取联系&#x1f345; &#x1f345;包售后&#xff0c;包调试&#xff0c;包讲…

SpringBoot 创建和使用

SpringBoot 创建和使用 1. 初识 SpringBoot Spring 的诞生是为了简化 java 程序的开发 , 而Spring Boot 的诞生是为了简化 Spring 程序开发. Spring boot 翻译以下就是"脚手架" , 有了脚手架就可以快速的开发和使用 Spring . 2. Spring Boot 优点 快速的集成框架…

【JSON】谷歌浏览器JSON可视化插件:JSON-Handle

摘要 : JSON-handle是一款对JSON格式的内容进行浏览和编辑&#xff0c;以树形图样式展现JSON文档&#xff0c;并可实时编辑。 今天我推荐一款chrome/Firfox下处理json的插件JSON-handle&#xff0c;这个应该是我用过最好最方便的了。 插件功能 实际开发工作中经常用到json数据&…

基于无线传感网络(WSN)的目标跟踪技术(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 无线传感器网络由于其自组织性、鲁棒性及节点数量巨大的特点,非常适合于目标跟踪。无线传感器网络中的移动目标跟踪实际上就是…

centerpoint论文和代码解读

目录 一、序论 二、论文结构 三、代码 论文地址&#xff1a; https://arxiv.org/pdf/2006.11275.pdf 代码地址&#xff1a;tianweiy/CenterPoint (github.com) 一、序论 centorpoint是一种anchor-free的方法&#xff0c;直接预测物体的中心点&#xff0c;然后直接回归其wh…

《微服务实战》 第四章 Spring Cloud Netflix 之 Eureka

前言 Eureka 是 Netflix 公司开发的一款开源的服务注册与发现组件。 Spring Cloud 使用 Spring Boot 思想为 Eureka 增加了自动化配置&#xff0c;开发人员只需要引入相关依赖和注解&#xff0c;就能将 Spring Boot 构建的微服务轻松地与 Eureka 进行整合。 1、Eureka 两大组…

三维后处理与重建PACS源码,大容量图像存储 报告单多种模式及自定义样式

医学影像系统源码 三维后处理与重建PACS源码 医学影像系统由PACS系统、RIS系统组成&#xff0c;同时提供与HIS的接口&#xff08;HL7或其他类型&#xff09;。 主要功能介绍 信息预约登记 支持对患者、检查项目、申请医生、申请单据、设备等信息进行管理。且支持检查病人排…

SwiftUI中DatePicker学习

在界面开发中&#xff0c;经常要使用到DatePicker控件&#xff0c;如年月日&#xff0c;时分选择。 但我们还要修改它的显示方式&#xff0c;文字语言&#xff0c;及其他功能 先看下最简单的调用方法就是&#xff1a; State private var date Date()var body: some View {D…

STM32F10X--中断

中断 中断优先级数值越小&#xff0c;中断优先级越高 中断编程的顺序 1、使能的是外设&#xff0c;配置外设寄存器 2、配置中断优先级分组&#xff0c;在msic.h里面有中断优先级组分组函数 这个函数里面配置了SCB->AIRCR寄存器 3、配置NVIC寄存器&#xff0c;初始化NVIC…

可视化和回归分析星巴克咖啡在中国的定价建议

可视化和回归分析星巴克咖啡在中国的定价建议。星巴克的拿铁大杯Tall 在各国的价格。 Claude AI | 代码自动生成的数据可视化代码 选择Claude AI 而非 ChatGPT的理由是前者更懂中文​&#xff01;具体可以参见我前面的两篇文章对比两者的中英文翻译的表现及使用安装等难易程度​…

2.SpringBoot运维实用篇

SpringBoot运维实用篇 ​ 基础篇发布以后&#xff0c;看到了很多小伙伴在网上的留言&#xff0c;也帮助超过100位小伙伴解决了一些遇到的问题&#xff0c;并且已经发现了部分问题具有典型性&#xff0c;预计将有些问题在后面篇章的合适位置添加到本套课程中&#xff0c;作为解…

做公有云服务,为什么对象存储不可或缺?

试问&#xff1a;公有云的竞争&#xff0c; 你觉得从什么时候开始白热化了&#xff1f; 【全球云观察 &#xff5c; 热点关注】对于这个问题&#xff0c;可谓仁者见仁智者见智。 在我看来&#xff0c;火山引擎还未推出全面的云服务之前&#xff0c;在国内的公有云领域&#x…

KingbaseES V8R6运维案例之---MySQL和KingbaseES字符串排序规则对比

案例说明&#xff1a; 相同数据排序后查询&#xff0c;在MySQL和KingbaseES下得到的排序顺序不一致&#xff0c;本案例从MySQL和KingbaseES的排序规则分析&#xff0c;两种数据库排序的异同点。适用版本&#xff1a; KingbaseES V8R6、MySQL 8.0 一、MySQL的排序规则1、排序规则…