【Linux】进程间通信 -- 共享内存

共享内存

共享内存是SystemV标准进程间通信的一种,该标准还有消息队列和信号量,但下文主要介绍共享内存,然后在谈一下信号量的内容。SystemV标准的进程间通信可以看做单机版的进程间通信。

// 1. log.hpp
#pragma once

#include <iostream>

enum ErrLevel
{
    lev_0,
    lev_1,
    lev_2,
    lev_3,
    lev_4
};

const std::string error[] = {
    "err_0",
    "err_1",
    "err_2",
    "err_3",
    "err_4"
};

std::ostream& Log(const std::string& msg, int level)
{
    std::cout << " | " << (unsigned int)time(0) << " | " << error[level] << " | " << msg << " |";
    return std::cout;
}
// 2. comm.hpp
#pragma once

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>

using namespace std;

#include "log.hpp"

// key_t ftok(const char *pathname, int proj_id);
#define PATH_NAME "/home/zs/linux/testcpp"
#define PROJ_ID 0x20231118

// 共享内存的大小,最好是页(PAGE:4096)的整数倍
#define SHM_SIZE 4096

#define MODE 0666
#define FIFO_PATH "./fifo"

class FIFO
{
public:
    FIFO()
    {
        if(0 != mkfifo(FIFO_PATH, MODE))
        {
            perror("mkfifo");
            exit(1);
        }
        Log("create fifo success", lev_1) << endl;
    }

    ~FIFO()
    {
        if(0 != unlink(FIFO_PATH))
        {
            perror("unlink");
            exit(2);
        }
        Log("unlink fifo success", lev_2) << endl;
    }
};

int openFIFO(const string& pathname, int flags)
{
    int fd = open(pathname.c_str(), flags);
    if(fd == -1)
    {
        perror("open");
        exit(3);
    }

    return fd;
}

void Wait(int fd)
{
    uint32_t i = 0;
    ssize_t size = read(fd, &i, sizeof(i));
    if(size == -1)
    {
        perror("Wait::read");
        exit(4);
    }
}

void Signal(int fd)
{
    uint32_t i = 1;
    ssize_t size = write(fd, &i, sizeof(i));
    if(size == -1)
    {
        perror("Signal::write");
        exit(5);
    }
}

void closeFIFO(int fd)
{
    close(fd);
}

创建共享内存使用shmget函数。
key:只有创建的时候用到key(key是调用ftok用算法形成的,标识了系统层上的唯一性);大部分情况下用户访问共享内存用的还是shmid(shmget的返回值,它的使用类似文件描述符fd,标识了用户层上的唯一性)。
size:设置创建的共享内存的大小。
shmflg

  • IPC_CREAT:单独使用,如果共享内存不存在,创建并返回;如果已经存在,获取并返回。
  • IPC_EXCL:单独使用,没有意义。
  • IPC_CREAT | IPC_EXCL:共同使用,如果共享内存不存在,创建并返回;如果已经存在,出错返回(如果返回成功,一定是一个全新的共享内存)。
    在这里插入图片描述
    创建共享内存之前key的生成由ftok接口完成。
    ftok可以确保接收到相同的pathnameproj_id会使用算法生成相同的key
    而不同进程通过拿到相同的key值来看到同一份资源。
    在这里插入图片描述
// 3. server.hpp
#include "comm.hpp"

// 程序在加载时,自动构建全局变量,自动调用fifo的构造函数,创建管道文件
// 程序退出时,全局变量会被析构,自动调用fifo的析构函数,删除管道文件
FIFO fifo;

