【Linux】进程间通信之共享内存

news/2024/5/4 10:50:04/文章来源:https://blog.csdn.net/weixin_59400943/article/details/128381290

目录

  • 🌈前言
  • 🌸1、System V共享内存
    • 🍡1.1、概念
    • 🍢1.2、原理
  • 🌺2、共享内存相关函数和指令
    • 🍡2.1、shmget函数(创建)
    • 🍢2.2、shmctl函数(控制)
    • 🍧2.3、shmat函数(挂接)
    • 🍨2.4、shmdt(去挂接)
  • 🍀3、共享内存的使用
    • 🍡3.1、测试
    • 🍢3.2、共享内存与管道的区别
    • 🍧3.3、基于共享内存+管道实现访问控制

🌈前言

这篇文章给大家带来进程间通信中共享内存的学习!!!


🌸1、System V共享内存

🍡1.1、概念

概念:

  • 共享内存是与管道不同的一种进程间通信方式

  • 它们都是先让多个进程先看到一份相同的资源,所以必须提供存储数据的空间

  • 管道是在磁盘中创建文件,每次要加载到内存;共享内存是通过在物理内存创建一段内存,然后映射到共享区进行数据交互(只要创建一次,后面都会存在,因为共享内存是内核维护的)


🍢1.2、原理

进程间通信的前提是:让二个或多个不同的进程看到相同的资源(FIFO或pipe)!!!

  • 进程地址空间中有一个共享区:该区域除了映射动态库以外,还能映射其他数据(共享内存

  • 共享内存(Shared Memory):允许不同进程访问同一段的空间,常用于多进程间的通信

  • 共享内存被某个进程创建后,在物理内存中通过页表映射到该进程的进程地址空间的共享区中

  • 最后返回一个key值(标识这段空间的唯一性,key是一个整形),其他进程想要映射到相同的内存空间,必须通过key值!!!

左边是进程1,右边是进程2!!!

  • 共享内存区是最快的进程间通信方式,它不用在磁盘中创建管道文件,而是直接在物理内存映射到进程空间中进行数据交互即可

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

  • 共享内存是内核级的数据结构,进程退出后,共享内存不会被释放

在这里插入图片描述


🌺2、共享内存相关函数和指令

🍡2.1、shmget函数(创建)

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
函数解析:
  • 功能:用于创建共享内存,并返回共享内存的标识符
  • 返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1
参数:size
  • size:共享内存大小。共享内存是以页(4KB = 4096Bytes)为单位的,建议设置成页的整数倍,共享内存是由内核数据结构(struct shmid_ds)维护的

在这里插入图片描述


参数:shmflg
  • 由九个权限标记位构成,它们的用法和创建文件(open)时使用的mode模式标志是一样的
  • 该参数传参的是一个宏,创建共享内存一般传二个标记位(IPC_CREAT | IPC_EXCL)
  • 其他进程想要获取共享内存,只需要传一个标记位(IPC_CREAT)即可
  • 想要正常使用共享内存时,必须设置共享内存的权限,(… | 0666)

IPC_EXCL和IPC_CREAT配合使用,是因为如果shmget调用成功后,保证创建的是一个全新的共享内存

作用
IPC_CREAT创建共享内存,如果已经存在,就获取它,不存在,就创建它
IPC_EXCL不单独使用,必须和IPC_CREAT配合(按位或)使用,如果不存在指定的共享内存,就创建,如果存在,就报错返回
共享内存存在哪里?
  • 存在内核中,内核会给我们维护共享内存的结构,该结构也要被OS管理起来
  • OS对共享内存的管理,就是对描述共享内存的数据结构的数组进行管理(增删查改)

凡是涉及管理:都是先描述事物的属性(struct shmid_ds),然后对其进行组织(数据结构)

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 andSHM_LOCKED flags */unsigned short __seq;    /* Sequence number */
};

这个key值就是共享内存再内核数据结构中的唯一标识符的值(整形)-- 唯一性:只有一个

结论:

  • 共享内存要被管理,需要在struct shmid_ds[]中找到某一页内部中的struct ipc_perm中的key值(共享内存在内核数据结构中唯一的标识符的值)

  • 共享内存,在内核中,让不同的进程看到同一份资源,做法是”让它们拥有相同的key值“即可

在这里插入图片描述


