【genius_platform软件平台开发】第一百讲:linux系统的进程间通信之消息队列操作接口(msgget、msgctl、msgsnd、msgrcv)及demo代码

1. XSI(Unix System Interface and Headers):

代表了一种Unix系统的标准。
XSI IPC,依托标识符和键来实现的,如同管道靠文件描述符来实现一样
三种IPC我们称作XSI IPC,即消息队列信号量共享内存

  • IPC标识
    内核为每个进程间通信维护一个结构体形式的IPC对象。该对象可以通过一个非负整数的IPC标识来引用。

  • 注意
    与文件描述符不同,文件描述符总是找当前系统中可用的最小的数,而IPC标识是持续加一的。

1.1 IPC键值

IPC标识是IPC对象在内核空间中的唯一性标识ID;
IPC键值是IPC对象在用户空间中的唯一性标识ID。

1)无论何时,只要创建IPC对象,就必须指定一个键值;
2)键值的数据类型在sys/types.h头文件中被定义为key_t,其原始类型就是长整型long

1.2 IPC对象的访问

IPC对象是全局对象
可用ipcs、ipcrm等命令查看和删除
每个IPC对象都由get函数创建

msgget、shmget、semget

调用get函数的时候必须指定关键字key

IPC对象的权限和所有者结构体
XSI IPC为每一个IPC结构设置了一个ipc_perm结构。
该结构规定了权限和所有者,至少包括下列成员:

#include <sys/ipc.h>

struct ipc_perm
{
    uid_t uid;   /*owner`s effective user id*/
    gid_t gid;   /*owner`s effective group id*/
    uid_t cuid;  /*creator`s effective user id*/
    gid_t cgid;  /*creator`s effective group id*/
    mode_t mode; /*access modes*/
    ...
};

每种实现在其ipc_perm结构中会包括另外一些成员。
如果想要查看完整定义,参见<sys/ipc.h>
对于IPC来说,消息队列就是内核中一个消息的链表
可以把消息看作是一个记录,具有特定的格式以及特定的优先级,对消息队列有写权限的进程可以向消息队列中按照一定的规则添加消息;对消息队列有读权限的进程则可以从消息队列中读走消息

2 类型

  • POSIX消息队列
  • 系统V(System V)消息队列

目前主要有两种类型的消息队列:POSIX消息队列以及系统V(System V)消息队列系统V消息队列目前仍大量被使用,考虑到程序的可移植性,新开发的应用程序应尽量使用POSIX消息队列

3 消息

  • 在进程间通信中,用户进程将数据传输到内核后,内核重新添加一些比如用户id、组id、读写进程的id和优先级等相关信息后打包成一个数据称为消息。

4 特性

  • 允许一个或者多个进程往消息队列中写消息和读消息,但一个消息只能被一个进程读取,读取完毕后就会自动删除
    消息队列具有一定的FIFO(First In First Out:先入先出)的特性,消息可以按照顺序发送到队列中,也可以按不同的方式从队列中读取。每一个消息队列在内核中用一个唯一的IPC标识ID表示。
    消息队列属性结构体
    每一个消息队列都有一个msqid_ds结构体:
#include <sys/msg.h>
struct msqid_ds
{
    struct ipc_perm msg_perm; 
    msgqnum_t msg_qnum;     /* # of message on queue*/
    msglen_t msg_qbytes;    /* max # of bytes on queue*/
    pid_t msg_lspid;        /* pid of last msgsnd*/
    pid_t msg_lrpid;        /* pid of last msgrcv*/
    time_t msg_stime;       /* last-msgsnd() time*/
    time_t msg_rtime;       /* last-msgrcv() time*/
    time_t msg_ctime;       /* last-change time*/
    ...
};

5 接口

5.1 msgget函数原型

在这里插入图片描述
如果用msgget创建了一个新的消息队列对象时,则msqid_ds结构成员变量的值设置如下:
msg_qnum、msg_lspid、msg_lrpid、 msg_stime、msg_rtime设置为0。
msg_ctime设置为当前时间。
msg_qbytes设成系统的限制值。
msgflg的读写权限写入msg_perm.mode中。
msg_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cuid成员被设置成当前进程的有效组ID。

5.2 msgctl函数原型

在这里插入图片描述

5.3 msgsnd函数原型

在这里插入图片描述
msgsnd()为阻塞函数,当消息队列容量满或消息个数满会阻塞。消息队列已被删除,则返回EIDRM错误;被信号中断返回E_INTR错误

如果设置IPC_NOWAIT消息队列满个数满时会返回-1,并且置EAGAIN错误。

  • msgsnd()解除阻塞的条件有以下三个条件:

① 不满足消息队列满或个数满两个条件,即消息队列中有容纳该消息的空间。

msqid代表的消息队列被删除。

③ 调用msgsnd函数的进程被信号中断。

5.4 msgrcv函数原型

在这里插入图片描述

  • msgrcv()解除阻塞的条件有以下三个:

① 消息队列中有了满足条件的消息。

② msqid代表的消息队列被删除。

③ 调用msgrcv()的进程被信号中断。

3.demo示例

msgctl.c源代码

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <sys/ipc.h>

#include <sys/msg.h>

#include <error.h>

#define TEXT_SIZE  512

struct msgbuf

{

    long mtype ;