void test()
{
    // 通信的前置工作,让不同进程看到同一份资源(内存)
    // 1.创建公共的key值
    key_t key = ftok(PATH_NAME, PROJ_ID);
    if(key == -1)
    {
        perror("ftok");
        exit(1);
    }
    Log("server create key success", lev_1) << " server key: " << key << endl;

    // 2.创建共享内存 -- 建议创建一个全新的共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | MODE);
    if(shmid == -1)
    {
        perror("shmget");
        exit(2);
    }
    Log("server create shm success", lev_2) << " shmid: " << shmid << endl;

    // 3.将共享内存挂接到自己的地址空间
    char* shmaddr = (char*)shmat(shmid, nullptr, 0);
    if((void*)shmaddr == (void*)-1)
    {
        perror("shmaddr");
        exit(3);
    }
    Log("server attach shm success", lev_3) << " shmaddr: " << (void*)shmaddr << endl;

    // 4.通信 -- 将共享内存看做字符串存储空间
    // 关于共享内存的通信有两个结论:
    // a.通信双方一方可以直接向共享内存写数据,另一方可以立刻看到写的数据
    //   共享内存是所有进程间通信(IPC)速度最快的。因为它不需要过多的拷贝(表现就是不需要将数据给到操作系统)
    //   共享内存映射进进程地址空间的共享区(在用户空间内),所以不需要经过系统调用,可以直接访问,双方进程通信属于内存级的读和写。
    // b.共享内存缺乏访问控制,会引起并发问题
    // 此处采用管道的访问控制功能,来对共享内存进行一定的访问控制
    int fd = openFIFO(FIFO_PATH, O_RDONLY);
    while(true)
    {
        Log("server wait...", lev_2) << endl;
        Wait(fd);

        cout << "server output: " << shmaddr << endl;
        if(strcmp(shmaddr, "quit") == 0)
        {
            cout << "client quit, then server quit" << endl;
            break;
        }
        // sleep(1);
    }
    closeFIFO(fd);
    // sleep(10);

    // 5.将共享内存从地址空间中祛关联
    int dt = shmdt(shmaddr);
    if(dt == -1)
    {
        perror("shmdt");
        exit(4);
    }
    Log("server detach shm success", lev_4) << endl;

    // 6.删除共享内存 -- IPC_RMID: 即使当前还有进程和shm挂接,依旧会删除shm
    int ipcrm = shmctl(shmid, IPC_RMID, nullptr);
    if(ipcrm == -1)
    {
        perror("shmctl");
        exit(5);
    }
    Log("server rm shm success", lev_1) << endl;
}

共享内存的提供者是操作系统,当进程运行结束时,共享内存是还存在着的,其生命周期是随操作系统的。共享内存有两种删除方式,一是手动删除,二是利用代码调用接口删除。
在这里插入图片描述
可以调用shmctl接口进行删除。
cmd:填写删除指令IPC_RMID
buf:是用于描述共享内存结构的结构体指针,使用buf可以对共享内存的结构进行更改。
这里可以引出对共享内存的重新理解:共享内存 = 共享内存块 + 对应的共享内存的内核数据结构。
操作系统为了管理大量的共享内存,需要先描述再组织,进行管理。
在这里插入图片描述
buf所指向的共享内存的结构体。
在这里插入图片描述
共享进程创建好后,进程要访问共享进程还需要对其进行挂接(将共享内存映射进进程所在地址空间中)。
shmaddr:除非自己特别清楚要将共享内存挂接在什么位置,否则nullptr让系统自己处理。
在这里插入图片描述

// 4. client.cpp
#include "comm.hpp"

void test()
{
    // 1.获取key
    Log("client pid is:", lev_0) << " " << getpid() << endl;
    key_t key = ftok(PATH_NAME, PROJ_ID); // key_t -- int
    if(key == -1)
    {
        perror("ftok");
        exit(1);
    }
    Log("client create key success", lev_1) << " client key: " << key << endl;

    // 2.获取共享内存
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT);
    if(shmid == -1)
    {
        perror("shmget");
        exit(2);
    }
    Log("client get shm success", lev_2) << " shmid: " << shmid << endl;

    // 3.将共享内存挂接到自己的地址空间
    char* shmaddr = (char*)shmat(shmid, nullptr, 0);
    if((void*)shmaddr == (void*)-1)
    {
        perror("shmaddr");
        exit(3);
    }
    Log("client attach shm success", lev_3) << " shmaddr: " << (void*)shmaddr << endl;

    // 4.通信
    int fd = openFIFO(FIFO_PATH, O_WRONLY);
    while(true)
    {
        write(1, "client input: ", strlen("client input: "));
        ssize_t size = read(0, shmaddr, SHM_SIZE);
        if(size == -1)
        {
            perror("read");
            exit(5);
        }

        shmaddr[size - 1] = '\0';
        Signal(fd);
        if(strcmp(shmaddr, "quit") == 0) break;
    }
    closeFIFO(fd);

    // 5.祛关联
    int dt = shmdt(shmaddr);
    if(dt == -1)
    {
        perror("shmdt");
        exit(4);
    }
    Log("client detach shm success", lev_4) << endl;
}