参数:key
  • 共享内存段的名字(唯一标识符),该参数由用户设置
  • key值需要用户通过ftok系统调用获取
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char* pathname, int proj_id)
  • pathname:指定一个文件或目录的路径,主要是文件和目录都有一个唯一的inode值

  • proj_id:项目id,取值范围是【0,255】

  • 返回值:生成成功返回一个key值,失败返回-1

  • 作用:通过指定路径的文件/目录的inode值和项目id生成一个唯一性的key值!!!


shmget的使用:

comm.hpp – hpp文件:函数声明和定义可以混合使用

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cerrno>// 在某个路径下的某个文件或目录
#define PATH_NAME "/home/lyh/Linux_Study"// 项目id
#define PROJ_ID 0x12key_t CreateKey()
{key_t key = ftok(PATH_NAME, PROJ_ID);if (key < 0){std::cout <<  "ftok errno: " << strerror(errno) << std::endl;exit(1); // key值冲突,结束调用该函数的进程}return key;
}// 用于调试打印消息 -- 该函数返回一个ostream& 可充当左值进行使用
std::ostream& Log()
{return std::cout << "Fot Debug | " << "timestamp: " << (long long)time(nullptr)<< " | ";
}

IpcShmSer.cpp – 该文件创建共享内存

#include "comm.hpp"
// 设置共享内存的大小
#define MIN_SIZE 4096// 共享内存的状态标记位(创建全新的,如果存在则设置errno,并且返回-1)
const int shmflags = IPC_CREAT | IPC_EXCL;// 该文件创建共享内存
int main()
{// 1、获取key值(共享内存的唯一标识符)key_t key = CreateKey();Log() << "key: " << key << std::endl;// 2、创建共享内存 -- 0666是共享内存的权限,标识谁能使用int shmid = shmget(key, MIN_SIZE, shmflags | 0666);if (shmid < 0){Log() << "shmget errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmget sucess!!! | " << "shmid: " << shmid << std::endl;return 0;
}
[lyh@192 lesson4(共享内存)]$ pwd
/home/lyh/Linux_Study/lesson4(共享内存)
[lyh@192 lesson4(共享内存)]$ ./IpcShmSer 
Fot Debug | timestamp: 1671720131 | key: 302207089
Fot Debug | timestamp: 1671720131 | shmget sucess!!! | shmid: 31
Fot Debug | timestamp: 1671720131 | shmctl sucess!!! | shmid: 31

验证OS是否存在共享内存 – ipcs -m指令(查看共享内存的信息)

[lyh@localhost lesson4(共享内存)]$ ./IpcShmSer 
Fot Debug | timestamp: 1671799258 | key: 302207089
Fot Debug | timestamp: 1671799258 | shmget sucess!!! | shmid: 31
[lyh@localhost lesson4(共享内存)]$ ipsc -m
bash: ipsc: command not found...
Similar command is: 'ipcs'
[lyh@localhost lesson4(共享内存)]$ ipcs -m------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 9          lyh        777        16384      1          dest         
0x00000000 10         lyh        777        6193152    2          dest         
0x00000000 16         lyh        600        524288     2          dest         
0x00000000 17         lyh        777        6193152    2          dest         
0x00000000 20         lyh        600        524288     2          dest         
0x00000000 24         lyh        600        16777216   2          dest         
0x00000000 25         lyh        600        524288     2          dest         
0x00000000 26         lyh        777        2064384    2          dest         
0x00000000 27         lyh        600        524288     2          dest         
0x12035071 31         lyh        666        4096       0                   
  • key是共享内存唯一标识符;shmid是共享内存标识符;owner是创建者;perms是共享内存的权限;nattch是挂接的(映射到指定的进程)数量

  • System V共享内存下的生命周期是随内核的(只能关机重启或显示调用函数或使用指令来进行删除),进程退出后也没有释放!


🍢2.2、shmctl函数(控制)

#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数解析:
  • 功能:用于控制共享内存,在System V共享内存段上执行cmd指定的控制操作,该段的标识符在shmid中给出
  • 返回值:成功返回0,失败返回-1,并且设置errno(错误码)
参数shmid:
  • shmget函数创建共享内存返回的标识符
参数cmd:
  • 将要采取的动作(有三个可取值)
  • 该函数主要用于显示删除共享内存段,只需要IPC_RMID(宏)进行删除

注意:cmd参数中的值都是标记位,可以通过按位或进行结合使用

在这里插入图片描述

参数shmid_ds *buf:
  • 指向一个保存着共享内存的模式状态访问权限的数据结构(struct shmid_ds)
  • 该参数一般设置为nullptr

shmctl函数的使用:

comm.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>#define PATH_NAME "/home/lyh/Linux_Study"
#define PROJ_ID 0x12key_t CreateKey()
{key_t key = ftok(PATH_NAME, PROJ_ID);if (key < 0){std::cout <<  "ftok errno: " << strerror(errno) << std::endl;exit(1); // key值冲突,结束调用该函数的进程}return key;
}// 该函数返回一个ostream& 可充当左值进行使用
std::ostream& Log()
{return std::cout << "Fot Debug | " << "timestamp: " << (long long)time(nullptr)<< " | ";
}

IpcShmSer.cpp

#include "comm.hpp"
// 设置共享内存的大小
#define MIN_SIZE 4096
// 共享内存的状态(创建全新的,如果存在则设置errno,并且返回-1)
const int shmflags = IPC_CREAT | IPC_EXCL;
// 该文件创建共享内存
int main()
{// 1、获取key值(共享内存的唯一标识符)key_t key = CreateKey();Log() << "key: " << key << std::endl;// 2、创建共享内存int shmid = shmget(key, MIN_SIZE, shmflags);if (shmid < 0){sleep(5);Log() << "shmget errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmget sucess!!! | " << "shmid: " << shmid << std::endl;// 使用共享内存// 删除共享内存sleep(5);int RM = shmctl(shmid, IPC_RMID, nullptr);if (RM < 0){Log() << "shmctl errno: " << strerror(errno) << std::endl;}Log() << "shmctl sucess!!! | " << "shmid: " << shmid << std::endl;return 0;
}

在这里插入图片描述

  • 还可以使用指令进行删除 – ipcrm -m shmid
[lyh@localhost lesson4(共享内存)]$ make
g++ -o IpcShmSer IpcShmSer.cxx -std=c++11 -g[lyh@localhost lesson4(共享内存)]$ ./IpcShmSer 
Fot Debug | timestamp: 1671797310 | key: 302207089
Fot Debug | timestamp: 1671797310 | shmget sucess!!! | shmid: 30[lyh@localhost lesson4(共享内存)]$ ipcs -m------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 9          lyh        777        16384      1          dest         
0x00000000 10         lyh        777        6193152    2          dest         
0x00000000 16         lyh        600        524288     2          dest         
0x00000000 17         lyh        777        6193152    2          dest         
0x00000000 20         lyh        600        524288     2          dest         
0x00000000 24         lyh        600        16777216   2          dest         
0x00000000 25         lyh        600        524288     2          dest         
0x00000000 26         lyh        777        2064384    2          dest         
0x00000000 27         lyh        600        524288     2          dest         
0x12035071 30         lyh        0          4096       0                       [lyh@localhost lesson4(共享内存)]$ ipcrm -m 30
[lyh@localhost lesson4(共享内存)]$ ipcs -m------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 9          lyh        777        16384      1          dest         
0x00000000 10         lyh        777        6193152    2          dest         
0x00000000 16         lyh        600        524288     2          dest         
0x00000000 17         lyh        777        6193152    2          dest         
0x00000000 20         lyh        600        524288     2          dest         
0x00000000 24         lyh        600        16777216   2          dest         
0x00000000 25         lyh        600        524288     2          dest         
0x00000000 26         lyh        777        2064384    2          dest         
0x00000000 27         lyh        600        524288     2          dest 

🍧2.3、shmat函数(挂接)

  • 共享内虽然在进程中被创建,但是不属于进程,它是由OS进行管理的

  • 我们想要使用共享内存,必须将共享内存与进程关联共享内存映射到当前进程的共享区)起来

shmat函数:

#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
函数解析:
  • 功能:将shmid标识的SystemV共享内存段附加到调用进程的地址空间
  • 返回值:成功时返回0;错误时返回-1,并设置errno以指示错误的原因。该返回值的使用与C语言的malloc一样!!!
参数shmid:
  • shmget函数创建共享内存返回的标识符
参数shmflg:
  • 它的两个可能取值是SHM_RND和SHM_RDONLY,一般为0,详细内容查man手册
参数shmaddr:
  • 该参数传递一个地址,表示我们需要将共享内存附加到进程地址空间的某一个位置
  • 如果shaddr为NULL,系统将选择一个合适的(未使用的)地址来附加段
  • shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址
  • shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
  • shmflg = SHM_RDONLY,表示连接操作用来只读共享内存

挂接共享内存:

comm.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>#define PATH_NAME "/home/lyh/Linux_Study"
#define PROJ_ID 0x12key_t CreateKey()
{key_t key = ftok(PATH_NAME, PROJ_ID);if (key < 0){std::cout <<  "ftok errno: " << strerror(errno) << std::endl;exit(1); // key值冲突,结束调用该函数的进程}return key;
}// 该函数返回一个ostream& 可充当左值进行使用
std::ostream& Log()
{return std::cout << "Fot Debug | " << "timestamp: " << (long long)time(nullptr)<< " | ";
}

IpcShmSer.cpp

#include "comm.hpp"
// 设置共享内存的大小
#define MIN_SIZE 4096
// 共享内存的状态(创建全新的,如果存在则设置errno,并且返回-1)
const int shmflags = IPC_CREAT | IPC_EXCL;
// 该文件创建共享内存
int main()
{// 1、获取key值(共享内存的唯一标识符)key_t key = CreateKey();Log() << "key: " << key << std::endl;// 2、创建共享内存 -- 0666是共享内存的权限,标识谁能使用sleep(5);int shmid = shmget(key, MIN_SIZE, shmflags | 0666);if (shmid < 0){Log() << "shmget errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmget sucess!!! | "<< "shmid: " << shmid << std::endl;// 3、挂接共享内存sleep(5);char *pIps = (char *)shmat(shmid, nullptr, 0);if (pIps < 0){Log() << "shmat errno: " << strerror(errno) << std::endl;return 3;}Log() << "shmat sucess!!! | "<< "shmid: " << shmid << std::endl;// 删除共享内存sleep(5);int RM = shmctl(shmid, IPC_RMID, nullptr);if (RM < 0){Log() << "shmctl errno: " << strerror(errno) << std::endl;return 4;}Log() << "shmctl sucess!!! | "<< "shmid: " << shmid << std::endl;return 0;
}

在这里插入图片描述


🍨2.4、shmdt(去挂接)

#include <sys/types.h>
#include <sys/shm.h>int shmdt(const void *shmaddr);
函数解析:
  • 功能:将共享内存段与当前进程脱离(去除共享内存与进程地址空间的映射)
  • 返回值:成功时shmdt()返回0;错误时返回-1,并设置errno以指示错误的原因
参数shmaddr:
  • shmaddr:由shmat所返回的指针,与C语言中使用free释放堆空间类似

IpcShmSer.cpp

#include "comm.hpp"
// 设置共享内存的大小
#define MIN_SIZE 4096
// 共享内存的状态(创建全新的,如果存在则设置errno,并且返回-1)
const int shmflags = IPC_CREAT | IPC_EXCL;int main()
{// 1、获取key值(共享内存的唯一标识符)key_t key = CreateKey();Log() << "key: " << key << std::endl;// 2、创建共享内存 -- 0666是用户访问共享内存的权限,标识谁能读、写、执行sleep(5);int shmid = shmget(key, MIN_SIZE, shmflags | 0666);if (shmid < 0){Log() << "shmget errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmget sucess!!! | " << "shmid: " << shmid << std::endl;// 3、挂接共享内存sleep(5);char *pIps = (char *)shmat(shmid, nullptr, 0);if (pIps < 0){Log() << "shmat errno: " << strerror(errno) << std::endl;return 3;}Log() << "shmat sucess!!! | " << "shmid: " << shmid << std::endl;// 4、使用共享内存// 5、去挂接sleep(5);int flag = shmdt(pIps);if (flag < 0){Log() << "shmdt errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmdt sucess!!! | " << "shmid: " << shmid << std::endl;// 5、删除共享内存sleep(5);int RM = shmctl(shmid, IPC_RMID, nullptr);if (RM < 0){Log() << "shmctl errno: " << strerror(errno) << std::endl;return 4;}Log() << "shmctl sucess!!! | " << "shmid: " << shmid << std::endl;return 0;
}

在这里插入图片描述

🍀3、共享内存的使用

🍡3.1、测试

  • 我们上面所讲的内容,只完成了共享内存的创建、挂接、去挂接和删除共享内存

  • 共享内存实际上是映射进程地址空间的用户空间(堆、栈之间

  • 对于每个进程而言,挂接到自己上下文当中的共享内存,是属于自己的空间,可以被用户直接使用

  • 共享内存被映射到进程地址空间后类似于堆、栈空间,可以直接被使用

comm.hpp – 头文件和定义混编

  • 测试:服务器文件(IpcShmCli)写入数据,客户端文件(IpcShmSer)读取数据

  • 每次写入一个字节的数据,查看共享内存的读写顺序(是否存在同步与互斥

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>#define PATH_NAME "/home/lyh/Linux_Study"
#define PROJ_ID 0x12
#define MIN_SIZE 4096// 获取key值
key_t CreateKey()
{key_t key = ftok(PATH_NAME, PROJ_ID);if (key < 0){std::cout <<  "ftok errno: " << strerror(errno) << std::endl;exit(1); // key值冲突,结束调用该函数的进程}return key;
}// 该函数返回一个ostream& 可充当左值进行使用 -- 用于debug
std::ostream& Log()
{return std::cout << "Fot Debug | " << "timestamp: " << (long long)time(nullptr) << " | ";
}

IpcShmSer – 客户端创建共享内存并且读取共享内存的数据

  • 使用共享内存时,shmat跟malloc一样,返回的是引用共享内存块/堆块的虚拟地址

  • 我们可以对这些地址进行解引用写入数据

#include "comm.hpp"
// 共享内存的状态(创建全新的,如果存在则设置errno,并且返回-1)
const int shmflags = IPC_CREAT | IPC_EXCL;int main()
{// 1、获取key值key_t key = CreateKey();Log() << "key: " << key << std::endl;//--------------------------------------------------------------------------// 2、创建共享内存 -- 0666是用户访问共享内存的权限,标识谁能读、写、执行int shmid = shmget(key, MIN_SIZE, shmflags | 0666);if (shmid < 0){Log() << "shmget errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmget sucess!!! | " << "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 3、挂接char *pIps = (char *)shmat(shmid, nullptr, 0);if (pIps < 0){Log() << "shmat errno: " << strerror(errno) << std::endl;return 3;}Log() << "shmat sucess!!! | " << "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 4、使用共享内存 -- 读取数据int cnt = 0;while (true){sleep(2);std::cout << "Read data success: " << pIps << std::endl;if (cnt > CNT)break;++cnt;}//--------------------------------------------------------------------------// 5、去挂接int flag = shmdt(pIps);if (flag < 0){Log() << "shmdt errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmdt sucess!!! | " << "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 6、删除共享内存int RM = shmctl(shmid, IPC_RMID, nullptr);if (RM < 0){Log() << "shmctl errno: " << strerror(errno) << std::endl;return 4;}Log() << "shmctl sucess!!! | " << "shmid: " << shmid << std::endl;return 0;
}

IpcShmCli – 服务器写入数据

#include "comm.hpp"// 该文件使用共享内存
int main()
{// 1、获取key值key_t key = CreateKey();Log() << "key: " << key << std::endl;//--------------------------------------------------------------------------// 2、获取共享内存标idint shmid = shmget(key, MIN_SIZE, IPC_CREAT | 0666);if (shmid < 0){Log() << "shmget errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmget sucess!!! | " << "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 3、挂接char *pIps = (char *)shmat(shmid, nullptr, 0);if (pIps < 0){Log() << "shmat errno: " << strerror(errno) << std::endl;return 3;}Log() << "shmat sucess!!! | " << "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 4、使用共享内存 -- 写入数据int cnt = 0;while (cnt <= CNT){pIps[cnt] = 'A' + cnt;++cnt;pIps[cnt] = '\0';sleep(1);}//--------------------------------------------------------------------------// 5、去挂接int flag = shmdt(pIps);if (flag < 0){Log() << "shmdt errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmdt sucess!!! | " << "shmid: " << shmid << std::endl;return 0;
}

每次读取等待两秒,每次写入等待一秒,A->ABC->ABCDE…

在这里插入图片描述


🍢3.2、共享内存与管道的区别

通过上面的测试,我们知道:

  • 共享内存,因为它自身的特性,没有任何访问控制(同步与互斥)

  • 共享内存可以直接被二个或多个进程看到,属于二个或多个进程的用户空间,可以直接通信

  • 可以直接通信意味着:多个进程可以各自随便写入和读取不安全,没有访问控制

  • 共享内存,是所有IPC中,速度最快的

管道与共享内存中进程通信所拷贝数据次数的对比

在这里插入图片描述


🍧3.3、基于共享内存+管道实现访问控制

前言:

  • 虽然共享内存不能同步和互斥,但是我们可以通过管道的特性(访问控制)来间接的让共享内存获得访问控制这一特性!

  • 首先,前面的代码不变,我们还需增加命名管道的代码

  • 注意:命名管道只是辅助共享内存,主要是共享内存进行通信!!!

comm.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string>
#include <cstring>
#include <cerrno>#define PATH_NAME "/home/lyh/Linux_Study"
#define PROJ_ID 0x12
#define MIN_SIZE 4096
const int CNT = 5;
// 获取key值
key_t CreateKey()
{key_t key = ftok(PATH_NAME, PROJ_ID);if (key < 0){std::cout <<  "ftok errno: " << strerror(errno) << std::endl;exit(1); // key值冲突,结束调用该函数的进程}return key;
}// 该函数返回一个ostream& 可充当左值进行使用 -- 用于debug
std::ostream& Log()
{return std::cout << "Fot Debug | " << "timestamp: " << (long long)time(nullptr) << " | ";
}// 创建命名管道
void CreateName_Fifo()
{umask(0);// 在当前进程cwd下创建名为fifo的管道文件if (mkfifo("./fifo", 0666) == -1){Log() << "fifo errno: " << strerror(errno) << std::endl;exit(1);}Log() << "fifo success!!!" << std::endl;
}// 向命名管道写入任务码
void Write_Data()
{int fifofd = open("./fifo", O_WRONLY);if (fifofd == -1){Log() << "fifofd errno: " << strerror(errno) << std::endl;exit(1);}// 写入任务码为:"1"的数据 -- 随便写,命名管道是辅助共享内存uint32_t taskcode = 1;write(fifofd, &taskcode, sizeof(uint32_t));
}// 读取命名管道的任务码
ssize_t Read_Data()
{int fifofd = open("./fifo", O_RDONLY);if (fifofd == -1){Log() << "fifofd errno: " << strerror(errno) << std::endl;exit(1);}uint32_t taskcode = 0;ssize_t s = read(fifofd, &taskcode, sizeof(uint32_t));return s;
}

IpcShmSer – 增加命名管道访问控制代码(读端)

#include "comm.hpp"
// 共享内存的状态(创建全新的,如果存在则设置errno,并且返回-1)
const int shmflags = IPC_CREAT | IPC_EXCL;int main()
{// 打开命名管道CreateName_Fifo();// 1、获取key值key_t key = CreateKey();Log() << "key: " << key << std::endl;//--------------------------------------------------------------------------// 2、创建共享内存 -- 0666是用户访问共享内存的权限,标识谁能读、写、执行int shmid = shmget(key, MIN_SIZE, shmflags | 0666);if (shmid < 0){Log() << "shmget errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmget sucess!!! | "<< "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 3、挂接char *pIps = (char *)shmat(shmid, nullptr, 0);if (pIps < 0){Log() << "shmat errno: " << strerror(errno) << std::endl;return 3;}Log() << "shmat sucess!!! | "<< "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 4、使用共享内存 -- 读取数据int cnt = 0;while (cnt <= CNT){// 调用命名管道,进行访问控制ssize_t s = Read_Data();// 二个进程中的读端关闭,则退出循环if (s == 0)break;else if (s > 0){std::printf("read success: %s\n", pIps);}++cnt;}//--------------------------------------------------------------------------// 5、去挂接int flag = shmdt(pIps);if (flag < 0){Log() << "shmdt errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmdt sucess!!! | "<< "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 6、删除共享内存int RM = shmctl(shmid, IPC_RMID, nullptr);if (RM < 0){Log() << "shmctl errno: " << strerror(errno) << std::endl;return 4;}Log() << "shmctl sucess!!! | "<< "shmid: " << shmid << std::endl;// 7、删除管道文件unlink("./fifo");return 0;
}

IpcShmCli – 增加命名管道访问控制代码(写端)

#include "comm.hpp"// 该文件使用共享内存
int main()
{// 1、获取key值key_t key = CreateKey();Log() << "key: " << key << std::endl;//--------------------------------------------------------------------------// 2、获取共享内存标idint shmid = shmget(key, MIN_SIZE, IPC_CREAT | 0666);if (shmid < 0){Log() << "shmget errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmget sucess!!! | "<< "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 3、挂接char *pIps = (char *)shmat(shmid, nullptr, 0);if (pIps < 0){Log() << "shmat errno: " << strerror(errno) << std::endl;return 3;}Log() << "shmat sucess!!! | "<< "shmid: " << shmid << std::endl;//--------------------------------------------------------------------------// 4、使用共享内存 -- 写入数据int cnt = 0;while (cnt <= CNT){std::string str;std::cout << "请输入需要写入的数据: ";std::fflush(stdout);std::getline(std::cin, str);// 调用命名管道,进行访问控制Write_Data();strcpy(pIps, str.c_str());++cnt;}//--------------------------------------------------------------------------// 5、去挂接int flag = shmdt(pIps);if (flag < 0){Log() << "shmdt errno: " << strerror(errno) << std::endl;return 2;}Log() << "shmdt sucess!!! | "<< "shmid: " << shmid << std::endl;return 0;
}

---

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【自然语言处理】【ChatGPT系列】ChatGPT的智能来自哪里?

相关博客 【自然语言处理】【ChatGPT系列】ChatGPT的智能来自哪里&#xff1f; 【自然语言处理】【ChatGPT系列】Chain of Thought&#xff1a;从大模型中引导出推理能力 【自然语言处理】【ChatGPT系列】InstructGPT&#xff1a;遵循人类反馈指令来训练语言模型 【自然语言处理…

基于HTML5 技术的开放自动化HMI

人机交互接口&#xff08;HMI&#xff09;是自动化系统中不可或缺的一部分。传统的做法是提供一个HMI 显示屏&#xff0c;并且通过组态软件来配置显示屏的功能&#xff0c;通过modbus 或者以太网与PLC 连接。 现在&#xff0c;事情变得复杂了许多&#xff0c;用户不仅需要通过专…

linux共享内存的使用

共享内存可以由多个程序同时访问的内存&#xff0c;能够避免进程间通信过程中的冗余数据拷贝&#xff0c;是IPC中最快的一种,特别适合用来作大块数据的传输。共享内存可以映射到不同的进程空间&#xff0c;这些进程间的数据传递就不再涉及内核。这个过程其实是把同一块物理内存…

FMOC-PEG-COOH,FMOC-PEG-acid,芴甲氧羰基-聚乙二醇-羧基试剂供应

英文名称&#xff1a;FMOC-PEG-COOH&#xff0c;FMOC-PEG-acid 中文名称&#xff1a;芴甲氧羰基-聚乙二醇-羧基 蛋白质、肽和其他材料通过氨基酸或其他酸活性化学组&#xff0c;增加溶解度和稳定性&#xff0c;降低免疫原性&#xff1b;药物修饰或缓释药物研发&#xff0c;新…

ARM_SMMU_下

SMMU驱动代码分析 本文主要分析linux kernel中SMMUv3的代码(drivers/iommu/arm-smmu-v3.c) linux kernel版本是linux 5.7, 体系结构是aarch64 SMMU的作用是把CPU提交给设备的VA地址&#xff0c;直接作为设备发出的地址&#xff0c;变成正确的物理地址&#xff0c;访问到物理内…

[~/vulhub]/log4j/CVE-2021-44228-20221225

[~/vulhub]/log4j/CVE-2021-44228 ┌──(kwkl㉿kwkl)-[~/vulhub] └─$ cd log4j/CVE-2021-44228 ┌──(kwkl㉿kwkl)-[~/vulhub/log4j/CVE-2021-44228] └─$ dir 1.png 2.png docker-compose.yml README.md README.zh-cn.md┌──(kwkl㉿kwkl)-[~/vulhub/log4j/CVE-2021…

_15LeetCode代码随想录算法训练营第十五天-C++二叉树

_15LeetCode代码随想录算法训练营第十五天-C二叉树 题目列表 110.平衡二叉树257.二叉树的所有路径404.左叶子之和 110.平衡二叉树 题目 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每…

SpringBoot-2 读取properties;自动加载127个类原理总结;全部加载,按需配置

读取properties 方式一&#xff1a;非配置类填写&#xff1a;ComponentConfigurationProperties 1)建立bean&#xff1a; /只有在容器中的组件才拥有springboot提供的强大功能 Component ConfigurationProperties(prefix "mycar") public class Car {private Stri…

NetSuite Decode函数

昨天是平安夜&#xff0c;小家伙仍然为圣诞老人的到来准备了礼物&#xff0c;这是他的传统。每年为了感谢圣诞老人和驯鹿的到来&#xff0c;他都会准备上点心、水果。今年&#xff0c;他认为驯鹿可能需要电力&#xff0c;所以准备了电池给它们享用。 真希望天真一直伴随他的成长…

互联网技术不再是统领,当下正在发生着一场深刻的变革

拥抱实体经济&#xff0c;绝对是当下互联网玩家们的首要选择。无论是头部的互联网企业来讲&#xff0c;还是新生的互联网玩家而言&#xff0c;它们都不约而同地将关注的焦点聚焦在了这样一个方向上。   透过这一点&#xff0c;我们可以非常明显地感受到&#xff0c;一个全新的…

蓝桥杯备赛Day4——多维数组

二维数组初始化 p[[0 for i in range(5)] for j in range(2)] #法一 p[[0]*5 for j in range(2)] #法二 s[[1,2,3],[4,5,6]] print(s) for i in range(2):for j in range(3):print(s[i][j],end ) 三维数组初始化 a[[[0 for _ in range(2)] for __ in…

CSDN每日一练最长递增的区间长度 C语言

题目名称&#xff1a;最长递增的区间长度 时间限制&#xff1a;1000ms 内存限制&#xff1a;256M 题目描述 给一个无序数组&#xff0c;求最长递增的区间长度。如&#xff1a;[5,2,3,8,1,9] 最长区间 2,3,8 长度为 3 &#xff08;注意&#xff1a;测试用例仅做参考&#xff0c;…

windows@网络防火墙@软件联网控制

文章目录ref打开防火墙控制面板常用部分限制某个软件联网文档参考具体操作取消控制/禁用第三方软件控制ref (Windows) 创建出站端口规则 | Microsoft LearnWindows Defender Firewall with Advanced Security (Windows) | Microsoft Learn组策略 Windows) 高级安全性的 Window…

二叉树经典算法题目

1.二叉树的前中后序遍历&#xff08;简单&#xff09; 省略 2.二叉树的深度(简单) 输入一棵二叉树的根节点&#xff0c;求该树的深度。从根节点到叶节点依次经过的节点&#xff08;含根、叶节点&#xff09;形成树的一条路径&#xff0c;最长路径的长度为树的深度。 例如&a…

VS coda C++、python运行与Dbug配置

首先新建终端 一次性使用C方法 检查C编译器是否存在 which g可见位置存在于&#xff1a;/usr/bin/g 一次性命令格式&#xff1a; 使用json配置文件 运行C方法&#xff08;推荐&#xff09;&#xff1a; 根据你查找的g的位置来决定 使用配置好的tasks.json&#xff08;C的…

Java window多环境配置

目录JDK版本下载配置内容描述创建JAVA_HOME在Path配置版本切换效果JDK版本下载 Java8 Download address 这个是Java8 的下载地址&#xff0c;下载是要登录的&#xff0c;自己花费一点时间去注册。如果想要下载其它版本的JDK&#xff0c;请看下面的图&#xff0c;然后你就可以看…

SSD_学习笔记记录

one-steage VGG16模型的修改 VGG16的模型输出&#xff0c;其中224.。。为特征图的大小。 SSD模型中对VGG网络的修改&#xff1a; SSD模型是对VGG中的第四个卷积块中的最后一个Conv2d进行第一个输出&#xff0c;在这里简称为Conv4-3。以及第五个卷积块中的最后一个Conv2d进行…

前端开发:Vue封装通过API调用的组件的方法

前言 在前端开发中&#xff0c;关于Vue的使用相比大家并不陌生&#xff0c;而且Vue框架的优势也是其他框架所不能比的&#xff0c;尤其是Vue的封装思想更是堪称一绝&#xff0c;还有就是组件化的运用实践过程也是亮点。所以关于Vue框架的使用想必看官都不陌生&#xff0c;而且常…

干货 | 关于PCB中的“平衡铜”,一文全部说明白

平衡铜是PCB设计的一个重要环节&#xff0c;对PCB上闲置的空间用铜箔进行填充&#xff0c;一般将其设置为地平面。 平衡铜的意义在于&#xff1a; 对信号来说&#xff0c;提供更好的返回路径&#xff0c;提高抗干扰能力&#xff1b;对电源来说&#xff0c;降低阻抗&#xff0c;…

8 NP完全性理论

8 NP完全性理论 p问题 NP问题 NP完全问题 NPC(complete ) NP难问题NP-hard p问题 是一类能够用**(确定的)算法**在多项式时间内求解的可判定问题 ●这种问题类型也称为多项式类型 NP问题 是一类能够用不确定算法在多项式时间内求解的可判定问题 在确定性计算模型下多项式时…