Linux进程间通信(system V共享内存)

news2024/11/22 21:03:52

共享内存原理

看上面这张图,其实只要是进程间通信都离不开让他们看到同一块资源(内存),其实共享内存这里和动态库那里一样,都是要加载到共享区,共享内存提供者,是操作系统,操作系统要不要管理共享内存,当然要的,先描述再组织,重新理解,共享内存=共享内存块+对应的共享内存的内核数据结构。

共享内存有关函数介绍

代码创建共享内存,接下来介绍一下这个函数

返回值:共享内存的用户层标识符,类似曾经的fd,失败返回-1

key:要通信的对方进程,怎么保证,对方能看到,并且看到的就是我创建的共享内存呢,通过key,数据是几,不重要只要能够在系统唯一即可,server && client ,使用同一个key 只要key值相同,就是看到了同一个共享内存,这里就需要用到一个函数ftok来生成,下面再仔细说。

size:这个就是你要创建的共享内存多大,共享内存的大小,最好是页(PAGE: 4096)的整数倍。

shmflg:有这两个参数IPC_CREAT and IPC_EXCL,单独IPC_CREAT,如果创建共享内存,如果底层已经存在,获取之,并且返回,如果不存在,创建之,并返回,单独使用IPC_EXCL,没有意义 ,IPC_CREAT和IPC_EXCL一起,如果底层不存在,创建之,并返回,如果底层存在,出错返回,返回成功一定是一个全新的shm

返回一个key值,失败返回-1

pathname:这个参数,是传一个地址,最好传一个有权限访问的地址

proj_id:这个随便传一个数即可。

这个函数的作用是删除共享内存,这个第一个参数就是之前创建共享内存时返回的那个值,cmd就以什么方式删除(IPC_RMID),最后那个参数是操作系统管理共享内存的数据结构,可以传nullptr

perms是权限,nattch有多少个是与我有关联的,owner是所有者,bytes是大小。

 

 shmat将指定的共享内存挂接到自己的地址空间,返回的就是这个共享内存的起始地址,第一个参数就不说了,前面有,第二个传nullprtr即可,第三个传0,shmdt去除关联,传前面那个函数的返回值即可

共享内存使用

接下来代码演示:

//Log.hpp
#ifndef _LOG_H_
#define _LOG_H_

#include <iostream>
#include <ctime>

#define Debug   0
#define Notice  1
#define Warning 2
#define Error   3


const std::string msg[] = {
    "Debug",
    "Notice",
    "Warning",
    "Error"
};

std::ostream &Log(std::string message, int level)
{
    std::cout << " | " << (unsigned)time(nullptr) << " | " << msg[level] << " | " << message;
    return std::cout;
}


#endif
//Makefile
.PHONY:all
all:shmClient shmServer

shmClient:shmClient.cc
	g++ -o $@ $^ -std=c++11
shmServer:shmServer.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f shmClient shmServer
//comm.hpp
#pragma once

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cassert>
#include "Log.hpp"

using namespace std; 

#define PATH_NAME "/home/chx"
#define PROJ_ID 0x66
#define SHM_SIZE 4096 
//shmClient.cc
#include "comm.hpp"

int main()
{
    key_t k = ftok(PATH_NAME, PROJ_ID);
    if (k < 0)
    {
        Log("create key failed", Error) << " client key : " << k << endl;
        exit(1);
    }
    Log("create key done", Debug) << " client key : " << k << endl;

    // 获取共享内存
    int shmid = shmget(k, SHM_SIZE, 0);
    if (shmid < 0)
    {
        Log("create shm failed", Error) << " client key : " << k << endl;
        exit(2);
    }
    Log("create shm success", Error) << " client key : " << k << endl;

    sleep(10);

    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if (shmaddr == nullptr)
    {
        Log("attach shm failed", Error) << " client key : " << k << endl;
        exit(3);
    }
    Log("attach shm success", Error) << " client key : " << k << endl;
    sleep(10);

    // 使用

    // 去关联
    int n = shmdt(shmaddr);
    assert(n != -1);
    Log("detach shm success", Error) << " client key : " << k << endl;
    sleep(10);

    // client 要不要chmctl删除呢?不需要!!

    return 0;
}
//shmServer.cc
#include "comm.hpp"

