目录
一.共享内存
1.底层原理和系统接口
a.底层原理
b.系统接口
① shmget
② shmat
③ shmdt
④ shmctl
c.命令行控制指令
2.共享内存的通信特点
a.共享内存间的通信没有任何的同步机制
b.共享内存是所有进程间通信速度最快的
c.共享内存可以提供较大的空间。
3.OS对共享内存的管理
4.*代码级实现~
源码链接
二.消息队列
1.消息队列的主要特点
2.消息队列系统接口
a. msgget
b. msgsnd
c. msgrcv
d. msgctl
3.命令行控制指令
我们知道,进程间通信的本质就是让不同的进程看到同一份资源,由于资源的不同,进程间通信的方式也就不同,上篇文章博主主要讲解了基于管道的进程间通信,这篇文章,则主要讲解基于共享内存和消息队列的进程间通信。
一.共享内存
共享内存,顾名思义,就是让不同的进程使用同一份物理内存上的空间,从而实现进程间通信。
1.底层原理和系统接口
a.底层原理
在物理内存上开辟一份空间,用过用户级页表,将其映射到进程地址空间上的共享区内,并生成空间首地址返回给用户,而不同的进程便可通过其地址空间上共享区内的空间地址,共享物理内存上的同一块空间,从而实现不同进程间的通信!
大体细节,图解如下:
通过上述原理详解,我们知道,基于共享内存进行进程间通信,大致有两步:①开辟一份物理内存;②将开辟出来的物理内存地址与进程地址空间上的地址建立映射关系(与进程建立关系)。这样一来,用户便能通过进程向共享空间中进行数据IO了~~
待进程通信结束后,咱们如果想要释放某块共享内存,也有两步骤,即先接触内存与进程地址空间的映射关系,再释放共享内存。
好巧不巧,为实现上述功能,系统已经为我们安排好了专门的系统调用接口~~
b.系统接口
① shmget
int shmid = shmget(key_t key,size_t size,int shmflg);
作用:OS开辟一块物理内存,返回值是物理内存的入口(进程层面)
参数详解
第一个参数 key : 相当于进入一个内存空间的秘钥,不同的进程通过同一个 key 找到目标共享内存,为确保key值的唯一性,常与 ftok() 函数连用,该函数通过特殊的算法,可以将文件路径和进程id转化成一个具有唯一性的整型值,供给key使用。
第二个参数 size : 表示所创建的共享内存的大小,单位是“字节”,一般是4096的整数倍。
第三个参数 shmflg :IPC_CREAT 表示,若 key 不存在,就创建;若存在,就获取并返回;IPC_CREAT | IPC_EXCL 表示,若 key 不存在,就创建;若存在,就出错返回。
② shmat
void* shmaddr = shmat(int shmid, const void *shmaddr, int shmflg);
作用:OS将指定的物理内存映射到进程地址空间,返回值是映射到地址空间上的地址 shmaddr,上层用户可以将 shmaddr 当做一个数组来使用。
参数详解
第一个参数 shmid :共享内存的标识符,该标识符由 shmget 函数返回。
第二个参数 shmaddr :指定共享内存出现在进程内存地址的什么位置。如果直接指定为NULL,则让内核自己决定一个合适的地址位置。
第三个参数 shmflg :指定共享内存的映射选项。SHM_RDONLY表示以只读方式连接共享内存段,否则默认为读写方式(0)。
③ shmdt
int shmdt(const void* shmaddr);
作用:解除一个物理内存和进程地址空间的映射关系,参数就是 shmat 函数的返回值。
④ shmctl
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
作用:控制某一物理内存,包括将其删除等操作。
参数详解
第一个参数 shmid :共享内存标识符,即要控制的共享内存段的标识符,shmget 函数的返回值。
第二个参数 cmd :控制命令,用于指定要执行的操作。常用的命令包括IPC_STAT(获取共享内存的状态)、IPC_SET(改变共享内存的状态)、IPC_RMID(删除共享内存段)等。
第三个参数 buf :指向struct shmid_ds结构的指针,用于存储或传递共享内存段的信息。对于IPC_STAT命令,该结构用于接收共享内存的状态信息;对于IPC_SET命令,该结构用于提供新的状态信息(如权限、所有者等)。如果不需要传递或接收信息,可以设置为NULL。
c.命令行控制指令
ipcs -m 查看已经存在了的共享内存.
ipcrm -m + shmid 删除标识为 shmid 的共享内存.
将 ipcs -m 这一指令每隔一秒运行一次:while : ; do ipcs -m ; sleep 1 ; done
注意:共享内存的生命周期是随内核的,不随进程的结束而终止,需用户手动写代码关闭!!
2.共享内存的通信特点
a.共享内存间的通信没有任何的同步机制
共享内存是直接裸露给所有使用者的,一定要注意共享内存的使用安全问题,我们可以结合互斥锁、条件变量或管道等机制来实现共享内存的同步机制。
b.共享内存是所有进程间通信速度最快的
①数据拷贝次数少:共享内存允许多个进程直接访问同一块物理内存区域,数据交换和通信过程中无需通过系统内核或其他中间媒介进行数据的复制或移动。相比之下,其他IPC机制(如命名管道、消息队列等)通常需要将数据从用户空间拷贝到内核空间,再从内核空间拷贝到另一个用户空间,这一过程增加了额外的开销。
②内存映射:在共享内存中,多个进程将同一块物理内存映射到它们各自的虚拟地址空间中,这使得进程可以直接通过指针访问共享内存(就像一个数组),无需通过系统调用或其他复杂的操作。
c.共享内存可以提供较大的空间。
3.OS对共享内存的管理
系统中同时存在多个共享内存,OS对这些内存进行管理,如何管理??—— 先描述,在组织!!
即,当某一块共享内存被开辟时,OS会先创建一个存有该共享内存属性数据的结构体对象,然后再将该对象链入特定的数据结构中,这样一来,OS对共享内存的管理,就变成了对特定数据结构中某个结构体对象的增、删、查、改!!
由于同一块共享内存可能被多个进程所使用,所以,该结构体内需要有一个“引用计数”,用来表征与内存建立映射关系的进程数,只有当该引用计数为0时,才能释放这块内存~~
由于共享内存可能同时存在多份,那么如何保证第二个之后的参与通信的进程,看到的就是目标共享内存呢?--- 所以,要求每块共享内存都要有一个具有唯一性的字段(key),用于标识自身!
用于管理共享内存的结构体类型:struct shmid_ds{ 引用计数、key值、空间大小、权限...... 等};
4.*代码级实现~
源码链接
https://gitee.com/Coder-Li-YuJie/naming-pipes-and-shared-memory
二.消息队列
消息队列是一种在分布式系统中常用的应用间通信方法,它主要解决的是应用解耦、异步消息处理、流量削峰等问题。消息队列允许一个或多个生产者发送消息到一个共享的队列中,而一个或多个消费者可以从这个队列中取出消息并处理它们。这种方式使得生产者和消费者之间不需要直接通信,它们只需与消息队列进行交互,从而降低了系统的耦合度,提高了系统的可扩展性和容错性。
1.消息队列的主要特点
解耦:生产者和消费者不需要直接通信,它们之间的依赖关系被消息队列解耦。生产者只需关注如何将消息发送到队列,而消费者只需关注如何从队列中接收消息并处理。
异步处理:生产者将消息发送到队列后,可以立即返回继续处理其他任务,而不需要等待消费者的响应。这样可以显著提高系统的响应速度和处理能力。
流量削峰:在高峰期,生产者可能会产生大量的消息,这些消息会暂时存储在消息队列中,等待消费者逐一处理。这样可以避免直接冲击后端服务,起到“削峰填谷”的作用。
负载均衡:多个消费者可以从同一个队列中消费消息,队列可以根据一定的策略(如轮询、优先级等)将消息分发给不同的消费者,从而实现负载均衡。
消息可靠性:消息队列通常提供消息持久化功能,确保即使发生系统故障,消息也不会丢失。同时,一些消息队列还支持消息确认机制,只有消费者成功处理消息后,消息才会从队列中删除。
2.消息队列系统接口
a. msgget
int msgget(key_t key, int msgflg);
创建一个消息队列。功能与 shmget 类似,参数 key 和 msgflg 与 shmget() 函数中的 key、shmflg作用一摸一样,就不在此赘述~~
b. msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
将数据写入到指定的消息队列中。
参数一 msqid:消息队列的标识符,也是 msgget() 的返回值。
参数二 msgp:指向要发送的消息的指针,消息的第一个字段必须是ong
类型,表示消息类型。
参数三 msgsz:消息的大小(不包括消息类型字段)。
参数四 msgflg:控制消息发送行为的标志位,如 IPC_NOWAIT 表示非阻塞发送。
c. msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
从指定的消息队列中读取数据。
参数一 msqid:消息队列的标识符,也是 msgget() 的返回值。
参数二 msgp:用于接收数据的接收缓冲区的指针。
参数三 msgsz:接收缓冲区大小(不包含消息类型字段)。
参数四 msgtyp:指定要接收的消息类型,0表示接收队列中的第一个消息,大于0表示接收类型等于 msgtyp 的第一个消息,小于0表示接收类型等于或小于 msgtyp 绝对值的第一个消息。
参数五 msgflg:控制消息接收行为的标志位,如0
表示阻塞接收。
d. msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数一 msqid:消息队列的标识符,也是 msgget() 的返回值。
参数二 cmd:指定要执行的操作,如IPC_STAT
用于获取消息队列的状态,IPC_SET
用于设置消息队列的属性。
参数三 buf:指向msqid_ds
结构的指针,用于存储或接收消息队列的属性。
3.命令行控制指令
命令行查看消息队列的指令——ipcs -q
命令行删除消息队列的指令——ipcrm -q