在这里插入图片描述

信号量

这里不谈消息队列的问题,但消息队列的接口使用和共享内存都是相似的。
在这里插入图片描述
临界资源:多个进程(执行流)看到的一份公共的资源。
临界区:进程里,访问临界资源的代码部分。
由临界资源和临界区的概念可以知道,多个进程(执行流),同时运行时会互相干扰,主要是不加保护地访问了同一份资源(临界资源)。而再分临界区,多个进程(执行流)是互不影响的。
互斥:为了更好地进行临界资源的保护,可以让多个进程(执行流)在任何时刻,只能有一个进入临界区。
原子性:要么不做,要么做完,没有中间状态。

信号量的出现让任何一个进程要访问临界资源时,不能直接访问,而是要先申请信号量(这里可以看出信号量也属于临界资源,可以被多个进程看到)。
信号量本质是一个计数器,申请信号量的本质就是让计数器减一。
只要申请信号量成功,在临界资源内部一定会预留有该进程要访问的资源。
而申请信号量的本质,是对临界资源的一种预定机制。
申请信号量(P操作)和释放信号量(V操作)必须是原子的。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网站优化工具Google Optimize

Google Optimize 是一款由Google提供的网站优化工具。Google Optimize旨在帮助网站管理员通过对网页内容、设计和布局进行测试和优化&#xff0c;来提升用户体验和网站的转化率。 Google Optimize 提供了 A/B 测试和多变量测试功能&#xff0c;使网站管理员能够比较和评估不同…

LeetCode算法题解(动态规划)|LeetCoed62. 不同路径、LeetCode63. 不同路径 II

一、LeetCoed62. 不同路径 题目链接&#xff1a;62. 不同路径 题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下…

【LeetCode刷题-滑动窗口】--345.反转字符串中的元音字母

