消息队列(创建消息队列,如何发送消息,接收消息,删除消息队列)

本文详细介绍了Linux系统中的IPC对象,特别是消息队列的原理、创建方法(如使用ftok和msgget)、发送和接收消息(msgsnd和msgrcv),以及消息队列的控制(msgctl)。通过实例展示了如何在程序中实现进程间的高效通信。
摘要由CSDN通过智能技术生成

一、IPC对象

除了最原始的进程间通信方式信号、无名管道和有名管道外,还有三种进程间通信方式,这 三种方式称之为IPC对象

IPC对象分类:消息队列共享内存信号灯集

IPC对象也是在内核空间开辟区域,每一种IPC对象创建好之后都会将其设置为全局,并且会给其分配一个编号,只要找到唯一的这个编号就可以进行通信,所以不相关的进程可以通 过IPC对象通信

IPC对象创建好之后,会在当前系统中可见,只要不删除或者不关闭系统,就会一直存在

查看已经创建的IPC对象:

 ipcs 查看当前系统中所有创建的IPC对象

 ipcs ‐q 查看创建的消息队列

 ipcs ‐m 查看创建的共享内存

 ipcs ‐s 查看信号量

 ipcrm 删除IPC对象

 例如:ipcrm ‐q msqid 删除标号为msqid的消息队列

 

 

二、消息队列概述 

2.1 消息队列的概念

消息队列是消息的链表,存放在内存中,由内核维护

消息队列的特点

  1. 消息队列中的消息是有类型的。
  2. 消息队列中的消息是有格式的。
  3. 消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以 按消息的类型读取。
  4. 消息队列允许一个或多个进程向它写入或者读取消息。
  5. 与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删 除。
  6. 每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
  7. 只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队 列,消息队列会一直存在于系统中。

 在ubuntu 12.04中消息队列限制值如下:

每个消息内容最多为8K字节 每个消息队列容量最多为16K字节

系统中消息队列个数最多为1609个 系统中消息个数最多为16384个

System V提供的IPC通信机制需要一个key值,通过key值就可在系统内获得一个唯一的消 息队列标识符。

key值可以是人为指定的,也可以通过ftok函数获得。 如果多个进程想通过IPC对象通信,则必须找到唯一的标识,而唯一的标识是由key决定 的,所以只要key知道,则就可以实现多个进程通信

2.2 ftok函数 

 #include <sys/types.h>
 #include <sys/ipc.h>
  key_t ftok(const char *pathname, int proj_id);

 功能:
    通过文件名和目标值共同创造一个键值并返回值
 参数:
     pathname:任意一个文件名(文件名或者目录名)
     proj_id:目标值,范围一般是0~127
 返回值:
     成功:键值
     失败:‐1
 如果使用ftok函数获取键值,得到的键值是由ftok的第一个
 参数对应文件的信息和第二个参数一起决定的

案例

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>

int main(int argc, char const *argv[]) {
    key_t mykey;

    // 获取IPC键值
    mykey = ftok(".", 100);
    if (mykey == -1) {
        perror("fail to ftok");
        exit(1);
    }

    printf("key = %#x\n", mykey);
    return 0;
}

执行结果

三、消息队列的操作 

3.1 创建消息队列 -- msgget()

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 int msgget(key_t key, int msgflg);
  功能:
    创建一个消息队列,得到消息队列的id
  参数:
    key:键值,唯一的键值确定唯一的消息队列 
 
方法1:任意指定一个数
方法2:使用ftok函数获取键值
msgflg:消息队列的访问权限,一般设置为 IPC_CREAT | IPC_EXCL | 0777 或者 IPC_CREAT | 0777

返回值:
    成功:消息队列的id
    失败:-1

使用shell命令操作消息队列:

查看消息队列

         ipcs ‐q

删除消息队列 

         ipcrm ‐q  msqid

 案例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main(int argc, char const *argv[]) {
    // 通过ftok函数获取ipc键值
    key_t mykey;
    if ((mykey = ftok(".", 100)) == -1) {
        perror("fail to ftok");
        exit(1);
    }

    printf("mykey = %#x\n", mykey);

    // 通过msgget函数创建一个消息队列
    int msqid;
    if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1) {
        perror("fail to msgget");
        exit(1);
    }

    printf("msqid = %d\n", msqid);

    system("ipcs -q");

    return 0;
}

 执行结果

 3.2 发送消息 -- msgsnd( ) 

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
  int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
 功能:向指定的消息队列发送数据(写操作)
 参数:
   msqid:消息队列的id
   msgp:要写入的数据,需要自己定义结构体

  struct struct_name
  {
  long mtype; //消息的编号,必须大于0
  char mtext[128]; //消息正文,可以定义多个成员
  }

  msgsz:消息正文的大小,不包括消息的编号长度
  msgflg:标志位
     0 阻塞
     IPC_NOWAIT 非阻塞
  返回值:
      成功:0
      失败:‐1

在使用msgsnd()函数时,需要注意以下几点:

  • 确保msqid有效,且当前进程具有相应的写权限。
  • msgp所指向的结构体大小应小于或等于消息队列的最大长度。
  • 如果设置了IPC_NOWAIT标志,当消息队列已满时,函数不会阻塞,而是立即返回-1,并将errno设置为EAGAIN
  • 如果设置了IPC_NOERROR标志,当发送的消息大小超过消息队列允许的最大长度时,函数不会报错,而是直接截断消息,并继续执行

