【linux进程间通信(二)】共享内存详解以及进程互斥概念

简介: 【linux进程间通信(二)】共享内存详解以及进程互斥概念

1. 前言

在学习Linux中的程序地址空间时,

善于观察的同学可能会发现在栈区

和堆区中间有一个共享区,这是用来

干啥的?今天就来揭晓一下!

本章重点:

本篇文章着重介绍进程间通信的一种
方式: 共享内存的概念,接口使用以及
它的底层原理,最后会介绍进程间互斥
的一些基本概念


2. 共享内存的原理

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

说大白话就是: 共享内存的通信方式就是在物理内存中开辟一份空间,然后将这份空间映射到两个进程的共享区中,这两个进程可以直接在自己的地址空间中读取或写入数据,不会经过内核,效率很快!


3. 实现共享内存的基本步骤

使用shm系统函数来实现这一功能:

  1. 第一步: 获取key值

要通信的双方怎样保证自己看见的是和对方一样共享内存? -> 通过一个key值来保证,只要两个进程拿到同一个key值,再用这个key值去创建共享内存,那么它们看见的就是同一份共享内存!

使用ftok函数可以形成一个唯一的key值

函数的参数随意指定,只要保证通信双方

传入的参数一样,就能备注拿到一样的key

  1. 第二步: 创建/获取共享内存

通信双方只需要一方来创建共享内存,另外一方可以直接通过key值获取到共享内存(毕竟物理内存只需要创建一份),不管是创建还是获取共享内存使用的都是同一个函数!

  1. 第三步: 将共享内存映射到地址空间

当双方进程都拿到共享内存段的标识码后,此时需要将这份共享空间从物理地址映射到进程自己内部的地址空间中,方便后续使用!

  1. 第四步: 使用完后将共享内存与进程去关联

值得注意的是,共享内存使用完后和指针一样需要"释放",否则会导致内存泄漏问题

  1. 第五步: 删除共享内存

将共享内存与进程去关联后,并不代表它就被删除了,共享内存的生命周期是随内核的,而我们的云服务器是永远不会关闭的,如果不删除共享内存,将来能使用的空间会越来越少

使用ipcs -m指令查看共享内存

使用ipcrm -m shmid指令删除共享内存

函数删除共享内存的方法


4. 共享内存编码实现

由于双方进程编写代码有很多重复的地方

所以使用一个commom.h文件存放公共内容

commom.h文件中:

#pragma once
#include<iostream>
#include<string>
#include<unistd.h>
#include<cstdio>
#include<sys/shm.h>
#include<sys/ipc.h>
#include<sys/types.h>
#include<cassert>
#include<cstring>
#include<sys/stat.h>
#include<fcntl.h>
using namespace std;
#define PATHNAME "/home/kwy"  //形成key值的字符串
#define PROJ_ID 0x666  //形成key值的整数
#define SHM_SIZE 4096 //共享内存的大小最好是页(PAGE : 4096)的整数倍

client端代码:

#include"common.hpp"
int main()
{
    //获取key
    key_t k = ftok(PATHNAME, PROJ_ID);
    if (k < 0)
    {
        perror("ftok");
        exit(1);
    }
    cout<<"creat key success: "<<k<<endl;
    // 获取共享内存
    int shmid = shmget(k, SHM_SIZE, 0);
    if(shmid < 0)
    {
        perror("shmget");
        exit(2);
    }
    cout<<"get shard success: "<<shmid<<endl;
    //挂接共享内存
    char *shmaddr = (char *)shmat(shmid, nullptr, 0);
    if(shmaddr == nullptr)
    {
       perror("shmat");
        exit(3);
    }
    cout<<"attach success!"<<endl;
    //将共享内存看作数组,写入数据
    char ch ='a';
    for(;ch<='z';ch++)
    {
        snprintf(shmaddr,SHM_SIZE-1,"hello server,i am other proc,mypid: %d ,inc: %c",getpid(),ch);
        sleep(2);
    }
    // 去关联
    int n = shmdt(shmaddr);
    assert(n != -1);
    cout<<"detach success!"<<endl;
    // client 要不要chmctl删除呢?不需要!!
    return 0;
}

server端代码:

#include"common.hpp"
int main()
{
    //创建公共的key的值
    key_t key = ftok(PATHNAME,PROJ_ID);
    cout<<"server key: "<<TransToHex(key)<<endl;
    assert(key!=-1);
    //创建共享内存(全新的共享内存)
    int shmid = shmget(key,SHM_SIZE,IPC_CREAT | IPC_EXCL | 0666);
    if(shmid==-1)
    {
        perror("shmid");
        exit(1);
    }
    cout<<"creat shmid: "<<shmid<<endl;
    //将创建好的共享内容挂接到虚拟地址
    char* shmaddr = (char*)shmat(shmid,nullptr,0);//shmat的返回值是void*,与malloc相似要强转
    cout<<"attach success!"<<endl;
    //使用共享内存,将共享内存当作一个大字符串
    //使用完共享内存后,将指定的内存与进程去关联
    int n1 = shmdt(shmaddr);
    if(n1==-1)
        perror("shmdt");
    else
        cout<<"detach success!"<<endl;
    //最后用代码删除共享内存
    int n2 = shmctl(shmid,IPC_RMID,nullptr);
    assert(n2!=-1);
    cout<<"delete shared memory: "<<shmid<<endl;
    return 0;
}

5. 进程互斥相关概念

大家在学习共享内存时有没有发现一个问题:

假如共享区有一个变量a,client进程在将a进行++操作的同时,server进程将a的值从10改为了100,这里就会出现问题,已经被改为100的a值在client进程执行完操作后,会把在CPU中计算出来的11赋值给a,那么进程server就白修改了,这里显然是有问题的

没错,在这个地方,共享内存被称为共享资源,共享资源如果被同时访问可能会出现某些预想不到的问题,所以各个进程不能在同一时间访问共享资源,这种关系叫做进程互斥

对于进程互斥和共享资源以及临界区的认识远远没有结束,今天我们只是窥见了冰山一角,在后续的学习中我们还会重点讲这一个概念!


6. 总结

进程间通信的方式远不止管道和共享内存这两种,还有很经典的消息队列以及信号量等方式这里并没有过多讲述,因为我们只是学习重点,并不是全部都要学会,但是学有余力的同学还是很有必要区了解一下的


🔎 下期预告:进程信号 🔍


相关文章
|
1月前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
34 0
|
13天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
44 4
linux进程管理万字详解!!!
|
4天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
41 8
|
12天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
46 4
|
13天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
15天前
|
消息中间件 存储 Linux
|
21天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
23 1
|
1月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
20 1
|
1月前
|
消息中间件 Linux API
Linux c/c++之IPC进程间通信
这篇文章详细介绍了Linux下C/C++进程间通信(IPC)的三种主要技术:共享内存、消息队列和信号量,包括它们的编程模型、API函数原型、优势与缺点,并通过示例代码展示了它们的创建、使用和管理方法。
29 0
Linux c/c++之IPC进程间通信
|
1月前
|
Linux C++
Linux c/c++进程间通信(1)
这篇文章介绍了Linux下C/C++进程间通信的几种方式,包括普通文件、文件映射虚拟内存、管道通信(FIFO),并提供了示例代码和标准输入输出设备的应用。
26 0
Linux c/c++进程间通信(1)

热门文章

最新文章