system V共享内存

news2024/11/25 0:34:24

一、前言

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

但其实比它好用的进程间通信还有很多种,但是因为有人还是会用这种方式,我们还是需要了解一下。

IPC就是进程间通信的缩写——Interprocess communication

二、共享内存的原理

这是两个进程,以及他们的虚拟地址空间,还有页表。他们都是通过页表映射打开并使用内存中的空间。

要想实现这两个进程间通信,还是需要让他们看到同一份资源,也就是我们手动在内存中开辟一段空间, 然后这两个进程能通过页表映射到同一块空间不就行了。

被开辟的这段空间位于虚拟地址空间的共享区,也叫内存映射段:

此处,内核将硬盘文件的内容直接映射到内存, 任何应用程序都可通过Linux的mmap()系统调用请求这种映射。内存映射是一种方便高效的文件I/O方式, 因而被用于装载动态共享库。如C标准库函数(fread、fwrite、fopen等)和Linux系统I/O函数,它们都是动态库函数,其中C标准库函数都被封装在了/lib/libc.so库文件中,都是二进制文件。这些动态库函数都是与位置无关的代码,即每次被加载进入内存映射区时的位置都是不一样的,因此使用的是其本身的逻辑地址,经过变换成线性地址(虚拟地址),然后再映射到内存。而静态库不一样,由于静态库被链接到可执行文件中,因此其位于代码段,每次在地址空间中的位置都是固定的。
 

 

三、需要学习的函数

shmget函数

功能:用来创建共享内存

原型:int shmget(key_t key,size_t size ,int shmflg);

我们从后往前一个个介绍它的参数。

shmflg参数:

这是调用这个函数式输入的命令,是位图结构,有九个权限标志构成。

会用到的命令:IPC_CREAT and IPC_EXCL

// 单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回

// IPC_EXCL不能单独使用,一般都要配合IPC_CREAT

// IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!

应用场景:

IPC_CREAT:创建一个共享内存,如果没有的话就创建这个内存,有了的话就直接返回这个内存的shmid——用于客户端获取服务端创建的共享内存shmid。

IPC_CRAET | IPC_EXCL:一般用于服务端创建一个全新的共享内存。

size参数:

这个很简单就是设置要创建的共享内存的大小。

key参数:

在讲这个参数的时候,我们要先知道,这个参数存在的意义

 这样的情况,是我们的理想情况,一个共享内存只有两个进程在使用。但是我们需要知道,一个操作系统,同时可能存在很多的进程,也可能同时存在很多被打开的共享内存。我们要如何让需要通信的两个进程准确的打开需要的共享内存呢?

就像是管理进程一样,操作系统将全部共享内存也管理起来了,也就是我们理解操作系统非常重要的原则:先描述,再组织。

先描述:

struct shmid_ds {
 struct ipc_perm shm_perm; /* operation perms */
 int shm_segsz; /* size of segment (bytes) */
 __kernel_time_t shm_atime; /* last attach time */
 __kernel_time_t shm_dtime; /* last detach time */
 __kernel_time_t shm_ctime; /* last change time */
 __kernel_ipc_pid_t shm_cpid; /* pid of creator */
 __kernel_ipc_pid_t shm_lpid; /* pid of last operator */
 unsigned short shm_nattch; /* no. of current attaches */
 unsigned short shm_unused; /* compatibility */
 void *shm_unused2; /* ditto - used by DIPC */
 void *shm_unused3; /* unused */
};

这就是描述一个共享内存的结构体,这里面包含了它全部的属性,比如:创建时间、谁创建的、被连接数量、size、权限、shmid、包括上面我们说的key参数也是被封装在shm_perm里面的。

再组织:

将这些共享内存通过链表结构组织起来。

回到上面的话题,我们要怎么才能让一个进程打开它需要的那个共享内存呢?

我们创建共享内存的那个进程拿一个key值去创建,在创建的时候就将key值赋值给了上面的shm_ds结构体。

就是通过key参数,getshm函数拿着这个参数一个个比对上面这个链表节点去寻找我们需要的共享内存。

当然,要实现查找,我们还是传入IPC_CREAT这个命令的。

