【Linux系统篇】:System V IPC核心技术解析---从共享内存到消息队列与信号量
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨
✨ 个人主页:余辉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资源的讲解,重点是共享内存,对于消息队列和信号量了解即可;如果哪里有错的话,可以在评论区指正,也欢迎大家一起讨论学习,如果对你的学习有帮助的话,点点赞关注支持一下吧!!!
相关文章:
【Linux系统篇】:System V IPC核心技术解析---从共享内存到消息队列与信号量
✨感谢您阅读本篇文章,文章内容是个人学习笔记的整理,如果哪里有误的话还请您指正噢✨ ✨ 个人主页:余辉zmh–CSDN博客 ✨ 文章所属专栏:c篇–CSDN博客 文章目录 一.System V共享内存(重点)1.基本概念和原理…...
关于GPU的涡轮散热与被动散热
显卡涡轮散热与被动散热的深度解析 一、涡轮散热的定义与工作原理 涡轮散热技术是通过高速旋转的涡轮风扇配合封闭式风道设计,将冷空气吸入并强制排出热量的主动散热方案。其核心原理包含以下关键点: 气流动力学设计:涡轮风扇采用精密叶片(如离心式结构),在相同尺寸下能…...
namesapce、cgroup
dd: 制作磁盘镜像:借助 dd 指令能够把整个磁盘或者分区的数据复制到一个文件里,形成磁盘镜像文件。此镜像文件可用于备份数据或者在其他系统中恢复磁盘。 恢复磁盘镜像:可以把之前创建的磁盘镜像文件恢复到磁盘或者分区 磁盘初始…...
C++23 新特性:行拼接前去除空白符 (P2223R2)
文章目录 1\. 什么是行拼接前去除空白符2\. 为什么需要这一特性3\. 示例代码输出结果 4\. 编译器支持5\. 优势与应用场景5.1 提高代码可读性5.2 减少潜在错误5.3 适用于多行字符串 6\. 其他相关特性7\. 总结 C 语言一直在不断进化,以满足现代软件开发的需求。C23 标…...
算法思想之链表
欢迎拜访:雾里看山-CSDN博客 本篇主题:算法思想之链表 发布时间:2025.4.18 隶属专栏:算法 目录 算法介绍常用技巧 例题两数相加题目链接题目描述算法思路代码实现 两两交换链表中的节点题目链接题目描述算法思路代码实现 重排链表…...
《软件设计师》复习笔记(11.5)——测试原则、阶段、测试用例设计、调试
目录 1. 测试基础概念 2. 测试方法分类 3. 测试阶段 真题示例: 题目1 题目2 题目3 4. 测试策略 5. 测试用例设计 真题示例: 6. 调试与度量 真题示例: 1. 测试基础概念 定义:系统测试是为发现错误而执行程序的过程&…...
工厂方法模式详解及在自动驾驶场景代码示例(c++代码实现)
模式定义 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,通过定义抽象工厂接口将对象创建过程延迟到子类实现,实现对象创建与使用的解耦。该模式特别适合需要动态扩展产品类型的场景。 自动驾驶感知场景分析 自动驾…...
Java 2025:解锁未来5大技术趋势,Kotlin融合AI新篇
各位Java开发者们好!🚀 2025年的Java世界正在经历一场前所未有的技术变革。作为深耕Java领域多年的技术博主,今天我将带大家深入探索Java生态即将迎来的5大技术趋势,特别是Kotlin的深度融合和AI技术的新篇章。准备好了吗ÿ…...
抗辐照设计优化:商业航天高可靠系统设计的关键路径
随着商业航天领域的快速发展,航天器的可靠性和抗辐照能力已成为系统设计的核心需求。在严苛的太空辐射环境中,电子设备面临着单粒子效应、总剂量效应和位移损伤效应等多重挑战。抗辐照设计优化不仅是确保航天器任务成功的关键路径,更是推动商…...
颚式破碎机的设计
一、引言 颚式破碎机作为矿山、建材等行业的重要破碎设备,其性能优劣直接影响物料破碎效率与质量。随着工业生产规模的扩大和对破碎效率要求的提高,设计一款高效、稳定、节能的颚式破碎机具有重要意义。 二、设计需求分析 处理能力:根据目…...
1panel第三方应用商店(本地商店)配置和使用
文章目录 引言资源网站实战操作说明 引言 1Panel 提供了一个应用提交开发环境,开发者可以通过提交应用的方式将自己的应用推送到 1Panel 的应用商店中,供其他用户使用。由此衍生了一种本地应用商店的概念,用户可以自行编写应用配置并上传到自…...
ObjectOutputStream 深度解析
ObjectOutputStream 深度解析 ObjectOutputStream 是 Java IO 体系中的一个关键类,用于序列化(将对象转换为字节流),通常与 ObjectInputStream 配合使用,实现对象的持久化存储或网络传输。 1.作用:完成对象的序列化过程 2.它可以将JVM当中的Java对象序列化到文件中/网…...
如何学习和研究量子计算与量子计算机:从理论到实践的完整路径
量子计算作为量子力学与计算机科学的交叉领域,正在迅速改变我们对计算能力的认知。无论是破解经典加密算法,还是加速药物分子模拟,量子计算都展现出巨大的潜力。然而,学习这一领域需要系统化的理论知识和实践能力。以下是基于最新…...
数据结构学习笔记 :二叉搜索树与高效查找算法详解
目录 二叉搜索树(BST)实现 1.1 顺序存储实现 1.2 链式存储实现查找算法 2.1 顺序查找 2.2 折半查找 2.3 哈希查找总结与应用场景代码示例与完整实现 一、二叉搜索树(BST)实现 1. 顺序存储实现 BST的顺序存储基于完全二叉树的特…...
广搜bfs-P1443 马的遍历
P1443 马的遍历 题目来源-洛谷 题意 要求马到达棋盘上任意一个点最少要走几步 思路 国际棋盘规则是马的走法是-日字形,也称走马日,即x,y一个是走两步,一个是一步 要求最小步数,所以考虑第一次遍历到的点即为最小步数ÿ…...
Ubuntu22.04安装QT、px4安装环境
Ubuntu22.04安装QGC编译环境、QT、px4编译环境 安装QGC安装Ubuntu安装QT配置px4安装环境出现错误怎么办 安装QGC 我使用的是pixhawk V5飞控,在QGC4.4 Guide里,说 安装Ubuntu 直接去清华源里将Ubuntu镜像下载下来(网址:清华源下…...
【IDEA2020】 解决开发时遇到的一些问题
目录 一、批量更新数据库数据 逐条更新 Db.updateEntitiesBatch() 二、Error running,Command line is too long. Shorten command line 报错场景 报错分析 解决方法 一、批量更新数据库数据 逐条更新 List<UserModel> ums userMapper.selectListBy…...
基于autoware1.14的实车部署激光雷达循迹,从建图、定位、录制轨迹巡航点、到实车运行。
1.首先安装autoware ,大家可以以下一下博客进行安装,如果缺少库什么的直接问ai安装对应的库就行。ubuntu18.04安装Autoware1.14---GPU版 最全环境配置说明_autoware1.14安装教程-CSDN博客 安装成功后运行: source install/setup.bash roslau…...
抽象类和接口的区别
1. 定义 抽象类:用于描述一类事物的共性接口:用于描述行为。 2. 方法和变量 抽象类: 可以有普通方法和抽象方法。可以有普通成员变量和静态常量。 接口: JDK 8之前只支持抽象方法,JDK 8后支持默认方法和静态方法…...
自注意力机制self-attention
目录 简介: 输入和输出方式: Sequence Labeling: self-attention运作方式: 一:怎么从vector得到b1 二:利用矩阵的方法怎么得到 Multi-head Self-attention: positional encoding&#x…...
《Operating System Concepts》阅读笔记:p735-p737
《Operating System Concepts》学习第 62 天,p735-p737 总结,总计 3 页。 一、技术总结 1.distributed system (1)定义 A collection of loosely coupled nodes interconnected by a communication network(一组通过通信网络相互连接的松散耦合节点)…...
2025-04-19 Python 强类型编程
文章目录 1 方法标注1.1 参数与返回值1.2 变参类型1.3 函数类型 2 数据类型2.1 内置类型2.2 复杂数据结构2.3 类别选择2.4 泛型 3 标注方式3.1 注释标注3.2 文件标注 4 特殊情形4.1 前置引用4.2 函数标注扩展4.3 协变与逆变4.4 dataclass 5 高级内容5.1 接口5.2 泛型的协变/逆变…...
RVOS的任务调度优化
12.系统优化–任务调度 12.1 改进任务管理功能 在原有基础上进⼀步改进任务管理功能。具体要求:改进 task_create(),提供更多的参数,具体改进后的函数如下所⽰: int task_create(void (*task)(void* param),void *param, uint8…...
【论文阅读20】-CNN-Attention-BiGRU-滑坡预测(2025-03)
这篇论文主要探讨了基于深度学习的滑坡位移预测模型,结合了MT-InSAR(多时相合成孔径雷达干涉测量)观测数据,提出了一种具有可解释性的滑坡位移预测方法。 [1] Zhou C, Ye M, Xia Z, et al. An interpretable attention-based deep…...
图像预处理-图像噪点消除
一.基本介绍 噪声:指图像中的一些干扰因素,也可以理解为有那么一些点的像素值与周围的像素值格格不入。常见的噪声类型包括高斯噪声和椒盐噪声。 滤波器:也可以叫做卷积核 - 低通滤波器是模糊,高通滤波器是锐化 - 低通滤波器就…...
PP-OCR的安卓端部署
EMO了几天 我浪费了几天的生命,去研究PP-OCR的模型微调、从训练模型导出预测模型,结果一个坑接着一个坑,没有善终。 找了好多资料,得到一些负面信息,比如说飞浆的团队修复问题不及时啦,代码仓库有好多年不…...
2048小游戏C++板来啦!
个人主页:PingdiGuo_guo 收录专栏:C干货专栏 大家好呀,我是PingdiGuo_guo,今天我们来学习如何用C编写一个2048小游戏。 文章目录 1.2048的规则 2.步骤实现 2.1: 初始化游戏界面 2.1.1知识点 2.1.2: 创建游戏界面 2.2: 随机…...
研0大模型学习(第四、五天)
学习CSDN教程:VSCode Debug指南 但里面貌似主要是针对nodejs的,所以我在 CSDN教程:VSCode调试python程序 中学习,刚开始调试报错python版本太低,于是我安装了旧版本的pythondebugger,再把python解释器从原…...
编程规范之整数运算
在表达式中混用有符号数和无符号数时,可能会因隐式转换而导致非预期的结果。因此应尽量在表达式中使用相同符号类型的 变量。 对于无法使用相同符号类型的场景,应将不同类型的变量显式转换为相同类型,当表达式中的无符号数隐式转换为另一个有…...
【零基础】基于 MATLAB + Gurobi + YALMIP 的优化建模与求解全流程指南
MATLAB Gurobi YALMIP 综合优化教程(进阶) 本教程系统介绍如何在 MATLAB 环境中使用 YALMIP 建模,并通过 Gurobi 求解器高效求解线性、整数及非线性优化问题。适用于工程、运营研究、能源系统等领域的高级优化建模需求。 一、工具概览 1.…...
C++17 信号量模拟实现
C17 信号量模拟实现 一、实现原理 C17 标准库没有原生信号量(C20才有),但可以通过 std::mutex std::condition_variable 模拟实现。以下是核心逻辑: #include <mutex> #include <condition_variable>class CountingSemaphore { private:…...
LINUX学习——守护进程的含义及编程实现
实验目的 理解守护进程的含义。掌握编程实现守护进程的主要步骤。 实验步骤 守护进程的含义: 守护进程是运行在后台的一种特殊进程,独立于控制终端,周期性地执行任务或等待处理事件。守护进程通常以 d 结尾,如 httpd、sshd 等。…...
Json 在线格式化 - 加菲工具
Json 在线格式化 打开网站 加菲工具 选择“Json 在线格式化” 或者直接进入 https://www.orcc.top/tools/json 输入Json,点击左上角的“格式化”按钮 得到格式化后的结果...
final关键字带来的问题
定义了一个配置类: public class EsignConfig { public static final String EsignOrgId "*****"; // 应用ID public static final String EsignAppId "*****"; // 应用密钥 public static final String EsignAppSecret…...
Flash存储器(一):接口标准全解析
目录 一.接口类型与协议标准 二.应用场景 Flash存储器的接口设计直接影响其性能、应用场景及系统集成复杂度。以下从接口类型、协议标准及应用场景三个方面进行系统阐述。 一.接口类型与协议标准 Flash接口类型与协议标准 序号接口类型协议标准特点1并行接口…...
第18周:对于ResNeXt-50算法的思考
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 问题:在上一章代码中是ResNeXt-50的残差单元,问题:如果conv shortcutFalse那么在执行“xAdd0)…“语句时,通道数不一致的&…...
Django 结合 Vue 实现简单管理系统的详解
以下是一个 Django 结合 Vue 实现简单管理系统的详细步骤及示例代码: 项目整体架构思路 后端:使用 Django 搭建 RESTful API,负责数据的存储和处理。前端:使用 Vue 构建用户界面,通过调用后端 API 实现数据的展示、添加、修改和删除等操作。步骤 1:创建 Django 项目和应…...
ValueError: model.embed_tokens.weight doesn‘t have any device set
ValueError: model.embed_tokens.weight doesn’t have any device set model.embed_tokens.weight 通常在深度学习框架(如 PyTorch)中使用,一般是在处理自然语言处理(NLP)任务时,用于指代模型中词嵌入层(Embedding layer)的权重参数。下面详细解释: 词嵌入层的作用 …...
公务员行测之速算分数记忆检验-无答案版本
前言 这一篇是根据百分数,默写分数,我发现我看见百分数有点想不到分数 速算之百分数记忆检验 50 % \ 50\% 50% 33.3 % \ 33.3\% 33.3% 25 % \ 25\% 25% 20 % \ 20\% 20% 16.7 % \ 16.7\% 16.7% 14.3 % \ 14.3\% 14.3% 12.5 % \ 12.5\% 1…...
大模型面经 | DeepSpeed中ZeRO-1、ZeRO-2和ZeRO-3的区别是什么?
大家好,我是皮先生!! 今天给大家分享一些关于大模型面试常见的面试题,希望对大家的面试有所帮助。 往期回顾: 大模型面经 | 春招、秋招算法面试常考八股文附答案(RAG专题一) 大模型面经 | 春招、秋招算法面试常考八股文附答案(RAG专题二) 大模型面经 | 春招、秋招算法…...
【Java面试系列】Spring Boot微服务架构下的分布式事务设计与实现详解 - 3-5年Java开发必备知识
【Java面试系列】Spring Boot微服务架构下的分布式事务设计与实现详解 - 3-5年Java开发必备知识 引言 在微服务架构中,分布式事务是一个不可避免的话题。随着业务复杂度的提升,单体应用逐渐演变为多个微服务,如何保证跨服务的数据一致性成为…...
大语言模型减少幻觉的常见方案
什么是大语言模型的幻觉 大语言模型的幻觉(Hallucination)是指模型在生成文本时,输出与输入无关、不符合事实、逻辑错误或完全虚构的内容。这种现象主要源于模型基于概率生成文本的本质,其目标是生成语法合理、上下文连贯的文本&…...
方案解读:虚拟电厂标杆项目整体建设方案【附全文阅读】
在电力市场背景下,传统电力现货市场存在电能定价不合理、分布式电源并网困难等问题。本虚拟电厂标杆项目旨在研究全时间尺度虚拟电厂智能管控关键技术,通过研制虚拟电厂控制器样机、开发运行管理平台,实现对分布式能源的合理优化配置。项目内容涵盖虚拟调控、建设目标、建设…...
shiro使用
shiro是apache提供的一种安全框架。他可以将登录,权限这一方面简单化。 使用shiro需要引入 <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.9.0</version></depend…...
Spring Boot日志系统详解:Logback与SLF4J的默认集成
大家好呀!👋 今天我们来聊聊Spring Boot中一个超级重要但又经常被忽视的功能——日志系统! 一、日志系统的重要性 首先,咱们得明白为什么日志这么重要?🤷♂️ 想象一下,你正在玩一个超级复…...
基于SpringBoot成绩管理系统设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…...
AI象棋 3.0 |AI自动下象棋工具,破译残局,自动帮助下棋,内置视频教程
Ai象棋是一款自动识别棋盘并分析下棋局势的工具,它能够自动化操作,帮助用户学习象棋知识。该软件无限制使用、界面无广告简洁,并提供永久授权。数据库中挑选1到2个即可完全满足使用需求,不必全部下载,解压mt管理器即可…...
刘鑫炜履新共工新闻社新媒体研究院院长,赋能媒体融合新征程
2025年4月18日,大湾区经济网战略媒体共工新闻社正式对外宣布一项重要人事任命:聘任蚂蚁全媒体总编刘鑫炜为新媒体研究院第一任院长。这一举措,无疑是对刘鑫炜在新媒体领域卓越专业能力与突出行业贡献的又一次高度认可,也预示着共工…...
学习型组织与系统思考
真正的学习型组织不是只关注个人的学习,而是关注整个系统的学习。—彼得圣吉 在这两年里,越来越多的企业开始询问是否可以将系统思考的内容内化给自己的内训师,进而在公司内部进行教学。我非常理解企业这样做的动机,毕竟内部讲师…...
mysql中优先使用datetime存储时间
基于大模型的问答进行了记录。 对为甚timestamp不如datetime好用做了深入了解,再此记录 范围广 ‘1000-01-01 00:00:00’ 到 ‘9999-12-31 23:59:59’ 而 timestamp 仅支持’1970-01-01 00:00:01’ 到 ‘2038-01-19 03:14:07’ UTC, 存在2038年问题, 不适合长期存…...