2024-02-27-物联网系统编程(6-消息队列)

6.消息队列

6.1 IPC对象

​ 除了最原始的进程间通信方式信号、无名管道和有名管道外,还有三种进程间通信方式,这三种方式称之为IPC对象: 消息队列、共享内存、信号灯集

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

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

查看系统中已经创建的IPC

ipcs    # 查看当前系统中所有创建的IPC对象
ipcs -q # 查看消息队列
ipcs -m # 查看创建的共享内存
ipcs -s # 查看信号量

image-20240227084001931

ipcrm # 删除ipc对象
如
ipcrm -q msqid # 删除标号为msqid的消息队列

6.2 消息队列概述

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

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

key 值可以人为指定,也可以通过 ftok 函数获得。

6.2.1 ftok函数

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj id);
功能:获得项目相关的唯一的 IPC 键值;
参数:
	路径名pathname:
	prej_id:项目 ID,非0整数(只有低 8 位有效),范围 0 ~ 127;
返回值:
	成功:返回 key 值;
	失败:返回 -1
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include  <stdlib.h>

int main(int argc, char const *argv[])
{
    key_t mykey;
    if ((mykey = ftok(".", 100))== -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n",mykey);
    
    return 0;
}

输出结果

mykey = 0x640516aa

6.3 消息队列的操作

6.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;
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    key_t mykey;
    if ((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    int msqid;
    if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

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

    return 0;
}

输出结果

mykey = 0x640516aa
msqid = 0

image-20240227090946435

6.3.2 发送信息 - msgsnd() 函数

#include<sys/msg. h>
int msgsnd(int msgid, const void *msgp,size_t msgsz, int msgflg);
功能:
	将新消息添加到消息队列;
参数:
	msgid:消息队列的标识符;
	msgp:待发送消息结构体的地址,需要写入的数据,自己定义结构体;
		struct struct_name{
            long mtype; // 消息编号,必须大于0
            char mtext[128]; // 消息正文,可以定义多个成员
        }
	msgsz: 消息正文的字节数,不包括消息编号的长度;
	msgflg: 函数的控制属性
		0: msgsnd,调用阻塞直到条件满足为止。
		IPC_NOWAIT: 非阻塞,若消息没有立即发送则调用该函数的进程会立即返回。
返回值:
	成功: 0;
	失败: 返回-1。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    // 第一个位置必须是long,代表消息类型
    long msg_type;
    char msg_text[128]; // 消息正文,可以多个成员并且类型也可以随意
} MSG;

// 定义消息正文的大小
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))

int main(int argc, char const *argv[])
{
    key_t mykey;
    if ((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    int msqid;
    if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msqid = %d\n", msqid);
    system("ipcs -q");

    // 使用msgsnd向消息队列中写入数据
    MSG msg1 = {1, "hello world"};
    MSG msg2 = {1, "i love c"};
    MSG msg3 = {1, "i love hello kitty"};
    if ((msgsnd(msqid, &msg1, MSGTEXT_SIZE, 0)) == -1)
    {
        perror("fail to send message");
    }
    system("ipcs -q");
    if ((msgsnd(msqid, &msg2, MSGTEXT_SIZE, 0)) == -1)
    {
        perror("fail to send message");
    }
    if ((msgsnd(msqid, &msg3, MSGTEXT_SIZE, 0)) == -1)
    {
        perror("fail to send message");
    }
    system("ipcs -q");
    return 0;
}

输出结果

mykey = 0x640516aa
msqid = 2

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        0            0           


--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        128          1           


--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        384          3  

image-20240227093933369

注意:消息队列结束后,消息仍然存在于消息队列中,而如果是管道,就会消失。

6.3.3 接收数据 - msgrev() 函数

#include <sys/msg.h>
ssize_t msgrcv(int msgid, void *msgp,size_t msgsz, long msgtyp, int msgflg)
功能: 从标识符为msgid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除;
参数:
	msgid: 消息队列的标识符,代表要从哪个消息列中获取消息。
	msgp: 存放消息结构体的地址。
	msgsz: 消息正文的字节数。
msgtyp: 消息的类型、可以有以下几种类型
	msgtyp = 0: 返回队列中的第一个消息
	msgtyp > 0: 返回队列中消息类型为msgtyp的消息
	msgtyp < 0: 返回队列中消息类型值小于或等于 msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。
注意:
	若消息队列中有多种类型的消息,msscv,获取消息的时候按消息类型获取,不是先进先出的。
	在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。
msgflg:函数的控制属性
	0: msgrev 调用阻塞直到接收消息成功为止。
	MSG_NOERROR:若返回的消息字节数比 nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。
	IPC NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1.
返回值:
	成功: 返回读取消息的长度;
	失败: 返回-1 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    // 第一个位置必须是long,代表消息类型
    long msg_type;
    char msg_text[128]; // 消息正文,可以多个成员并且类型也可以随意
} MSG;

// 定义消息正文的大小
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))

