Unix环境高级编程-学习-03-XSI-IPC之消息队列

发布时间:2023年12月18日

目录

一、验证环境

二、Linux进程间通信进制

三、XSI-IPC机制

四、IPC相关地操作系统命令

1、ipcs

(1)-a

(2)-l

2、ipcrm

(1)-Q

五、IPC对象的key值和ID值

六、消息队列

七、消息队列相关结构体

1、struct msqid_ds

2、struct ipc_perm

3、MsgInfoSt(自定义)

八、消息队列相关函数

1、ftok

(1)声明

(2)作用

(3)参数

(4)返回值

?2、msgget

(1)声明

(2)作用

(3)参数

(4)返回值

?3、msgctl

(1)声明

(2)作用

(3)参数

(4)返回值

?4、msgsnd

(1)声明

(2)作用

(3)参数

(4)返回值

(5)errno错误码

?5、msgrcv

(1)声明

(2)作用

(3)参数

(4)返回值

(5)errno错误码

九、测试

1、发送端源码

2、接收端源码

3、测试效果

(1)发送端会话

(2)接收端会话


一、验证环境

名称
CPUIntel(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)从操作系统中删除该消息队列,及其所有数据。

__bufmsqid_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

接受的消息内容过长。

ENOMSGIPC_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.

文章来源:https://blog.csdn.net/qq_45111959/article/details/134977022
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。