【Linux】System V 共享内存

server/2024/9/23 11:12:49/

文章目录

  • 1. 共享内存示意图
  • 2. 共享内存数据结构
  • 3. 共享内存函数
    • `shmget`
    • `shmat`
    • `shmdt`
    • `shmctl`
  • 4. 实例代码测试共享内存
  • 5. 共享内存相关命令
  • 6. System V 消息队列(了解)
  • 7. System V 信号量(了解)

在这里插入图片描述

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

1. 共享内存示意图

在这里插入图片描述

2. 共享内存数据结构

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 */
};

3. 共享内存函数

shmget

功能:用来创建共享内存
原型int shmget(key_t key, size_t size, int shmflg);
参数key:这个共享内存段名字size:共享内存大小shmflg:由九个权限标志构成,它们的用法和创建文件时使用的 mode 模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat

功能:将共享内存段连接到进程地址空间
原型void* shmat(int shmid, const void* shmaddr, int shmflg);
参数shmid:共享内存标识shmaddr:指定连接的地址shmflg:它的两个可能取值是 SHM_RND 和 SHM_RDONLY
返回值:成功返回一个指针,指向共享内存的地址;失败返回-1
  • 说明:
shmaddr 为 NULL,核心自动选择一个地址。
shmaddr 不为 NULL 且 shmflg 无 SHM_RND 标记,则以 shmaddr 为连接地址。
shmaddr 不为 NULL 且 shmflg 设置了 SHM_RND 标记,则连接的地址会自动向下调整为 SHMLBA 的整数倍;公式:shmaddr - (shmaddr % SHMLBA)。
shmflg = SHM_RDONLY,表示连接操作用来只读共享内存。

shmdt

功能:将共享内存段与当前进程脱离
原型int shmdt(const void* shmaddr);
参数shmaddr:由 shmat 所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl

功能:用于控制共享内存
原型int shmctl(int shmid, int cmd, struct shmid_ds* buf);
参数shmid:由 shmget 返回的共享内存标识码cmd:将要采取的动作(有三个可取值)buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

在这里插入图片描述

4. 实例代码测试共享内存

  • 使用共享内存通信,一定是一个进程创建新的 shm,另一个直接获取共享内存即可。

代码结构

$ ls
Comm.hpp  Fifo.hpp  Makefile  ShmClient.cc  ShmServer.cc$ cat Makefile 
.PHONY:all
all:shm_client shm_servershm_server:ShmServer.ccg++ -o $@ $^ -std=c++11
shm_client:ShmClient.ccg++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f shm_client shm_server

Fifo.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>using namespace std;#define Mode 0666
#define Path "./fifo"class Fifo
{
public:Fifo(const string &path = Path): _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);if (n == 0){cout << "mkfifo success" << endl;}else{cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}~Fifo(){int n = unlink(_path.c_str());if (n == 0){cout << "remove fifo file " << _path << " success" << endl;}else{cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}private:string _path; // 文件路径 + 文件名
};class Sync
{
public:Sync(): rfd(-1), wfd(-1){}void OpenReadOrDie(){rfd = open(Path, O_RDONLY);if (rfd < 0)exit(1);}void OpenWriteOrDie(){wfd = open(Path, O_WRONLY);if (wfd < 0)exit(1);}bool Wait(){bool ret = true;uint32_t c = 0;ssize_t n = read(rfd, &c, sizeof(uint32_t));if (n == sizeof(uint32_t)){std::cout << "server wakeup, begin read shm..." << std::endl;}else if (n == 0){ret = false;}else{return false;}return ret;}void Wakeup(){uint32_t c = 0;ssize_t n = write(wfd, &c, sizeof(c));assert(n == sizeof(uint32_t));std::cout << "wakeup server..." << std::endl;}~Sync(){}private:int rfd;int wfd;
};#endif

Comm.hpp:

