Unix环境高级编程-学习-03-XSI-IPC之消息队列
目录
一、验证环境
名称 | 值 |
CPU | Intel(R) Core(TM) i5-1035G1 CPU @ 1.00GHz |
操作系统 | CentOS Linux release 7.9.2009 (Core) |
内存 | 3G |
逻辑核数 | 2 |
二、Linux进程间通信进制
Linux进程间通信进制可以按照进程是否在同一主机上或不同主机上分为两大类,具体分类如下表:
一级 | 二级 | 三级 | 四级 |
进程间通信进制 | 同一主机进程间通信机制 | Unix进程间通信进制 | 管道 |
命名管道 | |||
信号 | |||
XSI-IPC机制 | 信号量集 | ||
共享内存 | |||
消息队列 | |||
不同主机进程间通信机制 | RPC | ||
Socket |
三、XSI-IPC机制
名称 | 英文名 | 描述 |
信号量集 | semaphore set | 用于实现进程之间的同步与互斥。 |
共享内存 | shared memory | 用于在进程之间高效地共享数据,适用于数据量大,速度要求高地场景。 |
消息队列 | message queue | 进程之间传递数据地一种简单办法。 |
四、IPC相关地操作系统命令
这些命令可以辅助我们更好地观察实验现象,有的话添彩,没有的话,也麽事情。
1、ipcs
命令用途是:提供有关IPC设施的信息。
只介绍我用的多的。
(1)-a
输出所有信息。
[root@czg2 ~]# ipcs -a
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x00000000 2 gdm 777 16384 1 目标
0x00000000 5 gdm 777 2129920 2 目标
--------- 信号量数组 -----------
键 semid 拥有者 权限 nsems
(2)-l
显示资源限制
[root@czg2 ~]# ipcs -l
---------- 消息限制 -----------
系统最大队列数量 = 32000
最大消息尺寸 (字节) = 8192
默认的队列最大尺寸 (字节) = 16384
---------- 同享内存限制 ------------
最大段数 = 4096
最大段大小 (千字节) = 18014398509465599
最大总共享内存 (千字节) = 18014398442373116
最小段大小 (字节) = 1
--------- 信号量限制 -----------
最大数组数量 = 128
每个数组的最大信号量数目 = 250
系统最大信号量数 = 32000
每次信号量调用最大操作数 = 32
信号量最大值 = 32767
2、ipcrm
命令用途是:删除XSI-IPC对象。
只介绍我用的多的。
(1)-Q
根据消息队列键值(Key)删除消息队列。
[root@czg2 ~]# ipcs -a
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x0000000a 21 gbase 666 0 0
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x00000000 2 gdm 777 16384 1 目标
0x00000000 5 gdm 777 2129920 2 目标
--------- 信号量数组 -----------
键 semid 拥有者 权限 nsems
[root@czg2 ~]# ipcrm -Q 0x0000000a
[root@czg2 ~]# ipcs -a
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x00000000 2 gdm 777 16384 1 目标
0x00000000 5 gdm 777 2129920 2 目标
--------- 信号量数组 -----------
键 semid 拥有者 权限 nsems
五、IPC对象的key值和ID值
Linux系统中的IPC对象是全局的,为每个IPC对象分配唯一ID,因为每个内核中的IPC结构(消息队列、信号量或共享内存段)都用一个非负整数表示。
六、消息队列
序号 | 描述 |
1 | 进程之间传递数据的一种简单方法。 |
2 | 把每个消息看作一条记录,具有特定的格式。 |
3 | 消息队列就是消息的链表。 |
4 | 对消息队列有写权限的进程可以按照一定的规则添加新消息。 |
5 | 对消息队列有读权限的进程可以从消息队列中读走消息。 |
6 | 消息队列更够克服管道或命名管道机制的一些缺点,如实时性差。 |
七、消息队列相关结构体
1、struct msqid_ds
内核内一条消息的记录结构。
/* Structure of record for one message inside the kernel.
The type `struct msg' is opaque. */
struct msqid_ds
{
struct ipc_perm msg_perm; /* structure describing operation permission */
__time_t msg_stime; /* time of last msgsnd command */
#ifndef __x86_64__
unsigned long int __unused1;
#endif
__time_t msg_rtime; /* time of last msgrcv command */
#ifndef __x86_64__
unsigned long int __unused2;
#endif
__time_t msg_ctime; /* time of last change */
#ifndef __x86_64__
unsigned long int __unused3;
#endif
__syscall_ulong_t __msg_cbytes; /* current number of bytes on queue */
msgqnum_t msg_qnum; /* number of messages currently on queue */
msglen_t msg_qbytes; /* max number of bytes allowed on queue */
__pid_t msg_lspid; /* pid of last msgsnd() */
__pid_t msg_lrpid; /* pid of last msgrcv() */
__syscall_ulong_t __unused4;
__syscall_ulong_t __unused5;
};
参数 | 描述 |
msg_perm | 结构体描述符操作权限。 |
msg_stime | 最后一条发送函数的时间。 |
msg_rtime | 最后一条接受函数的时间。 |
msg_ctime | 最后更改的时间。 |
__msg_cbytes | 队列的当前字节数。 |
msg_qnum | 队列的当前消息数。 |
msg_qbytes | 队列允许的最大字节数。 |
msg_lspid | 最后一个发送函数进程的pid号。 |
msg_lrpid | 最后一个接收函数进程的pid号。 |
2、struct ipc_perm
用于将权限信息传递给 IPC 操作的数据结构。
/* Data structure used to pass permission information to IPC operations. */
struct ipc_perm
{
__key_t __key; /* Key. */
__uid_t uid; /* Owner's user ID. */
__gid_t gid; /* Owner's group ID. */
__uid_t cuid; /* Creator's user ID. */
__gid_t cgid; /* Creator's group ID. */
unsigned short int mode; /* Read/write permission. */
unsigned short int __pad1;
unsigned short int __seq; /* Sequence number. */
unsigned short int __pad2;
__syscall_ulong_t __unused1;
__syscall_ulong_t __unused2;
};
参数 | 描述 |
__key | IPC键值。 |
uid | 拥有者用户ID。 |
gid | 拥有者组ID。 |
cuid | 创建者用户ID。 |
cgid | 创建者组ID。 |
mode | 读写权限。 |
__seq | 序列号。 |
3、MsgInfoSt(自定义)
这个结构体第一个参数必须是long类型,第二个类型是char类型。
typedef struct MsgInfoSt
{
MessageType MsgType;
char MsgInfo[MESSAGE_STR_MAX_SIZE];
}MsgInfoSt;
参数 | 描述 |
MsgType | 正消息类型。(必须大于0,小于0的消息类型有特殊的指示作用。) |
MsgInfo | 消息数据。 |
八、消息队列相关函数
1、ftok
(1)声明
key_t ftok(const char *__pathname, int __proj_id)
(2)作用
名称 | 描述 |
英文解释 | convert a pathname and a project identifier to a System V IPC key |
中文解释 | 将路径名和项目标识符转换为 System V IPC 密钥。 |
(3)参数
参数名 | 描述 |
__pathname | 文件路径,通信的进程间需要一样,这样才会生成同一个Key。 |
__proj_id | 项目编号,通信的进程间需要一样,这样才会生成同一个Key。 |
(4)返回值
名称 | 描述 |
成功 | 返回Key。 |
失败 | 返回-1,可以用errno得到具体错误码。 |
?2、msgget
(1)声明
int msgget(key_t __key, int __msgflg)
(2)作用
名称 | 描述 |
英文解释 | get the XSI message queue identifier |
中文解释 | 获取 XSI 消息队列标识符。 |
(3)参数
参数名 | 描述 |
__key | 创建或打开消息队列对象时指定的Key值。 |
__msgflg | 设置的访问权限,取值为一个或多个值的或。举例: (1)000400:读权限。 (2)000200:写权限。 (3)010000:改变控制方式权限。(没用过) 还可以附加其他参数(按位或) (1)IPC_CREAT:消息队列不存在创建,存在就打开。 (2)IPC_EXCL:只有消息队列不存在时,才能创建。不然报错。 (3)IPC_NOWAIT:如果操作需要等待,则直接返回错误。 |
(4)返回值
名称 | 描述 |
成功 | 返回消息队列对象ID。 |
失败 | 返回-1,可以用errno得到具体错误码。 |
?3、msgctl
(1)声明
int msgctl(int __msqid, int __cmd, struct msqid_ds *__buf)
(2)作用
名称 | 描述 |
英文解释 | XSI message control operations |
中文解释 | XSI 消息控制操作。 |
(3)参数
参数名 | 描述 |
__msqid | 消息队列对象ID。 |
__cmd | 对__msqid指定的消息队列执行的命令。支持的命令有: (1)IPC_STAT:取次队列的msqid_ds结构存放到__buf中。 (2)IPC_SET:将ipc_perm结构体的uid、gid、mode和msg_qbytes,从__buf复制到__msqid对应消息队列的msqid_ds结构体中。 (3)从操作系统中删除该消息队列,及其所有数据。 |
__buf | msqid_ds结构体。 |
(4)返回值
名称 | 描述 |
成功 | 返回0。 |
失败 | 返回-1,可以用errno得到具体错误码。 |
?4、msgsnd
(1)声明
int msgsnd(int __msqid, const void *__msgp, size_t __msgsz, int __msgflg)
(2)作用
名称 | 描述 |
英文解释 | XSI message send operation |
中文解释 | XSI 消息发送操作。 |
(3)参数
参数名 | 描述 |
__msqid | 消息队列对象ID。 |
__msgp | 指向自定义结构体MsgInfoSt的结构体指针。 |
__msgsz | 消息的字节大小。 |
__msgflg | 0:阻塞模式。 IPC_NOWAIT:非阻塞模式。 当出现下述情况时: (1)指定的消息队列容量已满。 (2)在系统范围存在太多的消息。 若设置了IPC_NOWAIT会立即返回EAGAIN错误。 |
(4)返回值
名称 | 描述 |
成功 | 返回0。 |
失败 | 返回-1,可以用errno得到具体错误码。 |
(5)errno错误码
错误码 | 错误描述 |
EAGAIN | __msgflg为IPC_NOWAIT时: (1)消息队列已满。 (2)队列中的消息总数等于系统限制值。 (3)队列中的字节总数等于系统限制值。 |
EIDRM | 系统中删除了此队列。 |
EINTR | 捕捉到了一个信号,并从信号处理程序返回。 |
EACCES | 调用进程没有操作权限。 |
EINVAL | __msqid的值不是有效的消息队列标识符,或者mtype的值小于1;或者msgsz的值小于0或大于系统施加的限制。 |
?5、msgrcv
(1)声明
ssize_t msgrcv(int __msqid, void *__msgp, size_t __msgsz, long __msgtyp, int __msgflg)
(2)作用
名称 | 描述 |
英文解释 | XSI message receive operation |
中文解释 | XSI 消息接收操作。 |
(3)参数
参数名 | 描述 |
__msqid | 消息队列对象ID。 |
__msgp | 指向自定义结构体MsgInfoSt的结构体指针。 |
__msgsz | 消息的字节大小。 |
__msgtyp | 接收的消息类型。 等于0:返回队列中的第一个消息。 大于0:返回消息队列中类型值为__msgtyp的第一个消息。 小于0:返回消息队列中类型值小于或等于__msgtyp绝对值中类型值最小的第一个消息。 |
__msgflg | 当消息队列没有收到的消息时会如何操作: 0:阻塞模式。 IPC_NOWAIT:非阻塞模式。 MSG_NOERROR:如果__msgsz小于__msgp返回的长度,数据截断,不会发生报错。 |
(4)返回值
名称 | 描述 |
成功 | 返回0。 |
失败 | 返回-1,可以用errno得到具体错误码。 |
(5)errno错误码
错误码 | 错误描述 |
EAGAIN | __msgflg为IPC_NOWAIT时: (1)消息队列已满。 (2)队列中的消息总数等于系统限制值。 (3)队列中的字节总数等于系统限制值。 |
EIDRM | 系统中删除了此队列。 |
EINTR | 捕捉到了一个信号,并从信号处理程序返回。 |
EACCES | 调用进程没有操作权限。 |
EINVAL | __msqid的值不是有效的消息队列标识符,或者mtype的值小于1;或者msgsz的值小于0或大于系统施加的限制。 |
E2BIG | 接受的消息内容过长。 |
ENOMSG | IPC_NOWAIT是在 msgflg 中指定的,并且消息队列中不存在请求类型的消息。 |
九、测试
1、发送端源码
#include "MyIpc.h"
#include "FileOperate.h"
#define TEST_PATH_NAME "/opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/Process/File2Key.txt"
#define TEST_PROJECT_ID 0
Status main()
{
int RetMsgId = -1;
key_t RetKey = -1;
if (File2Key(TEST_PATH_NAME, TEST_PROJECT_ID, &RetKey))
{
return FailFlag;
}
if (MessageGet(RetKey,0666|IPC_CREAT,&RetMsgId) == FailFlag)
{
return FailFlag;
}
// if (MessageGet(0x0000000A,0666|IPC_CREAT,&RetMsgId) == FailFlag)
// {
// return FailFlag;
// }
MsgInfoSt* SendData = (MsgInfoSt*)MyMalloc(sizeof(MsgInfoSt));
SendData->MsgType = 1;
while (1)
{
LogFormat(Debug,"Send Data : ");
fgets(SendData->MsgInfo,MESSAGE_STR_MAX_SIZE,stdin);
if (MessageSend(RetMsgId,SendData,0) == FailFlag)
{
free(SendData);
SendData = NULL;
return FailFlag;
}
if (strncmp(SendData->MsgInfo,"Finish",6) == 0)
{
break;
}
MySleep(1);
}
free(SendData);
SendData = NULL;
return SuccessFlag;
}
2、接收端源码
#include "MyIpc.h"
#include "FileOperate.h"
#define TEST_PATH_NAME "/opt/Developer/ComputerLanguageStudy/C/DataStructureTestSrc/PublicFunction/Process/File2Key.txt"
#define TEST_PROJECT_ID 0
Status main()
{
int RetMsgId = -1;
key_t RetKey = -1;
if (File2Key(TEST_PATH_NAME, TEST_PROJECT_ID, &RetKey))
{
return FailFlag;
}
if (MessageGet(RetKey,0666|IPC_CREAT,&RetMsgId) == FailFlag)
{
return FailFlag;
}
// if (MessageGet(0x0000000A,0666|IPC_CREAT,&RetMsgId) == FailFlag)
// {
// return FailFlag;
// }
MsgInfoSt* RecDaya = (MsgInfoSt*)MyMalloc(sizeof(MsgInfoSt));
RecDaya->MsgType = 1;
while (1)
{
if (MessageRcv(RetMsgId,RecDaya,MESSAGE_STR_MAX_SIZE,1,0) == FailFlag)
{
free(RecDaya);
RecDaya = NULL;
return FailFlag;
}
if (strncmp(RecDaya->MsgInfo,"Finish",6) == 0)
{
break;
}
}
free(RecDaya);
RecDaya = NULL;
struct msqid_ds* QueueDesc = (struct msqid_ds*)MyMalloc(sizeof(struct msqid_ds));
if (MessageCtl(RetMsgId,IPC_STAT,QueueDesc) == FailFlag)//获取消息队列相关信息
{
free(QueueDesc);
QueueDesc = NULL;
return FailFlag;
}
PrintfMsgQueueIdDescInfo(QueueDesc,Debug);
if (MessageCtl(RetMsgId,IPC_RMID,0) == FailFlag)//关闭消息队列
{
free(QueueDesc);
QueueDesc = NULL;
return FailFlag;
}
free(QueueDesc);
QueueDesc = NULL;
return SuccessFlag;
}
3、测试效果
(1)发送端会话
[gbase@czg2 Process]$ ./TestQueueSend
2023-12-15 14:42:26-P[95243]-T[0]-[Debug]-File2Key : OK, RetKey : c320.
2023-12-15 14:42:26-P[95243]-T[0]-[Debug]-MessageGet : OK.
2023-12-15 14:42:26-P[95243]-T[0]-[Debug]-Send Data : Sunshine
2023-12-15 14:42:29-P[95243]-T[0]-[Debug]-MessageSend : OK, MsgSize : 9, MsgType : 1, MsgInfo : 'Sunshine
'.
2023-12-15 14:42:30-P[95243]-T[0]-[Debug]-MySleep : OK, Seconds : 1.
2023-12-15 14:42:30-P[95243]-T[0]-[Debug]-Send Data : Moon
2023-12-15 14:42:38-P[95243]-T[0]-[Debug]-MessageSend : OK, MsgSize : 5, MsgType : 1, MsgInfo : 'Moon
'.
2023-12-15 14:42:39-P[95243]-T[0]-[Debug]-MySleep : OK, Seconds : 1.
2023-12-15 14:42:39-P[95243]-T[0]-[Debug]-Send Data : Finish
2023-12-15 14:42:43-P[95243]-T[0]-[Debug]-MessageSend : OK, MsgSize : 7, MsgType : 1, MsgInfo : 'Finish
'.
(2)接收端会话
[gbase@czg2 Process]$ ./TestQueueReceive
2023-12-15 14:41:46-P[95103]-T[0]-[Debug]-File2Key : OK, RetKey : c320.
2023-12-15 14:41:46-P[95103]-T[0]-[Debug]-MessageGet : OK.
2023-12-15 14:42:05-P[95103]-T[0]-[Debug]-MessageRcv : OK, MsgSize : 14, MsgType : 1, MsgInfo : '大�unshine
'.
2023-12-15 14:42:13-P[95103]-T[0]-[Debug]-MessageRcv : OK, MsgSize : 7, MsgType : 1, MsgInfo : 'Finish
'.
2023-12-15 14:42:13-P[95103]-T[0]-[Debug]-MessageCtl : OK.
2023-12-15 14:42:13-P[95103]-T[0]-[Debug]-P MsgQidDesc :
time of last msgsnd command : 1702622533
time of last msgrcv command : 1702622533
time of last change : 1702622501
current number of bytes on queue : 0
number of messages currently on queue : 0
max number of bytes allowed on queue : 16384
pid of last msgsnd() : 95070
pid of last msgrcv() : 95103
2023-12-15 14:42:13-P[95103]-T[0]-[Debug]-Decimal2Binary : OK, DecimalVal :438, RetBinary : 00000000000000000000000110110110.
2023-12-15 14:42:13-P[95103]-T[0]-[Debug]-P IpcPerm :
Key : c320
Owner's user ID : 1001
Owner's group ID : 1001
Creator's user ID : 1001
Creator's group ID : 1001
Read/write permission : 00000000000000000000000110110110
Sequence number : 0
2023-12-15 14:42:13-P[95103]-T[0]-[Debug]-MessageCtl : OK.
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!