int main()
{
    // 1. 创建公共的Key值
    key_t k = ftok(PATH_NAME, PROJ_ID);
    assert(k != -1);
    Log("create key done", Debug) << " server key : " << k << endl;
    // 2. 创建共享内存 -- 建议要创建一个全新的共享内存 -- 通信的发起者
    int shmid = shmget(k, SHM_SIZE, IPC_CREAT | 0666 | IPC_EXCL);
    if (shmid == -1)
    {
        perror("shmget");
        exit(1);
    }
    Log("create shm done", Debug) << " shmid : " << shmid << endl;
    // 3. 将指定的共享内存,挂接到自己的地址空间
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    Log("attach shm done", Debug) << " shmid : " << shmid << endl;

    sleep(10);

    // 4. 将指定的共享内存,从自己的地址空间中去关联
    int n = shmdt(shmaddr);
    assert(n != -1);
    (void)n;
    Log("detach shm done", Debug) << " shmid : " << shmid << endl;
    sleep(10);

    // 5. 删除共享内存,IPC_RMID即便是有进程和当下的shm挂接,依旧删除共享内存
    n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;
    Log("delete shm done", Debug) << " shmid : " << shmid << endl;
    return 0;
}

先运行Server端创建共享内存,然后让client去关联申请的共享内存,即可。

共享内存总结

堆栈相对而生,其中这块区域就是可以存放共享内存、内存映射和共享库,如果双方进程想进行通信,可以直接进行访问,也就是内存级的读和写。之前在文件操作中,我们使用系统调用read、write这些操作,这些属于内核级操作。而共享内存的使用是直接在进程内部进行操作的,所以效率比管道高,其不用经过内核处理。

首先我们要明白一点就是,共享内存是在内核空间的,还是用户空间,其实肯定是用户空间,不用经过系统调用,直接可以访问,双方进程如果要通信,直接进行内存级的读和写即可,我们之前讲的pipe,fifo都要通过read、write来进行通信,这是为什么呢,因为管道是文件级别的操作,而文件是操作系统负责的,处于3-4G内核空间的,所以需要调用系统调用接口,所以与之相比较他就减少了很多拷贝,所以也就快了。 

但是共享内存缺乏访问控制,会带来并发问题。比如写方还没有将数据写完,读端就将数据读取了。

共享内存实现访问控制 

 首先我们使用 Init类,当创建对象时,就创建了fifo管道,当进程结束时fifo自动删除。

然后我们定义以下4个接口,OpenFIFO、Wait、Signal、Closefifo本质对应的是open打开fifo文件;Wait对应read等待数据的写入,无数据则阻塞;Signal对应Client端进行write写入数据;Closefifo调用close关闭文件。


class Init
{
public:
    Init()
    {
        umask(0);
        int n = mkfifo("./fifo", 0666);
        assert(n == 0);
        (void)n;
        Log("create fifo success", Notice) << endl;
    }
    ~Init()
    {
        unlink("./fifo");
        Log("create fifo success", Notice) << endl;
    }
};
//创建管道
Init init;
 
#define READ O_RDONLY
#define WRITE O_WRONLY
int OpenFIFO(string pathname, int flags)
{
    Log("等待中……", Notice) << endl;
    int fd = open(pathname.c_str(), flags);
    assert(fd >= 0);
    return fd;
}
 
void Wait(int fd)
{
    uint32_t temp = 0;
    ssize_t s = read(fd, &temp, sizeof(uint32_t));
    assert(s == sizeof(uint32_t));
    Log("唤醒中……", Notice) << endl;
}
void Signal(int fd)
{
    uint32_t temp = 1;
    ssize_t s = write(fd, &temp, sizeof(uint32_t));
    assert(s == sizeof(uint32_t));
    (void)s;
}
void Closefifo(int fd)
{
    close(fd);
}
//Server端改动:
  int fd = OpenFIFO("./fifo", READ);
    for (;;)
    {
        Wait(fd);
        printf("%s\n", shmaddr);
        sleep(1);
        if (strcmp(shmaddr, "quit") == 0)
        {
            printf("quit\n");
            break;
        }
    }
    Closefifo(fd);
 
