Linux入门——09 共享内存

2024-08-23 08:20
文章标签 linux 入门 共享内存 09

本文主要是介绍Linux入门——09 共享内存,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.共享内存原理

OS内的每个进程都会有自己的内核结构(task_struct)和虚拟地址空间,通过页表与物理内存进程映射。

如果让两个不同的进程共享内存,首先就是在内存中申请一块空间(共享内存),

然后将建立好的内存映射到进程的虚拟地址空间上(进程和共享内存挂接)。

两个内存都映射统一块物理地址空间,就是看到了统一块资源,就建立了通讯。

如果未来不想通讯了,就取消进程与内存的映射关系(去关联),释放内存。

理解:

  1. 进程间通讯,是专门设计的,用来IPC
  2. 共享内存是一种通讯方式,所有想通讯的进程,都可以用
  3. OS一定会存在很多共享内存

概念:通过让不同内存,看到同一内存块的方式:共享内存

共享内存是最快的IPC形式

2.实现共享内存

2.1shmget获得共享内存函数

int shmget(key_t key, size_t size, int shmflg);

参数:

key:保证看到的是同一份内存,内进行唯一性标识。(就像身份证号是什么不重要,它能保证你的唯一性)

key可以通过ftok获取。只要保证路径和id进行计算得到同一个数字key,就能保证看到同一个共享内存

size:共享内存大小,共享内存大小,一般为4kb(4096字节)的整数倍 (系统分配是按内存块分配的,内存块的基本单元为4kb)//如果是4097内核会给你向上取整,给你8kb。但是当用ipcs查看,确实是4097,!!!要知道给你的和能用的是两码事。

shmflg:IPC_CREAT IPC_EXCL(本质是标志位)

IPC_CREAT:如果创建的共享内存不存在,创建,如果存在,获取之。默认0也是如果创建的共享内存不存在,创建,如果存在,获取之。

IPC_EXCL:无法单独使用,IPC_EXCL | IPC_CREAT:如果不存在创建,如果存在,报错返回。 -------》如果创建成功,一定是一个新的共享内存

返回值:

成功,返回一个共享内存的标识符,错误返回-1

int getShmHelper(key_t k,int flag)

{

    int shmid = shmget(k,MAX_SIZE,flag);

    if(shmid < 0)

    {

        std::cerr

        exit(2);

    }

    return shmid;

}

//创建共享内存

int createShm(key_t k)

{

    return getShmHelper(k , IPC_CREAT | IPC_EXCL | 0600); //注意这里创建的权限,如果不加0600,后面挂接的时候就没有读和写的权限

}

如果不加权限0600,创建出来的共享内存的权限就为0,下图正perms就为0

 

2.2 ftok函数(转化,将一个路径和项目标识符转化为一个System V 的IPC的key)

参数:key_t ftok(const char *pathname, int proj_id);

pathname:路径

proj_id:项目id,自己随便写的标识符就行

主要功能就是将路径和id进行计算得到一个数字key

返回值:成功返回key,失败返回-1,

2.2.1理解key的意义

  • OS一定会存在很多个共享内存
  • 申请一个空间,TODO
  • key是什么不重要,能进行唯一性标识重要

malloc一个多大的空间,而free只用一个申请的起始地址 -------》我怎么知道要释放多少个,因为每次申请的时候,都会多给你一些空间,用来维护这个空间,所以只用起始地址就可以了‘;

共享内存同样也是,它也要管理。先描述再组织-------》共享内存=物理内存块+共享内存的相关属性

创建共享内存的时候,如何保证共享内存在系统中的唯一的?--------key!就相当与门牌号

key在那?在共享内存的相关属性里

在内核中struct shm

{

        key_t key;

}

在我们调用ftok创建共享内存块的时候,就会传进去一个key,把本质是将key传入创间好的共享内存的属性key中。

当另一个进程要通讯的时候会根据key,遍历所能通讯的共享内存块。

总结:key是要通过shmget,设置进共享内存属性中的!用来标识共享内存在内核中的唯一性

2.2.1shmid

shmid vs key有点像fd vs inode

inode是每个文件的唯一编号,fd描述符可以通过整数,快速让上层找到对应的文件

shmid就是用特定的key标识特定的共享内存,通过整数让上层快速访问共享内存

为什么怎么麻烦呢?用一个不就行了,这样不便于解耦,就像在公司 你要有员工号,你这个员工号没了,并不影响你在国家的身份证号 ,互不干扰。