345.反转字符串中的元音字母 class Solution {public String reverseVowels(String s) {int len s.length();if(len < 2){return s;}char[] charArray s.toCharArray();int left 0,right len - 1;while(true){while(left < len && checkVowels(charArray[lef…

Selenium自动化测试框架

一.Selenium概述 1.1 什么是框架? 框架&#xff08;framework&#xff09;是一个框子——指其约束性&#xff0c;也是一个架子——指其支撑性。是一个基本概念上的 结构用于去解决或者处理复杂的问题。 框架是整个或部分系统的可重用设计&#xff0c;表现为一组抽象构件及…

2023腾讯云轻量应用服务器购买优惠活动,轻量服务器优惠链接

双11优惠活动即将到来&#xff0c;各大电商平台纷纷推出超值优惠&#xff0c;腾讯云也不例外。今天&#xff0c;我将向大家介绍一款在双11活动中备受瞩目的服务器套餐——腾讯云的3年轻量应用服务器配置为2核2G4M带宽、50GB SSD系统盘。这款服务器不仅配置强大&#xff0c;而且…

ubuntu下载conda

系统&#xff1a;Ubuntu18.04 &#xff08;1&#xff09;下载安装包 wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2021.11-Linux-x86_64.sh 报错错误 403&#xff1a;Forbidden 解决方法 wget -U NoSuchBrowser/1.0 https://mirrors.tuna.tsingh…

【LeetCode刷题-双指针】--259.较小的三数之和

259.较小的三数之和 方法&#xff1a;排序双指针 class Solution {public int threeSumSmaller(int[] nums, int target) {Arrays.sort(nums);int k 0;for(int i 0;i<nums.length;i){int start i 1,end nums.length - 1;while(start < end){int sum nums[start] …

Systemverilog中Clocking blocks

1. clocking block的作用 Clocking block可以将timing和synchronization detail从testbench的structural、functional和procedural elements中分离出来&#xff0c;因此sample timming和clocking block信号的驱动会隐含相对于clocking block的clock了&#xff0c;这就使得对一些…

sort()方法详解

作用 对数组进行排序&#xff0c;默认情况下&#xff0c;将元素转换为字符串&#xff0c;然后按照它们的UTF-16码值升序排序。 语法 sort() 元素是字符串时 默认排序时根据字典顺序进行排序的 元素是字母字符串时&#xff0c;按照字母进行升序&#xff0c; const stringAr…

网络和Linux网络_3(套接字编程)TCP网络通信代码(多个版本)

目录 1. TCP网络编程 1.1 前期代码 log.hpp tcp_server.cc 1.2 accept和单进程版代码 1.3 多进程版strat代码 1.4 client.cc客户端 1.5 多进程版strat代码改进多线程 1.6 线程池版本 Task.hpp lockGuard.hpp thread.hpp threadPool.hpp 多个回调任务 tcp_client…

Linux--网络概念

1.什么是网络 1.1 如何看待计算机 我们知道&#xff0c;对于计算机来说&#xff0c;计算机是遵循冯诺依曼体系结构的&#xff08;即把数据从外设移动到内存&#xff0c;再从内存到CPU进行计算&#xff0c;然后返回内存&#xff0c;重新读写到外设中&#xff09;。这是一台计算机…

Mysql-复合查询

实际开发中往往数据来自不同的表&#xff0c;所以需要多表查询。 1.笛卡尔积 通俗来讲就是两个表的每一列都组合一遍&#xff0c;也就是穷举法。 穷举出来的数据表会有大量重复数据&#xff0c;而我们只需要加上一些限定条件就可以完成有效数据的筛选。 select EMP.ename, EM…

linux进程之进程的优先级➕环境变量

文章目录 1.优先级的认识1.1优先级的介绍1.2初识优先级1.3ps指令1.4查看/修改进程的优先级1.5对优先级的认识1.6对进程的深一步理解 2.环境变量2.0环境变量相关的命令2.1环境变量的概念2.2常见/查看环境变量2.3环境变量的作用2.4修改环境变量1.将zombie可执行程序放到PATH现有的…

牛客-- 求解立方根python

描述 计算一个浮点数的立方根&#xff0c;不使用库函数。 保留一位小数。 数据范围&#xff1a;∣val∣≤20 输入描述&#xff1a; 待求解参数&#xff0c;为double类型&#xff08;一个实数&#xff09; 输出描述&#xff1a; 输出参数的立方根。保留一位小数。 使用…

CCF CSP认证 历年题目自练Day47

题目 试题编号&#xff1a; 201712-3 试题名称&#xff1a; Crontab 时间限制&#xff1a; 10.0s 内存限制&#xff1a; 256.0MB 样例输入 3 201711170032 201711222352 0 7 * * 1,3-5 get_up 30 23 * * Sat,Sun go_to_bed 15 12,18 * * * have_dinner 样例输出 201711170…

shopee选品工具:Shopee选品工具—知虾精准选品与科学运营的利器

在如今竞争激烈的电商市场中&#xff0c;如何进行精准选品和科学运营成为了每个卖家都需要面对的问题。而Shopee选品工具——知虾&#xff0c;作为一款强大的大数据采集及分析平台&#xff0c;为卖家提供了全面的市场分析、产品分析和店铺分析功能&#xff0c;帮助卖家发现市场…

​软考-高级-系统架构设计师教程(清华第2版)【第19章 大数据架构设计理论与实践 (P691~716)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第19章 大数据架构设计理论与实践 &#xff08;P691~716&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

ARDUINO UNO 12颗LED超酷流水灯效果

效果代码&#xff1a; #define t 30 #define t1 20 #define t2 100 #define t3 50 void setup() { // set up pins 2 to 13 as outputs for (int i 2; i < 13; i) { pinMode(i, OUTPUT); } } /Effect 1 void loop() { effect_1(); effect_1(); effect_…

机器人制作开源方案 | 智能快递付件机器人

一、作品简介 作者&#xff1a;贺沅、聂开发、王兴文、石宇航、盛余庆 单位&#xff1a;黑龙江科技大学 指导老师&#xff1a;邵文冕、苑鹏涛 1. 项目背景 受新冠疫情的影响&#xff0c;大学校园内都采取封闭式管理来降低传染的风险&#xff0c;导致学生不能外出&#xff0c…

Microsoft SQL Server Management Studio(2022版本)启动无法连接到服务器

Microsoft SQL Server Management Studio&#xff08;2022版本&#xff09;启动无法连接到服务器 解决方法&#xff1a; 打开SQL Server 2022 配置管理器。 启动即可。