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

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


🔎 下期预告:进程信号 🔍


相关文章
|
2天前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
42 20
|
5月前
|
Go 调度 开发者
[go 面试] 深入理解进程、线程和协程的概念及区别
[go 面试] 深入理解进程、线程和协程的概念及区别
|
1月前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
60 4
|
4月前
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
3月前
|
安全 Linux
Linux线程(十一)线程互斥锁-条件变量详解
Linux线程(十一)线程互斥锁-条件变量详解
|
4月前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
4月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
97 0
|
5月前
|
存储 程序员 C++
内存管理概念 (二)
内存管理概念 (二)
85 1
|
5月前
|
Linux Shell 调度
【在Linux世界中追寻伟大的One Piece】Linux进程概念
【在Linux世界中追寻伟大的One Piece】Linux进程概念
51 1
|
5月前
|
Linux API 调度
重温Linux内核:互斥和同步
本文全面回顾了Linux内核中的互斥和同步机制,包括中断屏蔽、原子变量、自旋锁、读写锁、顺序锁、信号量、互斥量、RCU机制以及完成量等,提供了它们的定义、实现原理、API用法和使用时的注意事项。
75 0
下一篇
开通oss服务