【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. 总结

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


🔎 下期预告:进程信号 🔍


相关文章
|
9天前
|
缓存 Linux 数据安全/隐私保护
Linux环境下如何通过手动调用drop_caches命令释放内存
总的来说,记录住“drop_caches” 命令并理解其含义,可以让你在日常使用Linux的过程中更加娴熟和自如。
59 23
|
4月前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
本文详细介绍了进程间通信(IPC)的六种主要方式:管道、信号、消息队列、共享内存、信号量和套接字。每种方式都有其特点和适用场景,如管道适用于父子进程间的通信,消息队列能传递结构化数据,共享内存提供高速数据交换,信号量用于同步控制,套接字支持跨网络通信。通过对比和分析,帮助读者理解并选择合适的IPC机制,以提高系统性能和可靠性。
557 14
|
4月前
|
缓存 Linux
linux 手动释放内存
在 Linux 系统中,内存管理通常自动处理,但业务繁忙时缓存占用过多可能导致内存不足,影响性能。此时可在业务闲时手动释放内存。
210 17
|
2月前
|
监控 Linux Python
Linux系统资源管理:多角度查看内存使用情况。
要知道,透过内存管理的窗口,我们可以洞察到Linux系统运行的真实身姿,如同解剖学家透过微观镜,洞察生命的奥秘。记住,不要惧怕那些高深的命令和参数,他们只是你掌握系统"魔法棒"的钥匙,熟练掌握后,你就可以骄傲地说:Linux,我来了!
136 27
|
3月前
|
消息中间件 Linux
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
169 48
|
3月前
|
缓存 NoSQL Linux
Linux系统内存使用优化技巧
交换空间(Swap)的优化 禁用 Swap sudo swapoff -a 作用:这个命令会禁用系统中所有的 Swap 空间。swapoff 命令用于关闭 Swap 空间,-a 参数表示关闭 /etc/fstab 文件中配置的所有 Swap 空间。 使用场景:在高性能应用场景下,比如数据库服务器或高性能计算服务器,禁用 Swap 可以减少磁盘 I/O,提高系统性能。
113 3
|
3月前
|
缓存 Linux
Linux查看内存命令
1. free free命令是最常用的查看内存使用情况的命令。它显示系统的总内存、已使用内存、空闲内存和交换内存的总量。 free -h • -h 选项:以易读的格式(如GB、MB)显示内存大小。 输出示例: total used free shared buff/cache available Mem: 15Gi 4.7Gi 4.1Gi 288Mi 6.6Gi 9.9Gi Swap: 2.0Gi 0B 2.0Gi • to
142 2
|
4月前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
323 20
|
8天前
|
消息中间件 NoSQL Linux
Redis的基本介绍和安装方式(包括Linux和Windows版本),以及常用命令的演示
Redis(Remote Dictionary Server)是一个高性能的开源键值存储数据库。它支持字符串、列表、散列、集合等多种数据类型,具有持久化、发布/订阅等高级功能。由于其出色的性能和广泛的使用场景,Redis在应用程序中常作为高速缓存、消息队列等用途。
97 16
|
2月前
|
Linux
linux命令详细说明以及案例
本文介绍了常用的 Linux 命令及其详细说明和示例,包括:`ls`(列出目录内容)、`cd`(更改目录)、`rm` 和 `mv`(删除与移动文件)、`grep`(搜索文本)、`cat`(显示文件内容)以及 `chmod`(更改文件权限)。每个命令均配有功能描述、选项说明及实际案例,帮助用户更好地掌握 Linux 命令的使用方法。
177 56