【Linux】:system V共享内存
创始人
2024-09-26 13:20:44
0

朋友们、伙计们,我们又见面了,本期来给大家带来system V共享内存相关代码和知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

​ 

目录

1. system V共享内存 

1.1 共享内存的原理

2. 共享内存系统接口

① 创建共享内存

② 挂接共享内存 

③ 断开共享内存 

④ 控制共享内存 

2.1 共享内存数据结构 

3. 示例代码 

4. 共享内存特点 

4.1 共享内存代码改进 


1. system V共享内存 

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

1.1 共享内存的原理

  • 首先有两个互不相干的进程需要进行通信,此时OS会在内存空间中开辟一块空间,将这块空间先通过第一个进程的页表映射到自己进程地址空间的共享区中,此时第一个进程就能拿到这块空间的起始地址;
  • 同样的第二个进程也将这块内存空间通过页表映射到自己进程地址空间的共享区,第二个进程也可以拿到同样的起始地址;
  • 此时这两个进程就通过同样的起始地址看到了由OS提供的同一份资源,这就形成了进程间通信的前提,此时若要通信直接向该内存中写入或者读取数据即可,这块内存空间就叫做共享内存;
  • OS中不止存在一个共享内存,所以众多的共享内存需要被OS管理,先描述,再组织(struct shm) 。
  • 一个共享内存不仅仅被两个进程映射,可能存在多个进程,那么如何保证第二个之后参与通信的进程看到的就是同一个共享内存呢?所以每个共享内存就要有唯一的标识。

2. 共享内存系统接口

① 创建共享内存

  • shmget接口:

  • 参数: 

key:唯一标识键值key

可以通过ftok函数专门生成:


size:共享内存大小

共享内存的大小强烈建议设置为4096的整数倍;
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的

IPC_CREAT:shm不存在就创建,存在就回去并返回;

IPC_EXCL:不单独使用,配合IPC_CREAT | IPC_EXCL使用,表示shm不存在就创建,存在就出错返回,保证了创建的共享内存都是全新的。

IPC_CREAT | IPC_EXCL | 0664:表示创建出的共享内存的权限是0664。       

  • 返回值:

成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

  • 在命令行输入ipcs -m:查看创建的共享内存。 
  • 在命令行输入ipcrm -m shmid:删除指定的共享内存。
  • 共享内存生命周期随内核,进程退出依旧存在。

② 挂接共享内存 

  • shmat接口:

  • 参数:

shmid:共享内存标识
shmaddr:指定连接的地址(设置为nullptr)
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY(设置为0)

  • 返回值:

成功返回一个指针,指向共享内存第一个节;失败返回-1

返回值可以以任意类型接收。

说明:

  • shmaddr为nullptr,核心自动选择一个地址
  • shmaddr不为nullptr且shmflg无SHM_RND标记,则以shmaddr为连接地址。
  • shmaddr不为nullptr且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
  • shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

③ 断开共享内存 

shmdt接口:

参数:

shmaddr:由shmat所返回的指针
返回值:

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

④ 控制共享内存 

shmctl接口:

参数:

shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)

IPC_STAT:把shmid_as结构中的数据设置为共享内存的当前关联值

IPC_SET:在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值

IPC_RMID:删除共享内存
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
若要删除共享内存第二个参数设置IPC_RMID,第三个参数直接设置为nullptr。

在应用层我们通常使用shmid来操作共享内存,在内核中用key来标识shm的唯一性! 