案例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define N 128

typedef struct {
    long msg_type; // 消息类型,必须在结构体的第一个位置并且类型必须是long
    char msg_text[N]; // 消息正文,也可以有多个成员并且类型也可以是任意
} MSG;

#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))

int main(int argc, char const *argv[]) {
    // 使用ftok函数获取键值
    key_t key;
    if ((key = ftok(".", 100)) == -1) {
        perror("fail to ftok");
        exit(1);
    }

    // 使用msgget函数创建一个消息队列
    int msgid;
    if ((msgid = msgget(key, IPC_CREAT | 0777)) == -1) {
        perror("fail to msgget");
        exit(1);
    }

    system("ipcs -q");

    // 使用msgsnd函数向消息队列中发送数据(写操作)
    MSG msg1 = {1, "hello world"};
    MSG msg2 = {4, "nihao beijing"};
    MSG msg3 = {2, "hello kitty"};
    MSG msg4 = {3, "welcome to hunan"};

    if (msgsnd(msgid, &msg1, MSGTEXT_SIZE, 0) == -1) {
        perror("fail to msgsnd");
        exit(1);
    }

    if (msgsnd(msgid, &msg2, MSGTEXT_SIZE, 0) == -1) {
        perror("fail to msgsnd");
        exit(1);
    }

    if (msgsnd(msgid, &msg3, MSGTEXT_SIZE, 0) == -1) {
        perror("fail to msgsnd");
        exit(1);
    }

    if (msgsnd(msgid, &msg4, MSGTEXT_SIZE, 0) == -1) {
        perror("fail to msgsnd");
        exit(1);
    }

    return 0;
}

运行结果

执行了两次

3.3 接收消息 -- msgrcv( ) 

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
  ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,
  long msgtyp, int msgflg);
 功能:
    从消息队列中接收数据(读操作),接收的数据会从消息队列中删除
 参数:
    msqid:消息队列id
    msgp:保存接收到的数据的结构体
    
struct struct_name
 {
  long mtype; //消息的编号,必须大于0
  char mtext[128]; //消息正文,可以定义多个成员
 }

  msgsz:消息正文的大小
  msgtyp:设置要接收哪个消息

   0 按照写入消息队列的顺序依次读取
  >0 只读取消息队列中消息编号为当前参数的第一个消息
  <0 只读取消息队列中小于等于当前参数的绝对中内最小的第一个消息
  msgflg:标志位
      0 阻塞
      IPC_NOWAIT 非阻塞
  返回值:
     成功:接收到的消息正文的长度
     失败:‐1

案例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define N 128

typedef struct {
    long msg_type;
    char msg_text[N];
} MSG;

#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))

int main(int argc, char const *argv[]) {
    // 使用ftok函数获取键值
    key_t key;
    if ((key = ftok(".", 100)) == -1) {
        perror("fail to ftok");
        exit(1);
    }

    // 使用msgget函数创建一个消息队列
    int msgid;
    if ((msgid = msgget(key, IPC_CREAT | 0777)) == -1) {
        perror("fail to msgget");
        exit(1);
    }

    // 系统命令查看消息队列
    system("ipcs -q");

    // 通过msgrcv函数接收消息队列中的信息(读操作)
    // 如果没有指定消息类型,msgrcv函数会阻塞等待
    MSG msg;

    // 如果第四个参数为0,则按照先进先出的方式读取数据
    // if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 0, 0) == -1)
    // 如果第四个参数为>0,则获取当前值得消息类型的数据
    // if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 2, 0) == -1)
    // 如果第四个参数为<0,则获取当前值得绝对值内消息类型最小的数据
    if (msgrcv(msgid, &msg, MSGTEXT_SIZE, -3, 0) == -1) {
        perror("fail to msgrcv");
        exit(1);
    }

    printf("recv_msg = %s\n", msg.msg_text);

    // 系统命令查看消息队列
    system("ipcs -q");

    return 0;

3.4 消息队列的控制

  #include <sys/types.h>
  #include <sys/ipc.h>
  #include <sys/msg.h>
  int msgctl(int msqid, int cmd, struct msqid_ds *buf);
 功能:
    设置或者获取消息队列的信息
 参数:
 msqid:指定的消息队列的id
 cmd:具体操作的指令
 IPC_SET 设置属性
 IPC_STAT 获取属性
 IPC_RMID 删除消息队列
 msqid_ds:设置或者获取消息队列的属性
 返回值:
      成功:0
      失败:‐1

 删除一个消息队列试试

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main(int argc, char const *argv[])
{
    // 使用ftok函数获取键值
    key_t key;
    if((key = ftok(".", 100)) == ‐1)
    {
        perror("fail to ftok");
        exit(1);
    }

    // 使用msgget函数创建一个消息队列
    int msgid;
    if((msgid = msgget(key, IPC_CREAT | 0777)) == ‐1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msgid = %d\n", msgid);
    system("ipcs ‐q");

    // 通过msgctl函数删除消息队列
    if(msgctl(msgid, IPC_RMID, NULL) == ‐1)
    {
        perror("fail to msgctl");
        exit(1);
    }

    system("ipcs ‐q");

    return 0;
}

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值