//Client端改动:
    int fd = OpenFIFO("./fifo", WRITE);
    while (true)
    {
        ssize_t s = read(0, shmaddr, SHM_SIZE - 1);
        Signal(fd);
        if (strcmp(shmaddr, "quit") == 0)
            break;
    }
    Closefifo(fd);

这样,server端就具有了访问控制,如果没有数据,则阻塞等待数据。

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

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

相关文章

Python环境搭建指南

Python能做太多有趣使用的事了&#xff0c;不仅可以做现在火热的人工智能、数据分析&#xff0c;还可以做爬虫、Web开发、自动化运维的事情。 随着Python为我们工作与生活带来更多的便捷后&#xff0c;很多人开始学习Python&#xff0c;关注Python的发展前景、薪资和职业素养的…

RabbitMQ——高级特性(SpringBoot实现)

本篇文章的内容与我之前如下这篇文章一样&#xff0c;只是使用技术不同&#xff0c;本篇文章使用SpringBoot实现RabbitMQ的高级特性&#xff01; RabbitMQ——高级特性_小曹爱编程&#xff01;的博客-CSDN博客RabbitMQ——高级特性&#xff1a;1、RabbitMQ高级特性&#xff1b;…

IDEA2021.3新建一个maven-archetype-webapp项目,发现在新建文件汇总,没有新建servlet文件选项

问题 我使用maven-webapp 模板新建了一个web项目&#xff0c;但是在新建文件的时候&#xff0c;发现没有servlet选项 解决 第一种&#xff1a;在pom中直接配置 <dependencies><dependency><groupId>javax.servlet</groupId><artifactId>java…

Linux进程信号产生以及捕捉

一.什么是信号 生活中&#xff0c;有哪些信号相关的场景呢&#xff0c;比如&#xff1a;红绿灯&#xff0c;闹钟&#xff0c;转向灯等等 1.这里我们要知道&#xff0c;你为什么认识这些信号呢&#xff0c;记住了对应场景下的信号&#xff0b;后续是有”动作“要你执行的 2.我们…

spring boot 项目打包镜像方式以及区分环境打包

springboot项目打包成docker镜像-贾玉珍-2023年2月8日方法一&#xff1a;将项目jar包手动推送到docker服务器上1.用maven对项目进行打包&#xff0c;打包成功后会有一个target目录&#xff0c;目录下有打好的项目jar包2.将jar包上传到服务器上&#xff0c;我用的是finalshell工…

Zabbix Agent item监控项讲解

前言 agent与snmp是Zabbix两种重要的监控方式&#xff0c;这一期主要介绍Zabbix Agent item监控项。。Zabbix agent分为主动代理、被动代理&#xff0c;配置item类型时&#xff0c;可以选择需要的类型&#xff1a; Zabbix agent &#xff1a;用于被动检查 Zabbix agent&…

【C语言进阶】结构体、位段、枚举和联合

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前是C语言学习者 ✈️专栏&#xff1a;C语言航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&a…

顶刊MS论文解读|数据驱动的动态定价和订购策略

作者&#xff1a;白静 金凯瑞 马玺渊 钟子俊编者按 本次解读的文章为“Data-Driven Dynamic Pricing and Ordering with Perishable Inventory in a Changing Environment”&#xff0c;于2022年发表在期刊 Management Science, 作者 N. Bora Keskin, Yuexing Li, Jing-Sheng …

推荐5款极大提高工作效率的办公软件!每个都是我精挑细选的

关于提高办公效率的相关软件&#xff0c;答主要把工作5年的独家秘方都拿出来了。分享的都是用过的且体验不错的软件&#xff0c;大多数是免费软件&#xff01; 按照使用场景&#xff0c;简单先分个类&#xff0c;分别是时间利用利器&#xff0c;流程优化软件&#xff0c;办公美…

