目录
🌈前言
这篇文章给大家带来进程间通信中共享内存的学习!!!
🌸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 and
SHM_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 0x12
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& 可充当左值进行使用
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>