这个key是怎么生产的呢?反正看到不是我们随便自定义的,我们需要保证它的唯一性,才不会出错————通过ftok函数

ftok函数:

功能:生成一个key值

原型:key_t ftok(const char *pathname, int proj_id);

pathname和proj_id只是用来帮助生成的key值的

需要注意的是,ftok函数生成的key_t类型的标识符并不保证唯一,因此在使用时需要结合其他方法保证标识符的唯一性,如使用进程ID作为proj_id参数,或者使用更为安全的IPC创建函数(如shmget、msgget、semget)。

shmat函数

上面的函数只是将共享内存创造出来了,还没将它映射到我们的进程虚拟地址空间,我们还使用不了。

功能:将共享内存段连接到进程地址空间

原型:void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

shmid:共享内存标识符

shmaddr:指定连接的地址,这个我们不用动,也不知道怎么用,直接传入一个nullptr进去就行了,操作系统会自动帮我们选择一个地址。

shmflg:它的两个可能取值是SHM_RND、SHM_RDONLY

返回值:成功了返回一个指针,指向共享内存开始的地址,失败返回-1.

shmdt函数

功能:将共享内存段与当前进程脱离,与shmat相对。

原型:int shmdt(const void* shmaddr);

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

shmctl函数

功能:用于控制共享内存

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

参数:

shmid shmget返回的共享内存标识码

cmd 将要采取的动作,如下

 buf 指向一个保存着共享内存的模糊状态和访问权限的数据结构

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

四、代码实例、

makefile:

.PHONY:all
all: shmclient shmserver
shmclient: shmclient.cc
	g++ $@ $^ -std=c++11
shmserver: shmserver.cc
	g++ $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f shmclient shmserver

 comm.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__

#include<iostream>
#include<cerror>
#include<cstdio>
#include<cstring>
#include<cassert>
#include<string>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<sys/stat.h>

using namespace std;

//  IPC_CREAT and IPC_EXCL
// 单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回
// IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
// IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!


#define PATHNAME "."
#define PROJID ox6666

const int size=4096;

//获取key
key_t getkey()
{
    key_t k =ftok(PATHNAME,PROJID);
    if(k==-1)
    {
        cout<<"error:"<<errno<<":"<<strerror(errno)<<endl;
        exit(1);
    }
    return k;
}

string toHEX(int x)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"ox%x",x);
    return buffer;
}

static int creatShmHelper(key_t key,int size,int flag)
{
    int shmid=shmget(k,size,flag);
    if(shmid==-1)
    {
        cout<<"error"<<errno<<":"<<strerror(errno)<<endl;
        exit(2);
    }
    return shmid;
}

int createShm(key_t k,int size)
{
    umask(0);
    return creatShmHelper(k,size,IPC_CREAT|IPC_EXCL| 0666)
}

int getShm(key_t k,int size)
{
    return creatShmHelper(k,size,IPC_CREAT);
}

char *attachShm(int chmid)
{
    char *start=(char*)shmat(shmid,nullptr,0);
    return start;
}


void detachShm(char *start)
{
    int n=shmdt(start);
    assert(n!=-1);
    (void )n;

}

//用一个临时对象封装起来
#define SERVER 1
#define CLIENT 0
class Init
{
    public :
        init(int t):type(t)
        {
            key_t k=getkey();
            if(type==SERVER)
            {
                shmid=createShm(k,size);
            }
            else getShm(k,size);
            start=attachShm(chmid);
        }
        char *getStart(){ return start; }
        ~init()
        {
            detachShm(start);
            if(type==SERVER)
            {
                delShm(shmid);
            }
        }
    private:
    char * start;
    int type;//通过type辨别调用用户
    int shmid;
}

shmserver.cc

#include"comm.hpp"
#include<unistd.h>

int main()
{
    Init init(SERVER);
    char *start=init.getStart();
    
    int n=0;

    while(m<=30)
    {
        cout<<"client->server#"<<start<<endl;
        sleep(1);
        n++;
    }
    return 0;
}

shmclient.cc