论文投稿指南——中文核心期刊推荐(工程材料学)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

重生之我是赏金猎人-SRC漏洞挖掘(一)-某SRC测试系统无脑Getshell

0x01 前言 https://github.com/J0o1ey/BountyHunterInChina 欢迎大佬们点个star 0x02 资产收集到脆弱系统 在某src挖掘过程中&#xff0c;本人通过ssl证书对域名资产进行了收集&#xff0c;通过计算域名对应ip段的权重 整理出其C段资产&#xff0c;进行了批量目录扫描 查看…

Spring Boot两种格式的配置文件,你都了解吗?

目录 前言 一、properties配置文件 1.1、语法格式 1.2、读取配置文件 1.3、缺点分析 2、yml配置文件 2.1、yml语法 2.1.1、注意&#xff1a;value值的单双引号 2.2、yml配置读取 2.3、yml配置的不同数据类型 2.4、yml配置对象 2.5、yml配置集合 3、面试&#xff1a…

2023年浙江道路运输安全员考试真题题库及答案

百分百题库提供道路运输安全员考试试题、道路运输安全员考试预测题、道路运输安全员考试真题、道路运输安全员证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 1.关于道路运输企业车辆技术管理机构的主要职责&#xff0c;在下列表述中最…

anaconda3文件夹被移动之后,如何操作可以复用原有conda环境

anaconda3文件夹被移动A-调整conda PATH地址B-更改.conda/environments.txt中的地址C-修改conda内的变量和每个环境的pip目录A-调整conda PATH地址 B-更改.conda/environments.txt中的地址 a. 优先切换到用户根目录 b. 查看隐藏conda目录 c. 编辑 vi .conda/environments.txt…

【服务器数据恢复】NetApp存储无法访问的数据恢复案例

服务器数据恢复环境&#xff1a; NetApp某型号存储&#xff1b; 配备SAS硬盘&#xff0c;该硬盘520字节一个扇区&#xff1b; 所有的lun映射到小型机使用&#xff0c;存放Oracle数据库文件&#xff0c;采用ASM裸设备存储方式。 服务器故障&#xff1a; 管理员误操作删除NetApp…

介电常数常用测量方法综述

张扬1&#xff0c;徐尚志1&#xff0c;赵文晖2&#xff0c;龚增2&#xff0c;赵晓群1 1同济大学&#xff0c;上海 2上海市计量测试技术研究院&#xff0c;上海 在设计电路、天线、电容器等过程中经常会涉及所用材料的介电常数, 所以深入了解介电常数的相关概念对实际工作有重…

【RuoYi-Vue-Plus】学习笔记 48 - 数据加密功能 Encrypt 源码分析

文章目录前言参考目录功能实现的准备知识1、目录结构说明2、一些准备知识2.1、自定义插件如何实现&#xff1f;2.2、Mybatis 拦截器的拦截点&#xff1f;2.3、关于 Intercepts 注解&#xff1f;2.4、关于拦截器中的 Interceptor() 方法和 plugin() 方法&#xff1f;功能调用流程…

企业数字化转型中信息化是什么

数字化转型带给企业的价值无疑是能够支撑起这股热潮的&#xff0c;这种流行趋势在数字化时代也并不意外。不过有一点值得注意&#xff0c;那就是数字化转型毕竟发展时间还不太长&#xff0c;虽然有未来加持&#xff0c;但又有多少企业能够在当下坚持数字化转型&#xff0c;顺利…

MYSQL的第四次索引视图

题目&#xff1a; 学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课…

[数据库迁移]-MySQL常见问题

[数据库迁移]-MySQL常见问题 森格 | 2023年2月 介绍&#xff1a;记录在MySQL数据库迁移过程中遇到的问题&#xff0c;以及解决方案。 文章目录[数据库迁移]-MySQL常见问题一、背景二、常见问题2.1 ERROR 20032.2 ERROR 12732.3 ERROR 10712.4 视图权限2.5 ERROR 1062三、总结一…