Linux多进程程序调试实例(五)--消息队列

消息队列

介绍

  • 消息队列是一种进程间的通信方式,支持一个进程向另一个进程发送数据块。每一个数据块被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。
  • 通过发送消息来避免命名管道的同步和阻塞问题,每个数据块都有一个最大长度的限制
    • 在Linux中,使用 MSGMAX 和 MSGMNB 来限制一条消息的最大长度和一个队列的最大长度。

函数

1. msgget() 函数

  • 函数原型
int msgget(key_t key, int msgflg);
  • 函数介绍
    • 和其他IPC机制一致,程序必须提供一个键来命名某个特定的消息队列;
    • msgflag 是一个权限标志,表示消息队列的访问权限,和文件的访问权限是一样的;
      • msgflag 可以于 IPC_CREAT 做或操作,表示当 key 所命名的消息队列不存在时创建一个消息队列,如果对应的消息队列存在,则 IPC_CREAT 标志会被忽略。
    • 该函数会返回一个以 key 命名的消息队列的标识符,该标识符为非零整数,如果失败会返回-1.

2. msgsnd() 函数

  • 函数原型
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
struct msg_st
{
    long int length;
    char text[BUFSIZ];
};
  • 函数介绍
    • msgid 是 msgget 函数返回的消息队列标识符
    • msg_ptr 是一个指向准备发送消息的指针,值得注意的是发送消息的数据结构是有一定要求的
      • 该消息的结构体必须以一个长整型成员变量开头,接收函数将要用这个成员来确定消息的类型
      • 定义的格式如上述中的 msg_st 所示,其中第二行的char 数组存储的是要传输的数据
    • msgflag 用于控制当前消息队列或者队列消息到达系统范围限制时将要发生的事情

3. msgrcv()函数

  • 函数原型
ssize_t msgrcv (int __msqid, void *__msgp, size_t __msgsz,
		       long int __msgtyp, int __msgflg);
  • 函数介绍
    • msgid、msg_ptr、msg_st 的作用等同于 msgsnd() 函数里的参数;
    • msgtype 是一种简单的接收优先级。如果该值为 0,表示获取队列中的第一个消息。如果这个值大于 0,则去获取具有相同消息类型的第一个消息。小于 0,则获取类型等于或者小于该值的绝对值的第一个消息;
    • msgflag 用于控制当前队列中没有相应类型的消息可以接受时将发生的事情;
    • 该函数调用成功时,返回放在接受缓冲区中的字节数,消息被复制到 msg_ptr 指向的用户分配的缓冲区中,然后删除消息队列中的对应消息,失败则返回 -1.

4. msgctl()

  • 函数原型
int msgctl (int __msqid, int __cmd, struct msqid_ds *__buf);
struct msgid_ds
{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
};
  • 函数介绍
    • command 是将要采取的动作,有三个值
      • IPC_STAT: 把msg_id结构中的数据设置为消息消息队列的当前关联值,即用消息队列的当前关联值覆盖 msgid_ds 的值
      • IPC_SET:如果进程有足够的权限,就把消息队列的当前关联值设置为 msgid_ds 结构体中给出的值
      • IPC_RMID:删除消息队列
    • buf 是指向 mgsid_ds 结构的指针,它指向消息队列模式和访问权限的结构
    • 函数执行成功是返回0,失败时返回-1

5. 代码

// msgsend.cpp
#include<cstdio>
#include<cstdlib>
#include<sys/msg.h>
#include<errno.h>
#include<cstring>

struct msg_st
{
    int type;
    char text[BUFSIZ];
};

int main()
{
    int msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(-1 == msgid) {
        fprintf(stderr, "Msgget failed with error:%d", errno);
        exit(EXIT_FAILURE);
    }

    msg_st data;
    char buffer[BUFSIZ] = {0};
    while(1){
        printf("Input text: ");
        fgets(buffer, BUFSIZ, stdin);
        data.type = 1;
        strcpy(data.text, buffer);
        if(-1 == msgsnd(msgid, (void*)&data,BUFSIZ, 0)){
            fprintf(stderr, "msgsnd failed\n");
            exit(EXIT_FAILURE);
        }

        if(0 == strncmp(buffer,"end", 3)){
            break;
        }
    }

    return 0;
}
#include<cstdio>
#include<cstdlib>
#include<sys/msg.h>
#include<errno.h>
#include<cstring>

struct msg_st
{
    int length;
    char text[BUFSIZ];
};

int main()
{
    int msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    if(-1 == msgid) {
        fprintf(stderr, "Msgget failed with error:%d", errno);
        exit(EXIT_FAILURE);
    }

    msg_st data;
    int len = 0;
    while(1){
        if(-1 == msgrcv(msgid, (void*)&data, BUFSIZ, len, 0)){
            fprintf(stderr, "msgrcv failed with errno:%d\n",errno);
            exit(EXIT_FAILURE);
        }
        printf("you receive: %s\n",data.text);
        if(0 == strncmp(data.text, "end", 3)){
            break;
        }
    }

    if(-1 == msgctl(msgid, IPC_RMID, 0)) {
        fprintf(stderr, "msgclt(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);
}

6.调试过程

上述两个进程都是普通的进程运行,按照正常的 GDB 步骤运行调试即可。

posted @ 2023-02-03 18:06  王清河  阅读(134)  评论(0编辑  收藏  举报