#pragma once#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using namespace std;const char *pathname = "/home/ubuntu";
const int proj_id = 0x66;// 在内核中,共享内存的大小是以4KB为基本单位的,你只能用你申请的大小。建议申请大小是N*4KB
const int defaultsize = 4096; // 单位是字节std::string ToHex(key_t k)
{char buffer[1024];snprintf(buffer, sizeof(buffer), "0x%x", k);return buffer;
}key_t GetShmKeyOrDie()
{key_t k = ftok(pathname, proj_id);if (k < 0){std::cerr << "ftok error, errno: " << errno << ", error string: " << strerror(errno) << std::endl;exit(1);}return k;
}int CreateShmOrDie(key_t key, int size, int flag)
{int shmid = shmget(key, size, flag);if (shmid < 0){std::cerr << "shmget error, errno: " << errno << ", error string: " << strerror(errno) << std::endl;exit(2);}return shmid;
}int CreateShm(key_t key, int size)
{// IPC_CREAT: 不存在就创建,存在就获取// IPC_EXCL: 没有意义// IPC_CREAT | IPC_EXCL: 不存在就创建,存在就出错返回return CreateShmOrDie(key, size, IPC_CREAT | 0666);
}int GetShm(key_t key, int size)
{return CreateShmOrDie(key, size, IPC_CREAT);
}void DeleteShm(int shmid)
{int n = shmctl(shmid, IPC_RMID, nullptr);if (n < 0){std::cerr << "shmctl error" << std::endl;}else{std::cout << "shmctl delete shm success, shmid: " << shmid << std::endl;}
}void ShmDebug(int shmid)
{struct shmid_ds shmds;int n = shmctl(shmid, IPC_STAT, &shmds);if (n < 0){std::cerr << "shmctl error" << std::endl;return;}std::cout << "shmds.shm_segez: " << shmds.shm_segsz << std::endl;std::cout << "shmds.shm_nattch: " << shmds.shm_nattch << std::endl;std::cout << "shmds.shm_ctime: " << shmds.shm_ctime << std::endl;std::cout << "shmds.shm_perm.__key: " << ToHex(shmds.shm_perm.__key) << std::endl;
}void *ShmAttach(int shmid)
{// 核心自动选择一个地址void *addr = shmat(shmid, nullptr, 0);if ((long long int)addr == -1){std::cerr << "shmat error" << std::endl;return nullptr;}return addr;
}void ShmDetach(void *addr)
{int n = shmdt(addr);if (n < 0){std::cerr << "shmdt error" << std::endl;}
}

ShmServer:

#include "Comm.hpp"
#include "Fifo.hpp"int main()
{// 1. 获取keykey_t key = GetShmKeyOrDie();std::cout << "key: " << ToHex(key) << std::endl;// 2. 创建共享内存int shmid = CreateShm(key, defaultsize);std::cout << "shmid: " << shmid << std::endl;// 4. 将共享内存和进程进行挂接(关联)char *addr = (char *)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// 0. 先引入管道Fifo fifo;Sync syn;syn.OpenReadOrDie();// 可以进行通信了for (;;){if (!syn.Wait())break;std::cout << "shm content: " << addr << std::endl;}ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// 3. 删除共享内存DeleteShm(shmid);return 0;
}

ShmClient:

#include "Comm.hpp"
#include "Fifo.hpp"int main()
{key_t key = GetShmKeyOrDie();std::cout << "key: " << ToHex(key) << std::endl;int shmid = GetShm(key, defaultsize);std::cout << "shmid: " << shmid << std::endl;char *addr = (char *)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;memset(addr, 0, defaultsize);Sync syn;syn.OpenWriteOrDie();// 可以进行通信了for (char c = 'A'; c <= 'Z'; c++) // shm没有使用系统调用{addr[c - 'A'] = c;syn.Wakeup();sleep(1);}ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);return 0;
}

结果演示:

在这里插入图片描述

在这里插入图片描述

5. 共享内存相关命令

  • 共享内存,如果进程结束,我们没有主动释放它,则共享内存一直存在。
  • 共享内存的生命周期是随内核的,只有重启系统它才会自行销毁。
  • 为了更好地控制共享内存,我们要会使用命令删除它。
  • 不管是指令还是代码,想对共享内存进行控制,都需要使用 shmid ,shmid 是共享内存的唯一性标识!
# 查看共享内存
ipcs -m# 删除指定共享内存
ipcrm -m [shmid]
# 实机演示
$ ipcs -m------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 0          root       644        80         2                       
0x00000000 1          root       644        16384      2                       
0x00000000 2          root       644        280        2                       
0x6602fc97 10         ubuntu     666        4096       1$ ipcrm -m 10	# 指定shmid即可删除该共享内存资源