int main(int argc, char const *argv[])
{
    key_t mykey;
    if ((mykey = ftok(".", 100)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("mykey = %#x\n", mykey);

    int msqid;
    if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msqid = %d\n", msqid);
    system("ipcs -q");

    // // 使用msgsnd向消息队列中写入数据
    // MSG msg1 = {1, "hello world"};
    // MSG msg2 = {1, "i love c"};
    // MSG msg3 = {1, "i love hello kitty"};
    // if ((msgsnd(msqid, &msg1, MSGTEXT_SIZE, 0)) == -1)
    // {
    //     perror("fail to send message");
    // }
    // system("ipcs -q");
    // if ((msgsnd(msqid, &msg2, MSGTEXT_SIZE, 0)) == -1)
    // {
    //     perror("fail to send message");
    // }
    // if ((msgsnd(msqid, &msg3, MSGTEXT_SIZE, 0)) == -1)
    // {
    //     perror("fail to send message");
    // }
    MSG msg;
    // 如果第四个数据位0,则按照先进先出的方式读取数据
    // 如果第四个数据大于0,则获取当前值的消息类型相同的数据
    // 如果第四个参数小于0,则获取当前值的绝对值内消息类型最小的数据
    if (msgrcv(msqid, &msg, MSGTEXT_SIZE, 1, 0) == -1)
    {
        perror("fail to msgrcv");
        exit(1);
    }
    printf("recv_msg = %s\n", msg.msg_text);
    system("ipcs -q");

    return 0;
}

输出结果

mykey = 0x640516aa
msqid = 2

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        384          3           

recv_msg = hello world

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        256          2     

6.3.4 消息队列的控制 - msgctl() 函数

#include<sys/msg. h>
int msgetl(int msqid int cmd, struct msqid_ds *buf)
功能: 对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列;
参数:
	msqid: 消息队列的标识符
	cmd: 函数功能的控制
        IPC_RMID: 删除由 msqid 指示的消息队列,将它从系统中删除并破坏相关数据结构;
        IPC_STAT: 将 msqid 相关的数据结构中各个元素的当前值存入到由buf指向的结构中;
        IPC_SET:  将 msqid 相关的数据结构中的元素设置为由 buf指向的结构中的对应值。
	buf: msqid_ds,数据类型的地址,用来存放或更改消息队列的属性

返回值:
       成功:返回0
       失败:返回-1
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char const *argv[])
{
    key_t key;
    if ((key = ftok(".", 20)) == -1)
    {
        perror("fail to ftok");
        exit(1);
    }
    printf("key = %#x\n", key);

    int msgid;
    if ((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
    {
        perror("fail to msgget");
        exit(1);
    }

    printf("msgid = %d", msgid);
    system("ipcs -q");

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

    system("ipcs -q");
    return 0;
}

输出结果

key = 0x140516aa

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        256          2           
0x140516aa 3          spider     777        0            0           


--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x640516aa 2          spider     666        256          2    
posted @ 2024-02-27 10:50  Yasuo_Hasaki  阅读(23)  评论(0)    收藏  举报
//雪花飘落效果