✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉zmh–CSDN博客
✨ 文章所属专栏:c++篇–CSDN博客
文章目录
- 一.System V共享内存(重点)
- 1.基本概念和原理
- 2.系统调用函数
- shmget函数(创建/获取共享内存)
- ftok函数(生成键值key)
- 创建测试
- shmat函数(挂接共享内存)
- shmdt函数(去关联共享内存)
- shmctl函数(控制共享内存,可以用来删除)
- 挂接,去关联与删除测试
- 3.进程间通信测试
- 4.共享内存的特性
- 5.共享内存的内核数据结构
- 二.System V消息队列(了解即可)
- 三.System V信号量(线程做铺垫)
- 四.IPC资源的内核数据结构
一.System V共享内存(重点)
1.基本概念和原理
共享内存是一种进程间通信(IPC)机制,允许多个进程访问同一块物理内存区域。它是所有IPC方式中速度最快的,因为数据无需在进程间复制。
底层原理:
1.虚拟内存映射机制:
进程A的虚拟地址空间 物理内存 进程B的虚拟地址空间
+---------------+ +-------+ +---------------+
| | | | | |
+---------------+ +-------+ +---------------+
| 0x8000-0x8FFF | --> | 数据 | <-- | 0xB000-0xBFFF |
+---------------+ +-------+ +---------------+
| | | | | |
+---------------+ +-------+ +---------------+
- 每个进程都有自己的虚拟地址空间
- 共享内存通过将不同进程的虚拟地址映射到相同的物理内存页来实现
- 进程可能在不同的虚拟地址上访问相同的物理内存
2.页表映射:
进程A页表 进程B页表
+-------------------+ +-------------------+
| 虚拟页 | 物理页帧 | | 虚拟页 | 物理页帧 |
+-------+---------+ +-------+---------+
| ... | ... | | ... | ... |
| 0x8 | 0x1A | -----> | 0xB | 0x1A |
| ... | ... | | ... | ... |
+-------------------+ +-------------------+
- 每个进程除了有自己的虚拟地址空间,还有一个页表
- 在物理内存上申请一块空间后,通过页表建立虚拟地址到物理地址的映射
- 不同进程的共享内存在虚拟页不同,但通过页表会映射到相同的物理页上
通过共享内存进行通信的进程都会有一个共享内存,如果系统中存在大量的共享内存,系统就要对所有的共享内存进行管理,如何管理?先描述再组织!也就是说每个共享内存都会有一个描述该共享内存的结构体对象,然后系统会对这些结构体对象做管理。
此外上面所有的都不是直接由进程来做,而是由操作系统来完成;进程表示的是用户,当用户和系统进行交互,只能通过系统调用接口来实现。
2.系统调用函数
shmget函数(创建/获取共享内存)
#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
参数:
-
key
:共享内存的键值,由ftok函数生成(后面讲) -
size
:共享内存的大小(字节为单位);创建时必须指定;获取已存在的共享内存时可以为0;一般建议共享内存的大小设置为4096字节的倍数 -
shmflg
:IPC_CREAT
:如果共享内存不存在则创建;存在就获取并返回(可以单独使用)。IPC_CREAT|IPC_EXCL
:如果共享内存不存在则创建;存在就出错放回;确保共享内存是新创建的(IPC_EXCL
不可以单独使用)。
返回值:
- 成功返回共享内存的标识符(
shmid
;类似于文件描述符) - 失败返回-1,并设置
errno
ftok函数(生成键值key)
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
功能:
- 输入文件路径和项目ID,根据相应的算法生成对应的键值key返回。key是一个数字,具有唯一性(因为文件路径具有唯一性)
参数:
pathname
:必须是一个存在且可访问的文件,通常选择稳定的系统文件或应用配置文件。proj_id
:项目ID,通常用字符,如’A’;只使用低8位,范围1-255
返回值:
- 成功返回生成的键值key
- 失败返回-1,并设置
errno
(文件路径必须存在,否则失败)
键值key和共享内存标识符shmid的作用:
键值(key)的作用
1.全局命名:键值是一个系统级别的标识符,用于在系统范围内唯一标识一个共享内存对象
2.进程间共识:不相关的进程可以通过约定相同的key来访问同一个共享内存
3.持久标识:key可以持久存在,即使系统重启,相同的ftok参数会生成相同的key
4.IPC资源定位:操作系统通过key在IPC表中查找相应的共享内存
标识符(shmid)的作用
1.内部引用:是系统内核为特定共享内存段分配的唯一标识符
2.句柄作用:进程获取shmid后,使用它进行后续的共享内存操作
3.临时性:shmid只在当前系统运行期间有效,系统重启后会改变
4.权限控制:系统通过shmid管理对共享内存的访问权限
当程序调用shmget()
时:
- 内核分配物理内存页
- 创建共享内存标识符
shmid
- 在内核的IPC资源表中记录此共享内存段信息
创建测试
对于使用共享内存通信的两个进程,并不需要两个进程都创建,只需要第一个创建后,第二个之后的进程直接获取共享内存标识符直接使用即可:
#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cstdlib>
#include "log.hpp"#define SIZE 1024const std::string pathname = "/home/zmh01";
const int proj_id = 0x6666;Log log;key_t GetKey(){// 生成共享内存的键值keykey_t k = ftok(pathname.c_str(), proj_id);if(k < 0){log(Fatal, "create key error: %s", strerror(errno));exit(1);}log(INFO, "create key success, key: %d", k);return k;
}int GetShareMemHelper(int flag){int shmid = shmget(GetKey(), SIZE, flag);if (shmid < 0){log(Fatal, "create share memory error: %s", strerror(errno));exit(2);}log(INFO, "create share memory success, shmid: %d", shmid);return shmid;
}// 创建共享内存接口
int CreateShm(){return GetShareMemHelper(IPC_CREAT | IPC_EXCL);
}// 获取共享内存接口
int GetShm(){return GetShareMemHelper(IPC_CREAT);
}#endif
第一个进程调用CreateShm
函数创建共享内存,第二个之后的进程调用GetShm
函数就可以直接使用已经创建好的共享内存
先用进程a调用CreatemShm
函数创建共享内存进行测试:
prcoessa.cc
文件:
#include "comm.hpp"
#include "log.hpp"using namespace std;int main(){sleep(3);int shmid = CreateShm();sleep(5);log(INFO, "processa quit!");return 0;
}
共享内存的生命周期:
共享内存的生命周期随内核,不会随进程的生命周期结束而结束:
需要手动删除(后面会讲解使用函数调用删除):
#查看共享内存资源
ipcs -m#删除指定的共享内存
ipcrm -m <shmid>
共享内存的权限:
共享内存也是需要权限的,设置权限需要在创建共享内存时设置:
int GetShareMemHelper(int flag){int shmid = shmget(GetKey(), SIZE, flag);if (shmid < 0){log(Fatal, "create share memory error: %s", strerror(errno));exit(2);}log(INFO, "create share memory success, shmid: %d", shmid);return shmid;
}// 创建共享内存接口
int CreateShm(){return GetShareMemHelper(IPC_CREAT | IPC_EXCL | 0666);
}
加上对应的权限后,创建出来的共享内存就具备了权限:
shmat函数(挂接共享内存)
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
shmid
:共享内存标识符,由shmget函数返回shmaddr
:如果为nullptr
系统会自动选择合适的虚拟地址挂接;如果非nullptr
请求挂接到指定地址(通常不建议使用)shmflg
:控制标志,常用值:- 0:默认读写权限
SHM_RDONLY
:只读方式挂接SHM_REMAP
:强制覆盖已有映射
返回值:
- 成功返回共享内存被挂接的起始地址
- 失败返回(void)-1,并设置errno
当程序调用shmat
函数时:
- 系统修改进程的页表,建立虚拟地址到共享物理地址的映射
- 返回映射的起始地址(虚拟地址)给进程使用
- 共享内存的挂接数加一
shmdt函数(去关联共享内存)
int shmdt(const void *shmaddr);
参数:
shmaddr
:共享内存被挂接的起始地址(描述共享内存的结构体对象中包含了共享内存的大小,系统只需要知道起始地址再根据大小,就可以将整个共享内存去关联)
返回值:
- 成功返回0;失败返回-1,并设置errno
注意点:
- 去关联只是取消进程与共享内存的关联,不会删除共享内存
- 去关联后不能再通过原指针进行访问
当程序调用shmdt
函数时:
- 系统移除进程页表中对应的映射关系
- 进程无法再根据起始地址访问共享内存
- 共享内存的挂接数减一
shmctl函数(控制共享内存,可以用来删除)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
shmid
:共享内存标识符,shmget函数返回值cmd
:控制命令,删除时使用IPC_RMID
(标记共享内存删除)buf
:指向描述共享内存结构体的指针(删除时设置为nullptr
即可)
返回值:
- 成功返回0;失败返回-1,并设置errno
当程序调用shmctl(IPC_RMID
)时:
- 系统标记该共享内存段为待删除
- 当挂接该共享内存的所有进程都分离后,物理内存被释放
挂接,去关联与删除测试
两个文件表示不同的两个程序
processa.cc
文件:
#include "comm.hpp"
#include "log.hpp"using namespace std;int main(){// 创建int shmid = CreateShm();log(Debug, "creat shm done");sleep(1);// 挂起char *shmaddr = (char *)shmat(shmid, nullptr, 0);log(Debug, "shmat shm done");sleep(2);// 去关联shmdt(shmaddr);log(Debug, "shmdt shm done");sleep(2);// 释放shmctl(shmid, IPC_RMID, nullptr);log(Debug, "shmctl shm done");sleep(2);return 0;
}
processb.cc
文件:
#include "comm.hpp"
#include "log.hpp"using namespace std;int main(){// 获取int shmid = GetShm();log(Debug, "creat shm done");sleep(1);// 挂起char *shmaddr = (char *)shmat(shmid, nullptr, 0);log(Debug, "shmat shm done");sleep(2);// 去关联shmdt(shmaddr);log(Debug, "shmdt shm done");sleep(2);return 0;
}
程序a和程序b启动后,程序a先创建共享内存此时挂接数(nattch
)为0,之后从0到1再到2表示两个程序调用shmat
函数挂接共享内存;然后挂接数从2变成1再到0表示两个程序调用shmdt
函数去关联共享内存;最后由程序a调用shmctl
函数删除共享内存。
3.进程间通信测试
processa.cc
文件:
进程a用来读取共享内存中写入的数据
#include "comm.hpp"
#include "log.hpp"using namespace std;int main(){// 创建int shmid = CreateShm();// 挂起char *shmaddr = (char *)shmat(shmid, nullptr, 0);// IPC codewhile(true){cout << "client say@" << shmaddr << endl;sleep(1);}// 去关联shmdt(shmaddr);// 释放shmctl(shmid, IPC_RMID, nullptr);return 0;
}
processb.cc
文件:
进程b用来向共享内存中写入数据
#include "comm.hpp"
#include "log.hpp"using namespace std;int main(){// 获取int shmid = GetShm();// 挂起char *shmaddr = (char *)shmat(shmid, nullptr, 0);//IPC codewhile(true){cout << "Please Enter@";// 不需要借助缓冲区,直接写入到共享内存中即可fgets(shmaddr, SHMSIZE, stdin);}// 去关联shmdt(shmaddr);return 0;
}
进程b写入时,自定义每次从共享内存的起始位置开始写入(写入位置可以自己设置),一旦进程b把数据写入到共享内存后,进程a就会立即看到共享内存中的数据,就可以直接读取,不需要再调用系统接口来读取。
此外进程a每次读取并不会关心另一个进程b是否写入新的数据,而是一直按照自己的读取方式来读取(也就是说没有同步和互斥机制)。
4.共享内存的特性
1.共享内存没有同步和互斥的保护机制
对于共享内存,系统并没有设置对应的同步和互斥保护机制,需要用户自己设计同步和互斥机制。
2.共享内存具有高性能特性,是所有进程间通信中,速度最快的
共享内存是零拷贝机制,数据无需在进程间复制,直接访问同一物理内存即可;一旦建立映射,访问速度接近于常规内存操作;通常适合大量数据交换,性能不会随数据量增加而显著降低;同时可以减轻CPU负载,减少了数据搬运和上下文切换的开销。
3.共享内存内部的数据,由用户自己来维护
进程读取和写入的位置可以由用户自己来设置,根据起始位置和大小来进行维护
5.共享内存的内核数据结构
前面提到过系统要对每个共享内存进行管理,所以内核中存在描述共享内存的内核数据结构,也就是strcut shmid_ds
:
struct shmid_ds {struct ipc_perm shm_perm; // 权限信息size_t shm_segsz; // 共享内存段大小(字节)time_t shm_atime; // 最后附加(attach)时间time_t shm_dtime; // 最后分离(detach)时间time_t shm_ctime; // 最后修改时间(如权限更改)pid_t shm_cpid; // 创建者 PIDpid_t shm_lpid; // 最后操作(attach/detach)的 PIDshmatt_t shm_nattch; // 当前附加的进程数// ... 其他内核内部字段
};
其中第一个字段包含了另一个内核数据结构strcut ipc_perm
:
struct ipc_perm {key_t __key; // IPC 对象的键值(通过 ftok 生成)uid_t uid; // 所有者的用户 IDgid_t gid; // 所有者的组 IDuid_t cuid; // 创建者的用户 IDgid_t cgid; // 创建者的组 IDmode_t mode; // 访问权限(如 0666)// ... 其他内核内部字段
};
该数据结构中存放的则是共享内存的键值key以及各种权限信息。
通过指令ipcs -m
即可查看所有共享内存的属性信息。
也可以通过shmctl
函数来获取struct shmid_ds
结构体:
二.System V消息队列(了解即可)
System V消息队列是一种进程间通信(IPC)机制,允许不相关进程通过消息交换数据,基本原理如下:
1.消息队列是内核中的链表结构,这个内核数据结构一定是由系统创建的,和共享内存一样属于共享资源;既然由系统创建,用户和系统进行交互只能通过系统调用来实现,所以和共享内存一样,消息队列也有一批相关的系统调用函数。
2.不同的进程之间要想进行通信,需要先看到同一份资源,也就是要标识同一个消息队列,如何表示同一个?和共享内存一样,通过唯一的键值key标识,同样也是由ftok函数生成。
3.进行通信之前,进程需要使用msgget函数(等同于shmget函数)创建或获取队列,生成一个msgid返回(等同于shmid),后续所有的操作通过这个id来实现。
4.发送消息使用msgsnd函数,接收消息使用msgrcf函数;发送的数据会生成一个消息节点存放在队列中,接收消息时从队列中获取,如果每个进程发送的数据不进行区分直接存放到消息队列中,就会导致无法正确接收到目标信息。因此在每个消息节点中,除了包含数据部分,还有类型字段部分,用来表示当前消息的特定类型。接收消息时按消息类型选择性接受即可。
5.消息队列的生命周期也是随系统的,持久性的存在于内核中,直到显示删除或系统重启。通过msgctl函数可以控制消息队列的删除(等价于shmctl函数)。
系统也要对每个消息队列进行管理,所以也存在描述消息队列的内核数据结构对象struct msqid_ds
:
struct msqid_ds {struct ipc_perm msg_perm; // 权限信息struct msg *msg_first; // 队列中第一条消息的指针(内核维护)struct msg *msg_last; // 队列中最后一条消息的指针time_t msg_stime; // 最后发送消息的时间time_t msg_rtime; // 最后接收消息的时间time_t msg_ctime; // 最后修改时间(如权限更改)unsigned long msg_cbytes; // 当前队列中消息总字节数msgqnum_t msg_qnum; // 当前队列中的消息数量msglen_t msg_qbytes; // 队列最大允许字节数(由 msgmnb 限制)pid_t msg_lspid; // 最后发送消息的 PIDpid_t msg_lrpid; // 最后接收消息的 PID// ... 其他内核内部字段
};
该结构体中的第一个字段同样是strcut ipc_perm
。
消息队列和共享内存大致相同,因为都是属于System V通信标准中的IPC资源,包括之后的信号量,也是相同的机制。
三.System V信号量(线程做铺垫)
信号量也是System V
进程通信的一种范畴。这里只讲解相关概念和原理,为之后的线程学习做铺垫,相关的系统调用函数在之后的线程部分再讲解使用。
- 前置相关名词解释:
以共享内存为例,前面讲解过共享内存没有同步和互斥机制,也就是没有任何保护机制;假设当前进程a和进程b通过共享内存来通信,当进程a正在写入,写入了一半时,就被进程b读取拿走了,就会导致双方发和收的数据不完整,这就是数据不一致问题。
1.如果进程a和进程b看到同一份资源,也就是共享资源,如果不加以保护,就会导致数据不一致问题。
2.如何解决数据不一致问题?加锁—也就是互斥访问;什么是互斥访问?任何时刻,只允许一个执行流(一个进程)访问共享资源,这就是互斥。
3.对于共享资源,任何时刻只允许一个执行流访问(就是执行访问代码)的资源,叫做临界资源(一般是内存空间)。
4.举例:一个程序有100行代码,可能只有5到10行代码才在访问临界资源,而访问临界资源的代码,叫做临界区。
明白了上面的,就可以解释一个现象:
当多个进程往显示器文件写入内容时,会发生内容错乱,混乱,甚至和命令行混在一起,为什么会有这种现象?
这是因为往显示器文件写入时,需要先写入到显示器文件的文件缓冲区,多个进程往同一个文件缓冲区写入,相当于多个进程共享同一个文件缓冲区,而文件缓冲区并没有同步和互斥保护机制,就会造成数据不一致问题,如果想要解决这种情况,就要将显示器文件变成临界资源。
显示器文件也是一种共享资源!!!上面的现象是典型的数据不一致问题。
- 理解什么是信号量
结论:信号量本质上就是一个计数器,类似但不等于int cnt = n
,用来描述临界资源中资源数量的多少。
如何理解临界资源中资源数量的多少这句话?
先举一个例子来讲解:
假设现在有一个电影院,电影院其中一个放映厅有100个座位,也就是说这个放映厅最多可以卖出100张票。
当我们要去看电影的时候,还没去看电影,需要先买票,买完票后,在电影放映的时段,这个座位就是属于我们自己的,即使可能买完票没有去,这个座位也要空着,因为我们提前买过票了,在这个时间段,这个座位就是我们的。
而买票的本质就是对资源的预定机制。
除此之外,这个放映厅最多可以卖出100张票,一旦这100张全部买外,之后其他人在想买票就没有空余的座位了。
我们可以认为存在一个票数计数器,每卖出一张票,计数器就要减一,表示放映厅中的资源减少一个!
当票数的计数器减到0后,就没有空余的座位,表示放映厅中的资源已经申请完毕了!
通过上面这个电影院的例子再来理解临界资源中的资源数量这个概念:
假如一整块临界资源可以分为多个小块,当进程a只访问其中一块资源时,就可以把这一小块资源给进程a;而当进程b访问另一块资源时,因为和进程a访问的不是同一块,也可以把进程b要访问的这一块资源给他。系统没必要把整个临界资源锁住,只要把各自要访问的不同资源给他们即可。
这种情况下就可以允许多个执行流(进程)同时访问临界资源,提高多进程执行流访问临界资源的并发度,在一定程度上提高效率。
但是上面这种情况最怕的就是两种情况:
1.多个执行流访问同一个资源;
2.整个临界资源分成n个资源,但是有n+1个执行流访问,同样也会导致第一种情况。
如何解决?通过一个引用计数器来解决:
假设整个临界资源分成了n块资源,所以引用计数就是int cnt = n
,每当有执行流访问时,就要申请计数器资源,引用计数减一;一旦引用计数cnt< = 0
表示整个资源被申请完了,再有执行流申请,就不给了。
1.申请计数器成功,引用计数减一,就表示当前执行流具有访问资源的权限了。
2.申请了计数器资源,并没有立即访问申请的资源,就和买票一样,申请了计数器资源是对资源的预定机制。
3.计数器可以有效保证进入共享资源的执行流数量。
4.每一个执行流想访问共享资源中的一部分的时候,并不是直接访问,而是先申请计数器资源,就比如看电影先买票。
上面一直在讲的计数器资源,就叫做信号量。
还是以上面电影院的例子来讲解:
如果现在电影院放映厅中只有一个座位,我们就只需要一个值为1的计数器。对于这一个座位,只有一个人能抢到,只有一个人能进放映厅看电影,表示看电影期间只有一个执行流在访问临界资源,这种情况就是上面提到的互斥。
把值只能为0和1的两态计数器叫做二元信号量—本质上就是一个锁!!!
计数器为1,资源为1的本质—其实就是将临界资源不要分成很多块了,而是当作一个整体使用。整体申请,整体释放,每次最多只有一个执行流在使用。
- 信号量也是一种共享资源
当执行流要访问临界资源时,先要申请信号量计数器资源,这个信号量计数器资源,也是一种共享资源!
如果存在多个执行流,每个执行流都要知道当前的信号量计数器资源还有多少,比如执行流a申请了一个计数器资源,计数器减一,减一之后,需要同步的让其他执行流知道当前的计数器资源还剩多少。否则就会造成多个执行流访问同一个资源的情况。
而计数器资源存在的目的,就是为了保证其他共享资源的安全问题,而要保护别人的安全,首先需要保证自己的安全。
对于计数器资源减一的操作cnt--
,并不是安全的!
在C语言上cnt--
就是一条语句;
而变成汇编语句后,就会变成多条汇编语句(一般是3条):
a.将cnt
变量的内容从内存拿到CPU寄存器中
b.CPU内进行cnt--
操作
c.将计算后的结果写回cnt
变量的内存位置
而进程在运行的时候,是可以随时被切换的,一旦当前进程正在执行这三条指令时被切换,被其他进程打断,就会导致cnt
变量的错乱,因此对于计数器资源减一的操作,并不是安全的
(关于计数器资源不安全的问题,到之后的线程部分再具体讲解,这里只需要了解即可)
既然信号量是一种共享资源,又是如何保证自身的安全的?
信号量有两种操作:
1.申请信号量,本质是对计数器资源的减一操作—这个操作叫做P
操作;
2.释放资源,释放信号量,本质是对计数器资源的加一操作—这个操作叫做V
操作
申请和释放信号量的操作叫做PV
操作----具有原子性
什么是原子性?
要么不做,要么就做完—是两态的;没有“正在做”的概念!
原子性放到汇编中,就是只有一条汇编语句,所以CPU执行该汇编语句时要么不做,要么就做完,当前进程不会正在做时被切换,被其他进程打断。
- 信号量为什么也是进程通信的一种
对于进程通信,不仅仅是对数据之间的相互传输,相互协同也是通信的范畴。
要相互协同,本质上也是通信,信号量首先需要被所有的通信进程看到,也是一种共享资源。
- 总结
1.信号量本质上是一把计数器,申请和释放信号量的操作叫做
PV
操作—具有原子性2.执行流申请临界资源,必须先申请信号量资源,得到信号量之后,才能访问临界资源
3.信号量值只有0和1两态的,叫做二元信号量,本质上就是一个锁,具有互斥功能
4.申请信号量资源的本质:是对临界资源的预定机制
四.IPC资源的内核数据结构
在 System V IPC(进程间通信)机制中,struct shmid_ds
(共享内存)、struct msqid_ds
(消息队列)和 struct semid_ds
(信号量)是内核用于管理不同 IPC 资源的内核数据结构。尽管它们服务于不同的 IPC 机制,但它们的核心设计遵循统一的模式,包含相似的字段,同时针对各自的功能扩展了特定成员。
三个IPC资源的内核数据结构的第一个字段都包含了一个struct ipc_perm
结构体,这个结构体中存放的都是用于管理IPC对象的权限和所有者信息。
系统要对所有的IPC资源对象做管理,就要借助一个stcut ipc_perm *array[]
数组,用于管理和跟踪所有的IPC对象(包括共享内存,消息队列和信号量)。
每一个IPC资源将结构体对象中第一个字段struct ipc_perm
结构体指针存放到数组中,存放的位置就是xxxid
(每个IPC资源的标识符)。
每个IPC资源通过标识符就可以到数组对应的位置中,找到struct ipc_perm
指针,根据这个指针就可以找到第一个字段struct ipc_perm
结构体对象,当需要访问IPC资源结构体对象stcut xxx_ds
中的其他资源时,只需要将struct ipc_perm*
强制转换成strcut xxx_ds*
即可(系统可以区分指针指向的类型,就可以转换成目标类型)。
这种数据结构设计就是C++中的多态,其中stcut ipc_perm
是基类,struct xxx_ds
是子类。
以上就是关于System V中三种IPC资源的讲解,重点是共享内存,对于消息队列和信号量了解即可;如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!