首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

【Linux】systemV消息队列和信号量

  • 25-04-25 04:45
  • 2825
  • 9479
blog.csdn.net

在这里插入图片描述

个人主页~


消息队列和信号量

  • 一、消息队列
    • 1、实现原理
    • 2、系统调用接口
      • (一)创建获取一个消息队列
      • (二)控制消息队列
      • (三)发送消息
      • (四)在消息队列中获取数据块
  • 二、信号量
    • 1、原理
    • 2、系统调用接口
      • (一)创建获取一个信号量
      • (二)控制信号量
      • (三)PV操作
  • 三、systemV IPC方法的比较
    • 1、描述IPC资源的结构体
    • 2、操作系统对IPC资源进行管理

一、消息队列

1、实现原理

操作系统在内核建立一个队列,通信的两个进程AB以数据块的形式将需要发送的数据pushback到队列中,数据块是一个结构体,其中有字段标识该数据块是谁发送的,所以我们只要让不同的进程看到同一个队列就可以了

2、系统调用接口

(一)创建获取一个消息队列

msgget函数的主要功能是创建一个新的消息队列或者获取一个已经存在的消息队列的标识符

#include 
#include 
#include 
int msgget(key_t key, int msgflg);
  • 1
  • 2
  • 3
  • 4

返回值:成功返回一个msgid,失败返回-1
key:ftok函数的返回值
msgflg:标识符

函数msgflg作用示例
msggetIPC_CREAT如果指定键对应的消息队列不存在,则创建一个新的消息队列;若已存在,则直接返回该消息队列的标识符msgget(key, IPC_CREAT | 0666)
msggetIPC_EXCL通常与 IPC_CREAT 一起使用,若同时设置这两个标志,当消息队列已经存在时,msgget 调用会失败并返回 -1,errno 会被设置为 EEXISTmsgget(key, IPC_CREAT | IPC_EXCL | 0666)
msgget0600消息队列的所有者具有读写权限,所属组和其他用户没有任何权限msgget(key, 0600)
msgget0660消息队列的所有者和所属组具有读写权限,其他用户没有权限msgget(key, 0660)
msgget0666消息队列的所有者、所属组和其他用户都具有读写权限msgget(key, 0666)

(二)控制消息队列

msgctl用于控制消息队列的系统调用函数,通常用于对消息队列执行各种管理操作,如获取消息队列状态、设置消息队列属性以及删除消息队列等

#include 
#include 
#include 
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
  • 1
  • 2
  • 3
  • 4

返回值:返回0表示操作成功,返回-1表示操作失败
msgid:消息队列标识符,msgget函数返回值
cmd:msgctl函数的cmd参数常用命令如下:

命令说明
IPC_STAT获取消息队列的状态信息,将信息存储在buf指向的msqid_ds结构中。这些信息包括消息队列的权限、所有者信息、消息队列的大小、当前消息数量等
IPC_SET根据buf指向的msqid_ds结构中的值,设置消息队列的属性。可以设置的属性包括消息队列的权限、队列的最大字节数等
IPC_RMID删除指定的消息队列。调用该命令后,消息队列将被立即删除,所有排队的消息都会被丢弃,并且与该消息队列相关的资源也会被释放
MSG_INFO获取与消息队列相关的系统资源使用信息,例如当前系统中消息队列的总数、系统允许的最大消息队列数等
MSG_STAT该命令与IPC_STAT类似,但它返回的是一个指向struct msg_info结构的指针,该结构包含了更多关于消息队列的统计信息,如发送和接收消息的字节数等

buf:一个指向msgid_ds结构体的指针,用于存储或提供消息队列的相关信息,msqid_ds结构包含了消息队列的各种属性,如队列的权限、所有者信息、消息队列的大小等

(三)发送消息

msgsnd用于向消息队列发送消息的系统调用函数,它允许进程将一个消息添加到指定的消息队列中

#include 
#include 
#include 
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
  • 1
  • 2
  • 3
  • 4

返回值:成功返回0,失败返回-1
msgid:消息队列标识符,msgget函数返回值
msgp:指向要发送的消息结构体的指针,该结构体的第一个成员必须是 long 类型,用于指定消息的类型,后续可以包含消息的数据部分
msgsz:消息数据部分的长度,即msgp所指向结构体中除第一个long类型成员之外的数据长度
msgflg:该位置为0就是不设置

函数msgflg作用示例
msgsndIPC_NOWAIT非阻塞发送消息,当消息队列已满,无法立即发送消息时,如果设置了该标志,msgsnd 函数会立即返回 -1,errno 被设置为 EAGAIN;若未设置该标志,msgsnd 函数会阻塞,直到消息队列有空间可以发送消息msgsnd(msgid, &msgbuf, sizeof(msgbuf.mtext), IPC_NOWAIT)