2.1 共享内存数据结构 

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 */ };  struct ipc_perm  {     key_t          __key;    /* Key supplied to shmget(2) */     uid_t          uid;      /* Effective UID of owner */     gid_t          gid;      /* Effective GID of owner */     uid_t          cuid;     /* Effective UID of creator */     gid_t          cgid;     /* Effective GID of creator */     unsigned short mode;     /* Permissions + SHM_DEST and                                 SHM_LOCKED flags */     unsigned short __seq;    /* Sequence number */ };
#include  #include  #include  #include  #include  #include   #include "comm.hpp"   int main() {     // 1. 创建shm key值     key_t key = Get_key();     std::cout << "key : " << ToHex(key) << std::endl;      // 2. 创建共享内存     int shmid = CreateShm(key);     std::cout << "shmid: " << shmid << std::endl;     std::cout << "开始将shm映射到进程的地址空间中" << std::endl;      struct shmid_ds ds;     shmctl(shmid, IPC_STAT, &ds); // 获取结构数据      std::cout << ToHex(ds.shm_perm.__key) << std::endl;  // key值     std::cout << ds.shm_segsz << std::endl;              // 空间大小     std::cout << ds.shm_atime << std::endl;     std::cout << ds.shm_nattch << std::endl;             // 挂接数量      // 3. 挂接共享内存     char* s = (char *)shmat(shmid, nullptr, 0);      // 4. 断开共享内存     shmdt(s);     std::cout << "开始将shm从进程的地址空间中移除" << std::endl;      // 5. 删除共享内存     shmctl(shmid, IPC_RMID, nullptr);     std::cout << "开始将shm从OS中删除" << std::endl;     return 0; }

3. 示例代码 

comm.hpp

公共资源

#pragma once  #include  #include  #include  #include  #include  #include  #include  //Inter-Process Communication #include  #include  #include  #include   const std::string pathname = "./"; const int proj_id = 0x11; const int size = 4069; // 4096*n大小即可  // 按照16进制打印 std::string ToHex(int key) {     char buffer[1024];     snprintf(buffer, sizeof(buffer), "0x%x", key);     return buffer; } // 获取shm key key_t Get_key() {     key_t key = ftok(pathname.c_str(), proj_id);     if(key < 0)     {         std::cerr<<"errno :" << errno << ", errstring" << strerror(errno) << std::endl;         exit(1);     }     return key; }  int CreateShmHelper(key_t key, int flag) {     int shmid = shmget(key, size, flag);     if(shmid < 0)     {         std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;         exit(2);     }      return shmid; } // 创建共享内存 int CreateShm(key_t key) {     return CreateShmHelper(key, IPC_CREAT|IPC_EXCL|0644); } // 获取共享内存 int GetShm(key_t key) {     return CreateShmHelper(key, IPC_CREAT/*0也可以*/); } 

client.cc

发送数据

#include  #include  #include  #include  #include  #include   #include "comm.hpp"  int main() {     // 1. 创建key值     key_t key = Get_key();      // 2. 获取共享内存     int shmid = GetShm(key);      // 3. 挂接共享内存     char *s = s = (char *)shmat(shmid, nullptr, 0);     std::cout << "attach shm done" << std::endl;      // 3.1 写入     char c = 'a';     for (; c <= 'z'; c++)     {         s[c - 'a'] = c;         std::cout << "write : " << c << " done" << std::endl;         sleep(1);     }      // 4. 断开共享内存     shmdt(s);     std::cout << "detach shm done" << std::endl;      return 0; }

server.cc:

接受数据

#include  #include  #include  #include  #include  #include   #include "comm.hpp"  int main() {     // 1. 创建shm key值     key_t key = Get_key();     std::cout << "key : " << ToHex(key) << std::endl;      // 2. 创建共享内存     int shmid = CreateShm(key);     std::cout << "shmid: " << shmid << std::endl;     std::cout << "开始将shm映射到进程的地址空间中" << std::endl;     sleep(3);      // 3. 挂接共享内存     // 以字符串的方式访问     char* s = (char *)shmat(shmid, nullptr, 0);      // 3.1 读取数据     while(true)     {         std::cout << "共享内存的内容:" << s << std::endl;         sleep(1);     }      sleep(1);     // 4. 断开共享内存     shmdt(s);     std::cout << "开始将shm从进程的地址空间中移除" << std::endl;      // 5. 删除共享内存     shmctl(shmid, IPC_RMID, nullptr);     std::cout << "开始将shm从OS中删除" << std::endl;      return 0; }

4. 共享内存特点 

  • 1. 读写端不同步,直接暴露给所有使用者,存在安全问题;
  • 2. 共享内存是所有进程间通信速度最快的(减少了数据拷贝次数);
  • 3. 可以提供较大的空间。

