Linux进程通信——共享内存(上)

简介: Linux进程通信——共享内存(上)

共享内存

原理与概念

两个进程的PCB创建虚拟地址空间然后映射到物理内存中,每个进程因为是独立的,所以在物理内存中的地址也不同。

那么共享内存是怎么做到的呢?

首先先在物理内存中申请一块内存。

然后讲这块内存通过页表映射分别映射到这两个进程的虚拟地址空间内,让这两个进程都能看到这块内存。(这里也称为进程和共享内存挂接)

最后如果不想通信了:

取消进程和内存的映射关系(去关联)

释放内存(释放共享内存)

理解:

a.这里和原本C语言当中的maclloc函数开辟空间不同,我们的目的是要让两个进程同时看到这块内存。

b.进程通信的这个申请一块共享内存是专门设计出来的,用来IPC。

c.共享内存是一种通信的方式,所有想通信的进程都可以用。

d.OS一定可能会存在很多的共享内存。

概念就是:通过让不同进程看到同一个内存块的方式就叫做共享内存。

函数接口的介绍与使用

shmget

创建共享内存接口:

首先来看第三个参数:

这里是通过位图的方式(二进制标志位)传参。

IPC_CREAT 如果不存在,创建,如果存在,就获取共享内存的位置。

IPC_EXCL 这个选项无法单独使用,必须结合IPC_CREAT使用,一起使用代表的含义是,如果不存在就创建,存在就会返回错误。(也就是说如果创建成功,他一定是一个新的共享内存——shm)

第二个参数是创建shm的大小。

返回值:

如果成功就返回一个共享内存的合法标识符,失败就返回-1。(这个和文件操作符完全不同,不是一个体系)

第一个参数:

这个是让多个进程看到同一份shm的关键。能进行唯一标识。

这个值是怎么来的呢?

用这个函数生成:

将一个合法路径(字符串)和字符数据通过某种算法组合来进行计算出key值,然后返回key。

失败了返回-1。

那么,怎么样才能让两个进程看到同一份共享内存呢?

在两个进程中如果传入到ftok中的两个参数相同,返回的key也相同,其中一个进程通过shmget接口创建共享内存,另一个接口通过shmget接口接收共享内存的位置,这样两个进程就能看到同一份资源了。

我们要利用接口让两个进程实现通信,首先创建两个.cc的文件,一个头文件.hpp。

因为两个进程都要创建/获取共享内存,所以获取key等等操作在头文件更方便。

#ifndef _COMM_HPP_
#define _COMM_HPP_
#include<iostream>
#include<cassert>
#include<cstring>
#include<cerrno>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cstdlib>
#define PATHNAME "."//ftok的第一个参数,是一个合法路径
#define PROJ_IO 0X666//ftok的第二个参数
key_t getkey()
{
    key_t k = ftok(PATHNAME, PROJ_IO);
    if(k < 0)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        exit(1);
    }
    return k;
}
#endif

然后来测试一下这个函数:

两个进程都调用这个函数:

#include"shm.hpp"
int main()
{
    key_t k = getkey();
    printf("0x%x\n",k);
    return 0;
}

运行之后我们发现两个进程打印的结果key值都是相同的。

key_t的本质就是一个32位的整数。

然后再用shmget去创建和获取共享内存。

int getshmhelper(key_t k, int flags)
{
    int shmid = shmget(k, MAXSIZE, flags);
    if(shmid < 0)
    {
        std::cerr << errno << ":" << strerror(errno) << std::endl;
        exit(2);
    }
    return shmid;
}
int getshm(key_t k)//获取共享内存
{
    return getshmhelper(k, IPC_CREAT);//没有就创建,有就获取
}
int createshm(key_t k)//创建共享内存
{
    return getshmhelper(k, IPC_CREAT | IPC_EXCL | 0600);//没有创建,有就报错,这里创建内存需要给对应的权限
}