3.代码实现共享内存

3.1comm.hpp

#ifndef _COMM_HPP_
#define _COMM_HPP_#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <unistd.h>#define PATHNAME "/home/lin/Desktop/Linux_learn/sharem"
#define PROJ_ID 0x66
#define MAX_SIZE 4096  //字节,一共4kb//获得shmget所需要的key
key_t getKey()
{key_t key = ftok(PATHNAME,PROJ_ID);if(key < 0){// cin,cout,cerr--->stdin,stdout,stderr---> 0,1,2std::cerr<< errno <<":"<< strerror(errno) << std::endl;exit(1);}return key;
}int getShmHelper(key_t k,int flag)
{int shmid = shmget(k,MAX_SIZE,flag);if(shmid < 0){std::cerr << errno <<":"<< std::strerror(errno) << std::endl;exit(2);}return shmid;
}//创建共享内存
int createShm(key_t k)
{return getShmHelper(k , IPC_CREAT | IPC_EXCL);
}//获得共享内存
int getShm(key_t k)
{return getShmHelper(k , IPC_CREAT /*0*/);
}//删除共享内存
int delShm(int shmid)
{if(shmctl(shmid,IPC_RMID,nullptr)==-1){std::cerr <<errno <<":" << std::strerror(errno) <<std::endl;return -1;}else{return 0;}
}#endif

3.3.2shm_server.cc

#include "comm.hpp"int main()
{key_t key = getKey(); printf("0x%x\n",key); int shmid =  createShm(key);printf("%d\n",shmid); sleep(5);delShm(shmid);return 0;
}

3.3.3shm_client.cc

#include "comm.hpp"int main()
{key_t key = getKey(); printf("0x%x\n",key); int shmid =  getShm(key);printf("%d\n",shmid); return 0;
}

当我们第一次执行./sshm_server的时候是成功的,当第二次执行会报错

可以看出来,进程结束的时候,申请的共享内存资源依然没有释放。-----》共享内存的声明周期是随操作系统的,不是随进程的。

这是system V的共性(消息队列,信号量数组)

4.查看共享内存&删除共享内存指令

ipcs命令 //查看共享内存

ipcs -m // -m代表共享内存

ipcs -q //-q代表消息队列

ipcs -s //-s代表信号数组

ipcs(system V的消息队列,共享内存,信号数组)

ipcrm -m shmid //删除共享内存

ipcrm -q shmid //删除消息队列

ipcrm -s shmid //删除信号数组

5.shmctl共享内存删除函数

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

        shmid:共享内存id

        cmd:控制命令:

                IPC_STAT:从内核中将共享内存的属性获取下来

                IPC_SET:将设置好的共享内存属性设置到内核中

                IPC_RMID:立即移除共享内存

        struct shmid_ds *buf:获取共性内存的属性到结构体中打印下来,

                不同的结构体或区不同的属性,一般设为nullptr

                        IPC_INFO

                        SHM_INFO

                        SHM_STAT

                        SHM_LOCK

                        SHM_UNLOCK

返回值:

当执行删除的时候,成功返回0,失败返回-1;

6.共享内存的关联

6.1shmat共享内存关联函数

void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
    int shmid:表示跟那个共享内存关联起来
    const void *shmaddr:将这块共享内存映射到那块共享空间当中(百分百不使用,不知道映射到哪,一般为nullptr)
    int shmflg:跟读写权限有关,默认为0,就可以
    
    
返回值:void *shmat:返回的是映射好的地址空间,将在虚拟地址空间的起始地址给返回,等价于malloc
    成功返回起始地址
    错误返回-1

举例
//给共享内存产生相关联void *attchShm(int shmid)
{void * mem =shmat(shmid,nullptr,0);if((int)mem == -1){std::cerr <<errno <<":" << std::strerror(errno) <<std::endl;exit(3);}return mem;}

6.2shmdt在使用完共享内存的时候去关联,不要粗暴的直接删除

去掉映射关系,删除页表与地址空间和物理内存的关联

int shmdt(const void *shmaddr);   //detach
参数:
    const void *shmaddr:将这块共享内存映射到那块共享空间当中的关系去掉 ,是shmat的返回值
          
返回值:
    成功返回0
    错误返回-1

7.生命周期随内核

7.1优点

  • 所有通讯方式中最快的!因为共享内存是双方在内存中共享数据,数据只要在内存就被双方看到,大大减少拷贝次数。

7.2缺点 

  • 不给我们进行同步和互斥的,没有对数据进行任何保护!管道有(有了再读,写满停止)

8.创建共享内存并使用

#include <sys/types.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>int main(int argc, char const *argv[])
{// 1.得到keykey_t key;key = ftok("./keytest",100);if(key < 0 ){printf("ftok error\n");return -1;}printf("key = %d\n",key);// 2.创建共享内存int shmid;shmid = shmget(key,512,0666);if(shmid < 0 ){printf("shmget error\n");return -1;}printf("shmid = %d\n",shmid);// 3.映射共享内存char *buffer;buffer = shmat(shmid,NULL,0);if(buffer < 0){perror(buffer);return -1;}// 4.读printf("读:%s\n",buffer);return 0;
}

这篇关于Linux入门——09 共享内存的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1098847

相关文章

防止Linux rm命令误操作的多场景防护方案与实践

《防止Linuxrm命令误操作的多场景防护方案与实践》在Linux系统中,rm命令是删除文件和目录的高效工具,但一旦误操作,如执行rm-rf/或rm-rf/*,极易导致系统数据灾难,本文针对不同场景... 目录引言理解 rm 命令及误操作风险rm 命令基础常见误操作案例防护方案使用 rm编程 别名及安全删除

Linux下MySQL数据库定时备份脚本与Crontab配置教学

《Linux下MySQL数据库定时备份脚本与Crontab配置教学》在生产环境中,数据库是核心资产之一,定期备份数据库可以有效防止意外数据丢失,本文将分享一份MySQL定时备份脚本,并讲解如何通过cr... 目录备份脚本详解脚本功能说明授权与可执行权限使用 Crontab 定时执行编辑 Crontab添加定

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

从入门到精通详解Python虚拟环境完全指南

《从入门到精通详解Python虚拟环境完全指南》Python虚拟环境是一个独立的Python运行环境,它允许你为不同的项目创建隔离的Python环境,下面小编就来和大家详细介绍一下吧... 目录什么是python虚拟环境一、使用venv创建和管理虚拟环境1.1 创建虚拟环境1.2 激活虚拟环境1.3 验证虚

linux系统上安装JDK8全过程

《linux系统上安装JDK8全过程》文章介绍安装JDK的必要性及Linux下JDK8的安装步骤,包括卸载旧版本、下载解压、配置环境变量等,强调开发需JDK,运行可选JRE,现JDK已集成JRE... 目录为什么要安装jdk?1.查看linux系统是否有自带的jdk:2.下载jdk压缩包2.解压3.配置环境

Linux搭建ftp服务器的步骤

《Linux搭建ftp服务器的步骤》本文给大家分享Linux搭建ftp服务器的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录ftp搭建1:下载vsftpd工具2:下载客户端工具3:进入配置文件目录vsftpd.conf配置文件4:

Linux实现查看某一端口是否开放

《Linux实现查看某一端口是否开放》文章介绍了三种检查端口6379是否开放的方法:通过lsof查看进程占用,用netstat区分TCP/UDP监听状态,以及用telnet测试远程连接可达性... 目录1、使用lsof 命令来查看端口是否开放2、使用netstat 命令来查看端口是否开放3、使用telnet

Linux系统管理与进程任务管理方式

《Linux系统管理与进程任务管理方式》本文系统讲解Linux管理核心技能,涵盖引导流程、服务控制(Systemd与GRUB2)、进程管理(前台/后台运行、工具使用)、计划任务(at/cron)及常用... 目录引言一、linux系统引导过程与服务控制1.1 系统引导的五个关键阶段1.2 GRUB2的进化优

Java List 使用举例(从入门到精通)

《JavaList使用举例(从入门到精通)》本文系统讲解JavaList,涵盖基础概念、核心特性、常用实现(如ArrayList、LinkedList)及性能对比,介绍创建、操作、遍历方法,结合实... 目录一、List 基础概念1.1 什么是 List?1.2 List 的核心特性1.3 List 家族成

Linux查询服务器 IP 地址的命令详解

《Linux查询服务器IP地址的命令详解》在服务器管理和网络运维中,快速准确地获取服务器的IP地址是一项基本但至关重要的技能,下面我们来看看Linux中查询服务器IP的相关命令使用吧... 目录一、hostname 命令:简单高效的 IP 查询工具命令详解实际应用技巧注意事项二、ip 命令:新一代网络配置全