凡是数据迁移,都叫做数据的拷贝(从键盘读取数据、将数据打印在显示器)。 

4.1 共享内存代码改进 

因为共享内存没有实现同步的机制,所以我们需要让他进行同步,因此我们可以在里面加入管道,当写端向shm写入数据时顺便向管道里面发送数据,只有当读端检测到管道有数据才可以从shm中读取。

comm.hpp:

公共资源

#pragma once  #include  #include  #include  #include  #include  #include  #include  //Inter-Process Communication #include  #include  #include  #include   const std::string pathname = "./"; const int proj_id = 0x11; const int size = 4069; // 4096*n大小即可 const std::string filename = "fifo";  // 按照16进制打印 std::string ToHex(int key) {     char buffer[1024];     snprintf(buffer, sizeof(buffer), "0x%x", key);     return buffer; } // 获取shm key key_t Get_key() {     key_t key = ftok(pathname.c_str(), proj_id);     if(key < 0)     {         std::cerr<<"errno :" << errno << ", errstring" << strerror(errno) << std::endl;         exit(1);     }     return key; }  int CreateShmHelper(key_t key, int flag) {     int shmid = shmget(key, size, flag);     if(shmid < 0)     {         std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;         exit(2);     }      return shmid; } // 创建共享内存 int CreateShm(key_t key) {     return CreateShmHelper(key, IPC_CREAT|IPC_EXCL|0644); } // 获取共享内存 int GetShm(key_t key) {     return CreateShmHelper(key, IPC_CREAT/*0也可以*/); }  bool MakeFifo() {     int n = mkfifo(filename.c_str(), 0666);     if(n < 0)     {         std::cerr << "errno: " << errno << ", errstring: " << strerror(errno) << std::endl;         return false;     }      std::cout << "mkfifo success... read" << std::endl;     return true; } 

client.cc:

发送数据

#include  #include  #include  #include  #include  #include   #include "comm.hpp"  int main() {     // 1. 创建key值     key_t key = Get_key();      // 2. 获取共享内存     int shmid = GetShm(key);      // 3. 挂接共享内存     char *s = s = (char *)shmat(shmid, nullptr, 0);     std::cout << "attach shm done" << std::endl;      // 3.1 打开管道文件     bool r = MakeFifo();     if (!r)         return 1;     int fd = open(filename.c_str(), O_WRONLY);      // 3.2 写入     char c = 'a';     for (; c <= 'z'; c++)     {         s[c - 'a'] = c;         std::cout << "write : " << c << " done" << std::endl;         sleep(1);                  // 向管道发送任意数据,实现同步机制         int code = 0;         write(fd, &code, sizeof(code));     }      // 4. 断开共享内存     shmdt(s);     std::cout << "detach shm done" << std::endl;      return 0; }

server.cc:

接受数据

#include  #include  #include  #include  #include  #include   #include "comm.hpp"  int main() {     // 1. 创建shm key值     key_t key = Get_key();     std::cout << "key : " << ToHex(key) << std::endl;      // 2. 创建共享内存     int shmid = CreateShm(key);     std::cout << "shmid: " << shmid << std::endl;     std::cout << "开始将shm映射到进程的地址空间中" << std::endl;     sleep(3);      // 3. 挂接共享内存     // 以字符串的方式访问     char* s = (char *)shmat(shmid, nullptr, 0);      // 3.1 读取数据     while(true)     {         int fd = open(filename.c_str(), O_RDONLY);         int code = 0;         ssize_t n = read(fd, &code, sizeof(code));         if (n > 0)         {             // 直接读取的哦             std::cout << "共享内存的内容: " << s << std::endl;             sleep(1);         }         else if (n == 0)         {             break;         }     }      sleep(1);     // 4. 断开共享内存     shmdt(s);     std::cout << "开始将shm从进程的地址空间中移除" << std::endl;      // 5. 删除共享内存     shmctl(shmid, IPC_RMID, nullptr);     std::cout << "开始将shm从OS中删除" << std::endl;      return 0; }

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!  