(四)在消息队列中获取数据块

msgrcv用于从消息队列接收消息的系统调用函数,它允许进程从指定的消息队列中获取消息

#include 
#include 
#include 
ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
  • 1
  • 2
  • 3
  • 4

返回值:成功返回实际收到的消息数据部分的字节数,不包括最前面的long
前两个参数与前面相同
msgsz:接收消息时用于存储消息数据部分的缓冲区的最大长度
msgtyp:如果等于0,那该函数只接收消息队列中的第一条消息,如果大于0,接收消息队列中消息类型为msgtyp的第一条消息,如果小于0,接收消息队列中消息类型小于等于msgtyp绝对值的最小类型的第一条消息
msgflg:该位置为0就是不设置

函数msgflg作用示例
msgrcvIPC_NOWAIT当消息队列中没有符合要求的消息时,如果设置了该标志,msgrcv 函数会立即返回 -1,errno 被设置为 ENOMSG;若未设置该标志,msgrcv 函数会阻塞,直到有符合要求的消息进入消息队列msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgtype, IPC_NOWAIT)
msgrcvMSG_NOERROR如果接收到的消息长度超过了指定的缓冲区大小,若设置了该标志,消息会被截断为缓冲区大小,多余部分会被丢弃,msgrcv 函数正常返回;若未设置该标志,msgrcv 函数会返回 -1,errno 被设置为 E2BIGmsgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), msgtype, MSG_NOERROR)

二、信号量

1、原理

信号量是一种用于实现进程间同步与互斥的机制,信号量本质上是一个整数变量,用于控制对共享资源的访问,它可以看作是一种特殊的计数器,其值表示当前可用的共享资源数量,信号量的值可以被多个进程或线程读取和修改,通过对信号量的操作,进程或线程可以协调对共享资源的访问

信号量的工作基于两个基本操作:P操作(wait操作)和V操作(signal操作)
P操作:当一个进程或线程需要访问共享资源时,它会执行 P 操作。P 操作会将信号量的值减 1,如果减 1 后信号量的值大于等于 0,表示当前有可用的资源,进程或线程可以继续访问;如果减 1 后信号量的值小于 0,表示没有可用的资源,进程或线程会被阻塞,直到有其他进程或线程释放资源

V 操作:当一个进程或线程使用完共享资源后,它会执行 V 操作,V 操作会将信号量的值加 1,如果加 1 后信号量的值小于等于 0,表示有其他进程或线程正在等待该资源,此时会唤醒一个等待的进程或线程

2、系统调用接口

(一)创建获取一个信号量

semget是用于创建或获取信号量集的系统调用函数

#include 
#include 
#include 

int semget(key_t key, int nsems, int semflg);
  • 1
  • 2
  • 3
  • 4
  • 5

返回值:成功返回信号量标识符semid,失败返回-1
nsems:表示要创建或获取的信号量集中信号量的数量,如果是创建新的信号量集则必须大于 0,如果是获取已有的信号量集则可以为0
semflg:标志位,用于指定创建或获取信号量集的方式和权限

(二)控制信号量

semctl是用于控制信号量集的系统调用函数,它可以对信号量集进行多种操作,如初始化信号量的值、获取信号量的状态、删除信号量集等

#include 
#include 
#include 

int semctl(int semid, int semnum, int cmd, ...);
  • 1
  • 2
  • 3
  • 4
  • 5

返回值:取决于cmd的当前值,对于 GETVAL 命令,返回指定信号量的当前值,对于 IPC_STAT、IPC_SET 和 IPC_RMID 等命令,返回 0 表示成功
semid:信号量标识符,semget函数返回
semnum:信号量集中信号量的编号,编号从 0 开始,如果 cmd 操作不需要针对特定的信号量(如删除整个信号量集),则可以忽略该参数,通常将其设为 0
cmd:要执行的命令,指定了对信号量集或特定信号量的操作类型

(三)PV操作

semop用于对信号量集执行操作的系统调用函数,它允许进程对一个或多个信号量进行原子性的 P和 V操作,从而实现进程间的同步与互斥

#include 
#include 
#include 

int semop(int semid, struct sembuf *sops, unsigned nsops);
  • 1
  • 2
  • 3
  • 4
  • 5

返回值:成功返回0,失败返回-1
sops:指向struct sembuf结构体数组的指针,该数组包含了要对信号量集执行的操作序列
nsops:sops数组中元素的数量,即要执行的操作序列的长度

三、systemV IPC方法的比较