#include "comm.hpp"
#include <unistd.h>
int main()
{
    Init init(CLIENT);
    char *start = init.getStart();
    char c = 'A';

    while(c <= 'Z')
    {
        start[c - 'A'] = c;
        c++;
        start[c - 'A'] = '\0';
        sleep(1);
    }

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/616701.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android HTTP请求方式:HttpClient

1.HttpClient使用流程 基本流程&#xff1a; 2.HttpClient使用示例 1&#xff09;使用HttpClient发送GET请求 直接贴下简单的发送Get请求的代码&#xff1a; public class MainActivity extends Activity implements OnClickListener { private Button btnGet; private WebV…

什么是OSPF被动接口?如何配置?华为、思科、瞻博网络三厂商命令来了

OSPF&#xff08;开放最短路径优先&#xff09;是一种常用的动态路由协议&#xff0c;用于在大型网络中实现路由选择。在OSPF中&#xff0c;被动接口是一种特殊类型的接口&#xff0c;它被用来监测网络中的邻居关系&#xff0c;并接收来自邻居发送的Hello消息。被动接口不主动发…

华为OD机试之在字符串中找出连续最长的数字串(含“+-”号)(Java源码)

在字符串中找出连续最长的数字串(含“”号) 输入描述 请在一个字符串中找出连续最长的数字串&#xff0c;并返回这个数字串。 如果存在长度相同的连续数字串&#xff0c;返回最后一个。 如果没有符合条件的字符串&#xff0c;返回空字符串””。 注意&#xff1a; 数字串可以由…

Ansible进阶2——角色管理

文章目录 一、角色1.1 获取角色方式1.2 角色结构1.3 定义变量和默认变量1.4 使用方法1.5 控制playbook中的任务执行流程 二、红帽企业Linux系统角色2.1 常见系统角色2.2 使用系统时间同步角色 三、自定义角色3.1 创建角色目录结构3.2 编写角色内容3.3 编写总结 四、ansible gal…

【C++】内存管理的基本操作,new与delete的实现原理以及operator new与operator delete函数

文章目录 前言一、new,delete操作内置类型二、new/delete操纵自定义类型3. operator new与operator delete函数4. new/delete实现原理4.malloc/free和new/delete的区别 前言 程序中内存的划分&#xff1a; 栈又叫堆栈–非静态局部变量/函数参数/返回值等等&#xff0c;栈是向…

高考必胜,归来仍是少年!

高考必胜&#xff0c;归来仍是少年&#xff01; 这是小索奇专门为高考生写的文章高考生 我以前给大家弄过一些免费的付费资料&#xff0c;现在看到后台很多伙伴们都在寻找资料&#xff0c;一些没有充分准备的小伙伴此刻一定很匆忙吧&#xff01; 我想对大家说&#xff1a; 高…

基于 FFMPEG 的跨平台视频播放器简明教程(二):基础知识和解封装(demux)

系列文章目录 基于 FFMPEG 的跨平台视频播放器简明教程&#xff08;一&#xff09;&#xff1a;FFMPEG Conan 环境集成 文章目录 系列文章目录前言基础知识视频&#xff0c;你所看到的&#xff01;音频 - 你所听到的声音编解码器 - 压缩数据容器 - 存放音频和视频的地方 解封…

vue3.0与vue2.0的区别简记(基于官方文档)

vue3.0与vue2.0的区别简记&#xff08;基于官方文档&#xff09; 基于vue3.0和vue2.0官方文档简单记录vue3.0版本和2.0版本的区别。 一直没有看文档的习惯&#xff08;就是不爱学习&#xff0c;现在吃了没文化的亏&#xff09;&#xff0c;遇到问题才去补充点食粮&#xff0c…

祝2023高考考生高考顺利!金榜题名

前言&#xff1a;光阴似箭&#xff0c;岁月如梭。明天就是全国每年一次的高考了&#xff0c;我也即将结束我的大一生活成为一名大二的小学长啦嘿嘿。而我今天呢主要是想祝马上要高考的学弟学妹们高考顺利&#xff0c;金榜题名&#xff0c;并且借此机会顺便讲讲我的高考前后的故…

解决python通过pip离线安装flask,numpy报错解决(centos)

1. 离线安装Python https://www.python.org/ftp/python/3.7.1/Python-3.7.1.tgz 解压&#xff0c;编译&#xff0c;安装 tar xzvf Python-3.7.1.tgz ./configuremakemake install 离线环境下如果系统不是完整版安装编译会报错&#xff0c;需要解决依赖问题&#xff0c;如下&am…

5 种常见的 Linux 打包类型:tar、gzip、bzip2、zip 、 7z

在 Linux 系统中&#xff0c;打包和压缩文件是常见的操作。不同的打包类型适用于不同的用途和需求。本文将详细介绍 5 种常见的 Linux 打包类型&#xff0c;包括tar、gzip、bzip2、zip 和 7z&#xff0c;以及它们的特点、使用方法和适用场景。 1. tar tar&#xff08;tape arc…

音悦台项目测试报告

文章目录 项目背景项目功能测试计划与设计功能测试自动化测试 测试结果功能测试结果UI自动化测试结果 项目背景 现如今人们的生活压力大&#xff0c;容易使人疲惫&#xff0c;为了使得人们在闲暇之余可以听音乐放松&#xff0c;为此设计出一款轻量的听音乐网站&#xff0c;快速…

centos安装部署Kubernetes(k8s)步骤使用kubeadm方式

文章目录 1、修改系统配置2、安装docker应用3. 拉取docker镜像4、cri-dockerd安装5、安装kubeadm和kubelet6、配置flannel网络插件7、Node节点加入集群操作 机器地址&#xff1a; 192.168.0.35 k8s-master 192.168.0.39 k8s-node1 192.168.0.116 k8s-node2 1、修改系统配置 修…

Web应用技术(第十六周/持续更新)

本次联系基于how2j的教程完成对SpringBoot的初步学习。 初识Springboot 学习导入&#xff1a;1.第一个基于SpringBoot的项目&#xff1a;&#xff08;1&#xff09;application.java&#xff1a;该文件中的核心代码&#xff1a; &#xff08;2&#xff09;HelloController.jav…

一. ATR技术指标的定义与运用

一. ATR的定义 1. 什么是ATR ATR英文全名是Average true range&#xff0c;翻译过来就是平均真实波幅&#xff0c;这个指标主要用来衡量最近N天TR(真实波幅)的平均值。 2. ATR相关计算公式 T R [ ( 最高价 − 最低价 ) &#xff0c; ( 前一次收盘价 − 最高价 ) &#xff0…

macOS Sonoma 14.0 Beta 1 (23A5257q) Boot ISO 原版可引导镜像

macOS Sonoma 14.0 Beta 1 (23A5257q) Boot ISO 原版可引导镜像 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Windows 和…

OAuth2.0 授权 OpenID Connect 身份认证

文章目录 OAuth2.0历史由来名词解释授权码模式&#xff08;authorization code 最常用&#xff09;先换取code&#xff0c;再根据 code 换取 access_token原因 简化模式&#xff08;implicit&#xff09;密码模式&#xff08;resource owner password credentials&#xff09;客…

【P49】JMeter 查看结果树(View Results Tree)

文章目录 一、查看结果树&#xff08;View Results Tree&#xff09;参数说明二、准备工作三、测试计划设计 一、查看结果树&#xff08;View Results Tree&#xff09;参数说明 可以查看取样器的请求参数、返回结果 使用场景&#xff1a;一般在调试测试计划期间用来查看取样…

Bigdata1234.cn课堂测试

Java源文件中有一个公共类名称为Test&#xff0c;则该源文件名必须是&#xff1a;Test.java . Java中的基本数据类型共有8个&#xff1a;byte、short、int、long、float、double、char、boolean。 . Eclipse中内容补全的快捷键是alt/ Eclipse 中自动导包的快捷键是 Ctrl Sh…

MySQL数据库从入门到精通学习第8天(表数据的查询)

表数据的查询 基本查询语句单表查询聚合函数查询多表连接查询子查询合并查询结果定义表和字段的别名使用正则表达式查询 基本查询语句 SELECT 语句非常的强大&#xff0c;是最常用的查询语句。他具有一个固定的格式&#xff0c;如下&#xff1a; SELECT 查询的内容 FROM 数据…