我们让server去创建一个共享内存,client去拿共享内存中的数据。

这里再次创建共享内存会报错。

那么key的意义是什么呢?

首先清楚,OS一定可能会存在很多共享内存,并且本质就是申请一块空间,能进行唯一性标识最重要。

之前的C语言,malloc开辟n大小的空间的时候,释放时并不需要告诉他释放多大,自己就知道释放掉n个大小的内存,其实这一块内存中OS也要对这块中间做管理,申请了n个大小,并不代表就是n个,因为里面还有对这块内存的属性和数据左储存,比如大小,释放的时候就回去找这个大小。

这里共享内存也是一样的,OS要先描述再组织,才能进行管理,每次申请一块共享内存,OS还会给这块共享内存申请一个数据结构对象。

所以:共享内存 = 物理内存快 + 共享内存的相关属性

OS管理的是对这个共享内存的数据结构对象做管理的。

那么在创建共享内存的时候,如何保证共享内存在OS中是唯一的呢?答案就是key。

key就像餐厅当中的桌号一样,每个都具有唯一性。

其中一个进程创建共享内存,这块区域中有key值,只要另一个进程也看到同一个key就说明能看到同一块内存。

那么怎么找到key 的位置呢?就在共享内存中的数据结构中。

struct shm

{

key_t k;

}

也就是说ftok函数返回的key值其实就是赋值给共享内存数据结构中的key值。

这就是创建key的含义,key是要通过shmget设置进入共享内存属性中的,用来表示该共享内存在内核中的唯一性。

那么用来接收shmget返回值的变量有什么意义呢?

比如说,我们在企业有自己的员工号,企业扩大的时候,变动的是员工号,并不影响我们的身份证号,这时一种解耦的体现。

用来接收shmget返回值的变量和key值互相不干扰。

就像钥匙和锁一样。(fd与inode也是相同的道理)

那么如何查看IPC资源呢?

首先上面的代码在创建共享内存的时候,明明进程已经结束了,但还显示这个资源已被占用,因为共享内存是OS级的,他的生命周期是和OS相同的,要等OS的关机才会释放掉。

这里用 ipcs -m 查看共享内存。

想删除这块共享内存要用 ipcrm -m shmid

这种方法不太好,所以提供了另一个接口。

shmctl

第一个参数就是上面接受创建共享内存函数的返回值,第二个参数是选项,用来控制这个函数做什么,第三个参数是一个数据结构,就是下面的那个:

返回值是shmid,失败返回-1。

这个选项就是删除掉这段共享内存。

void delshm(int shmid)
{
    if(shmctl(shmid, IPC_RMID, nullptr) == -1)
    {
        std::cerr << errno << ":" << strerror(shmid) << std::endl;
        exit(3);
    }
}

这里谁创建的谁进行删除就可以了。

现在还差一步让两个进程与这个共享内存关联。

相关文章
|
24天前
|
安全 Go 开发者
“不要通过共享内存来通信”——深入理解Golang并发模型与CSP理论
Golang 采用 CSP 理念,主张“通过通信共享内存”,以消息传递替代共享内存,避免数据竞争。其核心为 Goroutine 与 Channel:轻量协程并发执行,通道安全传递数据,将并发复杂性转为通信编排,提升程序清晰度与可维护性。
146 0
|
2月前
|
缓存 监控 Linux
Linux内存问题排查命令详解
Linux服务器卡顿?可能是内存问题。掌握free、vmstat、sar三大命令,快速排查内存使用情况。free查看实时内存,vmstat诊断系统整体性能瓶颈,sar实现长期监控,三者结合,高效定位并解决内存问题。
201 0
Linux内存问题排查命令详解
|
6月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
188 16
|
5月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
119 0
|
5月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
169 0
|
5月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
118 0
|
5月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
139 0
|
8月前
|
Linux 数据库 Perl
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
8月前
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
559 5
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能