1、描述IPC资源的结构体

描述共享内存IPC资源结构体:

struct shmid_kernel /* private to the kernel */
{	
	struct kern_ipc_perm	shm_perm;
	struct file *		    shm_file;
	int						id;
	unsigned long			shm_nattch;
	unsigned long			shm_segsz;
	time_t					shm_atim;
	time_t					shm_dtim;
	time_t					shm_ctim;
	pid_t					shm_cprid;
	pid_t					shm_lprid;
	struct user_struct		*mlock_user;
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

描述消息队列IPC资源结构体:

struct msg_queue {
	struct kern_ipc_perm q_perm;
	time_t 				 q_stime;			/* last msgsnd time */
	time_t 				 q_rtime;			/* last msgrcv time */
	time_t				 q_ctime;			/* last change time */
	unsigned long 		 q_cbytes;			/* current number of bytes on queue */
	unsigned long		 q_qnum;			/* number of messages in queue */
	unsigned long		 q_qbytes;			/* max number of bytes on queue */
	pid_t				 q_lspid;			/* pid of last msgsnd */
	pid_t				 q_lrpid;			/* last receive pid */

	struct list_head 	 q_messages;
	struct list_head	 q_receivers;
	struct list_head	 q_senders;
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

描述信号量IPC资源结构体:

struct sem_array {
	struct kern_ipc_perm	sem_perm;			/* permissions .. see ipc.h */
	time_t					sem_otime;			/* last semop time */
	time_t					sem_ctime;			/* last change time */
	struct sem				*sem_base;			/* ptr to first semaphore in array */
	struct sem_queue		*sem_pending;		/* pending operations to be processed */
	struct sem_queue		**sem_pending_last; /* last pending operation */
	struct sem_undo			*undo;				/* undo requests on this array */
	unsigned long			sem_nsems;			/* no. of semaphores in array */
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

他们有一个同样的特点就是第一个参数都是struct kern_ipc_perm类型的

struct kern_ipc_perm
{
	spinlock_t		lock;
	int				deleted;
	key_t			key;
	uid_t			uid;
	gid_t			gid;
	uid_t			cuid;
	gid_t			cgid;
	mode_t			mode; 
	unsigned long	seq;
	void			*security;
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2、操作系统对IPC资源进行管理

所有的IPC资源都有一个struct kern_ipc_perm结构,所以操作系统通过数组将这些struct kern_ipc_perm结构组织起来

ipc_ids是 Linux 内核中用于管理IPC资源的核心数据结构

struct ipc_ids {
    int             in_use;//记录当前系统中正在使用的IPC资源的数量
    int             max_id;//表示系统中允许的最大IPC标识符值
    unsigned short  seq;//是一个序列号,用于生成唯一的IPC标识符
    unsigned short  seq_max;//是序列号的最大值
    struct          semaphore sem;//这是一个信号量,用于对IPC资源的并发访问进行同步控制    
    struct          ipc_id_ary nullentry;//一个空的ipc_id_ary结构
    struct          ipc_id_ary* entries;//指向ipc_id_ary结构体的指针
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
struct ipc_id_ary {
    int    size;
    struct kern_ipc_perm *p[0];
};
  • 1
  • 2
  • 3
  • 4

这里的柔性数组p的作用就是维护当前操作系统中所有IPC资源,我们通过强制类型转换来通过这个数组里存的struct ipc_id_ary*找到具体的IPC对象,因为kern_ipc_perm是这三个结构体中的第一个成员,我们只要知道了一个kern_ipc_perm的地址,就相当于知道了某个具体IPC对象的起始地址,然后通过强制类型转换就可以访问到该IPC对象中的所有成员属性,这样就实现了对一个具体IPC对象的访问,如((struct shmid_kernel*)p[0])->q_stime,在kern_ipc_perm中有字段来标识该kern_ipc_perm是属于哪种IPC资源,操作系统就知道要将其强制转化成什么类型了,我们在用户层面上使用的:shmid、msqid、semid在内核上看就是p数组的下标

ipc_id_arry属于操作系统,不属于任何进程,数组下标是线性递增的,但不会因为IPC资源的释放而改变它的递增属性,即当前操作系统中最后一个IPC资源的下标是100,释放掉这个IPC资源,下一次再创建IPC资源的时候它的下标是101,而不是100,当递增到一定值的时候,会回到0


今日分享就到这里了~

在这里插入图片描述

探讨嵌入式学习的内容,请加V子
微信名片
注:本文转载自blog.csdn.net的s_little_monster_的文章"https://blog.csdn.net/s_little_monster/article/details/145651136"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

122
操作系统
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top