    char mtext[TEXT_SIZE] ;

}  ;

int main(int argc, char **argv)

{

    int msqid ;

    struct msqid_ds info ;

    struct msgbuf buf ;

    struct msgbuf buf1 ;

    int flag ;

    int sendlength, recvlength ;

 

    msqid = msgget( IPC_PRIVATE, 0666 ) ;

    if ( msqid < 0 )

    {

        perror("get ipc_id error") ;

        return -1 ;

    }

 

    buf.mtype = 1 ;

    strcpy(buf.mtext, "happy new year!") ;

    sendlength = sizeof(struct msgbuf) - sizeof(long) ;

    flag = msgsnd( msqid, &buf, sendlength , 0 ) ;

    if ( flag < 0 )

    {

        perror("send message error") ;

        return -1 ;

    }

    buf.mtype = 3 ;

    strcpy(buf.mtext, "good bye!") ;

    sendlength = sizeof(struct msgbuf) - sizeof(long) ;

    flag = msgsnd( msqid, &buf, sendlength , 0 ) ;

    if ( flag < 0 )

    {

        perror("send message error") ;

        return -1 ;

    }

 

    flag = msgctl( msqid, IPC_STAT, &info ) ;

    if ( flag < 0 )

    {

        perror("get message status error") ;

        return -1 ;

    }

    printf("uid:%d, gid = %d, cuid = %d, cgid= %d\n" ,

        info.msg_perm.uid,  info.msg_perm.gid,  info.msg_perm.cuid,  info.msg_perm.cgid  ) ;

    printf("read-write:%03o, cbytes = %lu, qnum = %lu, qbytes= %lu\n" ,

        info.msg_perm.mode&0777, info.msg_cbytes, info.msg_qnum, info.msg_qbytes ) ;

    system("ipcs -q") ;

    recvlength = sizeof(struct msgbuf) - sizeof(long) ;

    memset(&buf1, 0x00, sizeof(struct msgbuf)) ;

 

    flag = msgrcv( msqid, &buf1, recvlength ,3,0 ) ;

    if ( flag < 0 )

    {

        perror("recv message error") ;

        return -1 ;

    }

    printf("type=%d, message=%s\n", buf1.mtype, buf1.mtext) ;

 

    flag = msgctl( msqid, IPC_RMID,NULL) ;

    if ( flag < 0 )

    {

        perror("rm message queue error") ;

        return -1 ;

    }

    system("ipcs -q") ;

 

   return 0 ;

}

编译 gcc msgctl.c –o msgctl。

执行 ./msg,执行结果如下:

uid:1008, gid = 1003, cuid = 1008, cgid= 1003
read-write:666, cbytes = 1024, qnum = 2, qbytes= 163840
  • ipc-msgque-send.cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/msg.h>

typedef struct
{
    long msg_type; //消息类型
    // 下面是自己需要传递的数据
    int start; // 自定义消息
    int end;    // 自定义消息
}msg_t;

/*
 * 往消息队列中发送消息
 */
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("Lack arguments %s\n", argv[0]);
        exit(1);
    }

    key_t key = atoi(argv[1]);
    printf("key: %d\n", key);

    // 创建消息队列
    int msq_id;
    if ((msq_id = msgget(key, IPC_CREAT | IPC_EXCL | 0777/*读写权限*/)) < 0)
    {
        perror("msgget error");
    }
    printf("msq id: %d\n", msq_id);

    // 定义要发送的消息
    msg_t arr[5] = { /*{0, 0, 0},注意0号type不被插入*/ {1, 1, 100}, {2, 2, 200}, {3, 3, 300}, {4, 4, 400}, {5, 5, 500} };

    // 发送消息到消息队列
    for (int i = 0; i < 5; ++i)
    {
        if (msgsnd(msq_id, (const void *)&arr[i], sizeof(msg_t) - sizeof(long), IPC_NOWAIT) < 0)
        {
            printf("msgsnd error i:%d\n", i);
        }
    }

    // 发送后去获得消息队列中消息的总数
    struct msqid_ds ds;
    if (msgctl(msq_id, IPC_STAT, &ds) < 0)
    {
        perror("msgctl error");
    }
    /* msg_qnum:number of messages currently on queue */
    printf("total message: %ld\n", ds.msg_qnum);

    exit(0);
}


  • ipc-msgque-recv.cpp
#include <sys/msg.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    long msg_type; //消息类型
    // 下面是自己需要传递的数据
    int start; // 自定义消息
    int end;    // 自定义消息
}msg_t;

int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        printf("Lack arguments: %s\n", argv[0]);
        exit(1);
    }

    key_t key = atoi(argv[1]);
    long type = atoi(argv[2]);

    // 获得指定的消息队列
    int msq_id;
    if ((msq_id = msgget(key, 0777)) < 0)
    {
        perror("msgget error");
        exit(1);
    }
    printf("msg id: %d\n", msq_id);

    msg_t m;
    if ((msgrcv(msq_id, &m, sizeof(msg_t) - sizeof(long), type, IPC_NOWAIT)) < 0)
    {
        perror("msgrcv error");
        exit(1);
    }
    else
    {
        printf("type: %ld start: %d end: %d\n", m.msg_type, m.start, m.end);
    }

    exit(0);
}

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

隨意的風

如果你觉得有帮助,期待你的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值