注意:不是必须通过手动来删除,这里只是为了演示相关指令,删除共享内存资源是进程该做的事情。

注意:共享内存没有进程同步与互斥!

6. System V 消息队列(了解)

  • 消息队列提供了从一个进程向另外一个进程发送一块数据的方法。
  • 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。
  • IPC 资源必须删除,否则不会自动清除,除非重启,所以 System V IPC 资源的生命周期随内核。

7. System V 信号量(了解)

  • 信号量主要用于同步和互斥。
  • 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥。
  • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
  • 在进程中涉及到互斥资源的程序段叫临界区。

END

http://www.ppmy.cn/server/36288.html

相关文章

气膜滑雪馆:滑雪新宠的全面介绍—轻空间

气膜滑雪馆&#xff0c;作为一种创新型的滑雪运动设施&#xff0c;正以其独特的建筑特点和功能优势&#xff0c;成为滑雪运动领域的引领者。这些场馆凭借其轻盈的结构、优良的保温性能和环保节能的特性&#xff0c;为滑雪场馆的建设提供了全新的解决方案。相较于传统建筑&#…

Nvidia发布Llama3-ChatQA-1.5: 提升对话问答和表格推理能力,平均性能超越GPT-4

前言 近日&#xff0c;Nvidia推出了一款名为Llama3-ChatQA-1.5的对话问答模型。该模型在对话式问答和检索增强型生成等能力方面表现出色&#xff0c;在综合评测指标上甚至超越了当前业界顶尖的GPT-4模型。 技术特点 Llama3-ChatQA-1.5是基于Llama-3基础模型训练而成的。相比之…

EPSON的温补晶振TG7050SKN

EPSON公司推出的温补品振(TCXO)TG7050SKN&#xff0c;尺寸大小为7.0x5.0x1.5 mm&#xff0c;具有高稳定性(105℃℃高温)等特点&#xff0c;可输出10MHz~54MHz的频率可应用在网络同步&#xff0c;BTS&#xff0c;微波&#xff0c;以及需要符合Stratum3、SyncE和IEEE1588等规范的…

快手蓝V商家采集软件使用指南

快手是中国最大的短视频平台之一&#xff0c;许多商家都在快手上开设账号进行营销推广。为了更好地管理和分析自己的粉丝数据&#xff0c;快手为商家提供了蓝V商家采集软件。本篇文章将为大家介绍快手蓝V商家采集软件的使用方法&#xff0c;并附带一些代码示例。 一、下载和安…

边界框(bounding box) 目标物体的位置和大小 交并比(Intersection over Union,IoU) 锚框(Anchor box)

边界框(bounding box) 在检测任务中,我们需要同时预测物体的类别和位置,因此需要引入一些跟位置相关的概念。通常使用边界框(bounding box,bbox)来表示物体的位置,边界框是正好能包含物体的矩形框。 在目标检测任务中,边界框(bounding box,bbox)是一个非常重要的…

【AHK】固定wps的窗口大小/解决wps2019版重新打开窗口会缩小至100*100的bug

解决办法就是每一次点击lbutton添加触发事件&#xff0c;同样也可以换成其他快捷键 #IfWinActive ahk_exe wpp.exe ~lbutton:: justfyWin(ahk_exe wpp.exe) #IfWinActive#IfWinActive ahk_exe wps.exe ~lbutton:: justfyWin(ahk_exe wps.exe) return #IfWinActive#IfWinActive …

SpringBoot+Redission实现排行榜功能

SpringBootRedission实现排行榜功能 demo地址&#xff1a;ranking-demo: 排行榜DEMO (gitee.com) 一、业务需求 实现一个排行榜&#xff0c;要求按照分数和达成这个分数的时间排序&#xff0c;即相同分数下&#xff0c;时间早的在上面 二、Redis中的zSet(有序集合) 1.简介 …

【基础算法总结】滑动窗口二

滑动窗口二 1.水果成篮2.找到字符串中所有字母异位词3.串联所有单词的子串4.最小覆盖子串 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.水…