相关内容

热门资讯

屏蔽安卓系统消息通知,畅享无干... 手机里的消息通知是不是让你眼花缭乱,有时候甚至觉得它们比你的好友圈更新还要频繁呢?别急,今天就来教你...
荣耀新折叠是安卓系统,安卓系统... 你知道吗?最近手机界可是掀起了一股新潮流,那就是荣耀新折叠手机的发布。这款手机不仅外观设计独特,而且...
iphone连安卓系统电视,轻... 你有没有想过,你的iPhone和安卓系统的电视竟然能如此亲密地“牵手”呢?没错,就是那种你拿着iPh...
绿钻怎么退款安卓系统,安卓系统... 你有没有遇到过这种情况:买了一颗闪闪发光的绿钻,结果发现它并不适合你的安卓系统,或者是其他原因让你想...
请假管理系统下载安卓,轻松管理... 你有没有想过,工作生活中,请假这件事儿竟然也能变得如此轻松愉快?没错,就是那个让你头疼的请假流程,现...
安卓系统为什么没网络,探究原因... 手机没网络,这可真是让人头疼的小麻烦!你有没有遇到过这种情况:手机屏幕上显示着“无网络连接”,而你明...
安卓苹果换系统安装教程,安卓与... 亲爱的手机控们,是不是觉得手机用久了,系统卡得像蜗牛爬?别急,今天就来教你怎么给安卓和苹果手机换上全...
魅族安卓系统下载软件,尽享智能... 你有没有发现,最近手机圈里又掀起了一股热潮?没错,就是魅族的新款手机!这款手机不仅外观时尚,性能强大...
平板电脑安卓系统12墨,平板电... 亲爱的读者们,你是否也和我一样,对科技新品的到来充满期待?今天,我要和你聊聊一款让人眼前一亮的新品—...
荣耀畅玩刷安卓系统,解锁更多可... 你有没有想过,你的荣耀畅玩手机,其实可以焕发第二春呢?没错,就是刷上全新的安卓系统!想象你的手机瞬间...
为什么安卓系统老是卡机,性能瓶... 手机卡顿真是让人头疼!尤其是安卓系统,有时候用着用着就突然卡住了,让人忍不住想摔手机。那么,为什么安...
哪款盒子是安卓系统,智能娱乐新... 你有没有想过,在这个智能设备横行的时代,哪款盒子是安卓系统最让人心动呢?安卓系统以其开放性和强大的兼...
安卓Q删除系统文件,安全操作与... 亲爱的安卓用户们,你是否曾在使用安卓手机时,不小心误删了重要的系统文件,心里直发慌?别担心,今天就来...
安卓12系统怎么下载谷歌,安卓... 你有没有听说安卓12系统已经发布了?是不是也想赶紧升级体验一下新系统的魅力呢?不过,别急,升级之前你...
安卓系统怎么清除后台,安卓系统... 手机后台程序太多,是不是感觉手机越来越卡?别急,今天就来教你怎么轻松清除安卓系统的后台程序,让你的手...
ios和安卓重置系统,轻松恢复... 手机用久了是不是感觉卡得要命?别急,今天就来给你揭秘如何给iOS和安卓手机来个彻底的重置,让它焕发新...
华为的手机系统与安卓,融合与创... 亲爱的读者们,你是否曾好奇过,为什么华为的手机系统能在众多安卓手机中独树一帜?今天,就让我们一起揭开...
安卓系统如何加通知声音,安卓系... 你有没有发现,手机上的通知声音有时候就像是个小闹钟,总是不请自来地提醒你各种信息。不过,有时候这个“...
安卓系统怎么锁定主屏,安卓系统... 你是不是也和我一样,手机里藏着不少小秘密,不想让别人轻易窥探呢?别急,今天就来教你怎么给安卓手机的主...
安卓系统激活提示谷歌,谷歌助你... 你刚刚入手了一台全新的安卓手机,是不是兴奋得手舞足蹈?不过,别急着高兴,激活手机之前,可别忘了谷歌的...