百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

posix消息队列和systemV消息队列 消息队列 c

liebian365 2024-11-03 15:46 7 浏览 0 评论

一、概述:

消息队列可认为是一个消息链表。有足够写权限的线程可往队列中放置消息,有足够读权限的

线程可从队列中取走消息。posix消息队列和systemV消息队列主要如下差异:

1、一般来说posix的接口要比systemV的简单,但是systemV的可已移植性更好几乎所有的unix系统都支持。

2、对posix消息队列的读总是返回最高优先级的最早消息,对systemV消息队列的读则可以返回

任意指定优先级的消息。

3、当往一个空队列放置一个消息时,posix消息队列允许产生一个信号或者启动一个线程,systemV消息队列则不提供类似的机制。

二、posix消息队列

1、有如下主要函数:

#include <mqueue.h> 
mqd_t mq_open(const char *name, int oflag, /* mode_t mode, struct mq_attr *attr */); 
int mq_close(mqd_t mqdes); 
int mq_unlink(const char *name);
int mq_getattr(mqd_t mqdes, struct mq_attr *attr); 
int mq_setattr(mqd_t mqdes, struct mq_attr *attr, struct mq_attr *oattr); 
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio); 
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio); 
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
1
2
3
4
5
6
7
8
9

mqd_t mq_open(const char name, int oflag, / mode_t mode, struct mq_attr attr /);

功能:创建一个新的消息队列或者打开一个已经存在的消息队列。

返回值:若成功返回消息队列的描述符,失败返回-1

参数:

name : 消息队列的名称(必须以/开头,且后面不能再有/)

oflag: 标志。O_RDONLY,O_WRONLY,O_RDWR 三个中之一,还有可选的选项:O_NONBLOCK,O_CREAT,O_EXCL。

O_RDONLY 只读

O_RDWR 读写

O_WRONLY 只写

O_CREAT 没有该对象则创建

O_EXCL 如果O_CREAT指定,但name不存在,就返回错误

O_NONBLOCK 以非阻塞方式打开消息队列

mode: 权限

S_IWUSR 用户/属主写

S_IRUSR 用户/属主读

S_IWGRP 组成员写

S_IRGRP 组成员读

S_IWOTH 其他用户写

S_IROTH 其他用户读

attr:给新队列指定某些属性,如果为空就使用默认属性。

struct mq_attr{

long mq_flags;//标志,其值为0表示阻塞,O_NONBlOCK表示非阻塞。可用mq_getatter和mq_setattr设置和获取

long mq_maxmsg;//消息队列的消息个数的最大值,只能在创建消息队列的时候设置

long mq_msgsize;//每个消息的最大值,只能在创建消息队列的时候设置

long mq_curmsgs://当前队列的消息个数,可用mq_getatter获取

}

创建消息队列成功后可在/dev/mqueue中查看。

int mq_close(mqd_t mqdes)

功能:关闭一个消息队列,调用之后表示进程不在使用该描述符并不代表消息队列会从系统中删除

参数: mqdes消息队列描述符

返回值:若成功返回0,失败返回-1

int mq_unlink(const char *name);

功能:从系统中删除一个名称为name的消息队列

参数: name 消息队列名称

返回值:若成功返回0,失败返回-1

说明:每个消息队列有一个保存当前打开着描述符数的引用计数器,mq_close会使计数器减一,只用调用mq_unlink并且引用计数器为0时该消息队列才会从系统中删除。

int mq_getattr(mqd_t mqdes, struct mq_attr *attr);

功能: mq_getattr返回队列的四个属性

参数:attr 属性返回到attr指的结构体中

返回值:若成功返回0,失败返回-1

int mq_setattr(mqd_t mqdes, struct mq_attr *attr, struct mq_attr *oattr);

功能: mq_setattr设置队列的mq_flags属性

参数:attr指向新设置属性的结构体,oattr指向返回旧的属性的结构体。

返回值:若成功返回0,失败返回-1

int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio);

功能: 往消息队列中放置一个消息,消息队列已满,mq_send()函数将阻塞,知道有可用空间再次允许放置消息。

如果O_NONBLOCK被指定,满队时mq_send()将不会阻塞,而是返回EAGAIN错误。当一条消息被插入队列时它会被放置在队列中具有相同优先级的所有的消息之后。

参数:mqdes: 消息队列描述符。

msg_ptr:要发送消息的指针。

msg_len:消息长度(不能大于属性值mq_msgsize的值)。

msg_prio:优先级(消息在队列中将按照优先级从大到小的顺序排列消息;数字越大优先级越高。)。

返回值:若成功返回0,失败返回-1

ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *prio);

功能:按优先级从高到低进行接收。即优先级值大的先接收。如果队列为空,mq_receive()函数将阻塞,知道消息队列中有新的消息。

如果O_NONBLOCK被指定,mq_receive()将不会阻塞,而是返回EAGAIN错误。

参数:mqdes: 消息队列描述符。

ptr : 指向接收消息缓冲区

len : ptr指向缓冲区的大小(必须大于等于属性值mq_msgsize的值,否则返回EMSGSIZE错误)

prio: 如果prio不为NULL,那么接收到的消息优先级会被复制到prio指向的位置

返回值:若成功返回接收消息的长度,失败返回-1。

编译要加librt.so -lrt

一个测试的小程序

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <mqueue.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/stat.h>
#define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
void *sendthread(mqd_t mqd)
{
 int rv;
 char *str = "hello posix mq_queue";
 int len = strlen(str);
 rv = mq_send(mqd, str, len, 2);
 if(rv == -1)
 {
 perror("mq_send error");
 }
 printf("send msg =[%s]\n", str);
}
void *recvthread(mqd_t mqd)
{
 int rv;
 char str[1024] = {0};
 unsigned int pro;
 rv = mq_receive(mqd, str, 1024, &pro);
 if(rv == -1)
 {
 perror("mq_receive error");
 }
 printf("recv len = [%d], msg =[%s],pro = [%d]\n",rv, str, pro);
}
int main(int argc, char *argv[])
{
 int rv,flag = 0;
 long maxmsg = 10;
 long msglen = 1024;
 pthread_t pid1,pid2; 
 struct mq_attr attr, new_attr, old_attr;
 attr.mq_maxmsg = maxmsg;
 attr.mq_msgsize = msglen;
 attr.mq_flags = 0;
 mqd_t mqd = mq_open("/mq_test", O_CREAT|O_RDWR, FILE_MODE, &attr);
 if(-1 == mqd)
 {
 perror("mq_open error");
 return 0;
 }
 memset(&new_attr, 0, sizeof(struct mq_attr));
 memset(&old_attr, 0, sizeof(struct mq_attr));
 rv = mq_getattr(mqd, &new_attr);
 if(rv == -1)
 {
 perror("mq_getattr error");
 return 0;
 }
 printf("new_attr.mq_maxmsg = [%ld],new_attr.mq_msgsize= [%ld]\n", new_attr.mq_maxmsg, new_attr.mq_msgsize);
 new_attr.mq_flags = O_NONBLOCK;
 rv = mq_setattr(mqd, &new_attr, &old_attr);
 if(rv == -1)
 {
 perror("mq_setattr error");
 return 0;
 }
 printf("new_attr.mq_flags = [%ld],old_attr.mq_flags= [%ld]\n", new_attr.mq_flags, old_attr.mq_flags);
 rv = pthread_create(&pid1, NULL, (void *)sendthread,mqd); 
 if(rv < 0) 
 { 
 printf("Create pthread error!\n"); 
 return 1; 
 } 
 rv = pthread_create(&pid2, NULL, (void *)recvthread,mqd); 
 if(rv < 0) 
 { 
 printf("Create pthread error!\n"); 
 return 1; 
 } 
 pthread_join(pid1,NULL);
 pthread_join(pid2,NULL);
 mq_unlink("/mq_test");
 return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

三、SystemV 消息队列

SystemV 消息队列使用消息标识符标识,无论任何进程只要具有权限就可以读写。

对于每个消息队列内核都会维护如下一个结构体

struct msqid_ds { 
 struct ipc_perm msg_perm; //存一些权限,创建信息之类的 
 struct msg* msg_first; //ptr to first message on queue 
 struct msg* msg_last; //ptr to last message on queue 
 msglen_t msg_cbytes; //current # bytes on queue 
 msgqnum_t msg_qnum; //current # of message on queue 
 msglen_t msg_qbytes; <span style="white-space:pre"> </span>//max \# of bytes allowed on queue 
 pid_t msg_lspid; //pid of last msgsnd 
 pid_t msg_lrpid; //pid of last msgrcv 
 time_t msg_stime; //time of last msgsnd() 
 time_t msg_rtime; //time of last msgrcv() 
 time_t msg_ctime; //time of last msgctl() 
}; 
![这里写图片描述](https://img-blog.csdn.net/20180604172448187?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ2MDgyODA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

主要函数

#include <sys/msg.h>
int msgget(key_t key, int oflag)
int msgsnd(int msqid, const void * ptr, size_t length, int flag)
ssize_t msgrcv (int msqid, void *ptr, size_t length, long type, int flag)
int msgctl(int msqid, int cmd, struct msqid_ds *buf)
1
2
3
4
5

int msgget(key_t key, int oflag)

功能:创建一个新的消息队列或者访问一个已存在的消息队列。

参数: key 值通常为IPC_PRIVATE 或者ftok()函数的返回值。

msgflg: 读写权限值的组合,还可以与IPC_CREAT或 IPC_EXCL|IPC_CREAT

返回值:若成功返回非负标识符,失败返回-1

说明:当创建一个新的消息队列时,msqid_ds结构如下成员会被初始化

msg_perm 结构的uid和cuid成员被设置为当前进程的有效用户ID,gid和cgid成员被设置为当前进程的有效组ID。

oflag中的读写权限位存放在msg_perm.mode中

msg_qnum、msg_lspid、msg_lrpid、msg_stime和msg_rtime被设置为0

msg_ctime被设置为当前时间

msg_qbytes被设置为系统限制值

int msgsnd(int msqid, const void * ptr, size_t length, int flag)

功能:往消息队列上放置一个消息

参数:msqid 消息队列标识符

ptr 结构指针,结构体的常规形式为

struct msgbuf

{

long mtype; /消息类型,必须大于0/

char mtext[1];/消息内容/

};

常规形式并不代表必须,消息类型是必须要带的,后面的消息内容可以是其他类型或者其他类型的组合。

length: 消息长度,可以为0,值为sizeof(struct msgbuf) - sizeof(long)

flag : 标志,0或者IPC_NOWAIT,IPC_NOWAIT使得msgsnd调用非阻塞。

返回值:成功返回0,失败返回-1

ssize_t msgrcv (int msqid, void *ptr, size_t length, long type, int flag)

功能:从消息队列中取一个消息

参数:msqid 消息队列标识符

ptr, 指向接受消息的结构

length 指定了ptr指向的数据缓冲区的数据部分大小。该函数返回值最大不能超过length。可用sizeof(struct msgbuf) - sizeof(long)表示;

type 指定从消息队列中读出消息的类型。

如果type为0 那就返回队列中的第一个消息,即最早入队列的消息。

如果type大于0 那就返回类型为type的第一个消息。

如果type小于0,那就返回类型值小于或者等于type绝对值的消息中的类型值最小的第一个消息。

flag 当flag值为IPC_NOWAIT时,队列中没有消息,msgrcv会返回ENOMSG错误,否则msgrcv会阻塞到队列中有消息可取或者消息队列被删除。‘’

返回值:成功时返回读取的数据的长度,失败 返回-1

int msgctl(int msqid, int cmd, struct msqid_ds *buf)

功能:对消息队列的各种操作

参数:msqid 消息队列标识符

cmd 指令 IPC_RMID删除消息队列,队列中的数据也删除

IPC_SET 设置msqid_ds中msg_perm.uid 、msg_perm.gid ,msg_perm.mode 和 msg_qbytes,他们值来自buff结构中相应的成员

IPC_STAT 获取消息队列的msqid_ds 结构,值存放在buff中。

返回值:成功返回0,失败返回-1.

一个简单的测试

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct msg_buf
{
 long msg_type;
 char msg_text[256];
}Msg; 
int main()
{
 int msgid, ret;
 Msg msg;
 int key;
 key = ftok("./systemv_queue.c", 10);
 msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
 if(msgid == -1)
 {
 if(errno == EEXIST)
 {
 key = ftok("./systemv_queue.c", 10);
 msgid = msgget(key, IPC_CREAT | 0666);
 }
 else
 {
 printf("msgget error!\n");
 }
 return -1; 
 }
 msg.msg_type = getpid();
 strcpy(msg.msg_text, "Hello everyone!");
 ret = msgsnd(msgid, &msg, strlen(msg.msg_text), IPC_NOWAIT);
 if(ret == -1)
 {
 printf("msgsnd error!\n");
 return -1;
 }
 sleep(5);
 memset(&msg, 0, sizeof(Msg));
 ret = msgrcv(msgid, &msg, sizeof(msg.msg_text), 0, IPC_NOWAIT);
 if(ret == -1)
 {
 printf("msgrcv error!\n");
 return -1;
 }
 printf("ret = [%d], msg.msg_type = [%d], msg.msg_text = [%s]\n", ret, msg.msg_type, msg.msg_text);
 return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

查看系统的消息队列命令为:ipcs

ipcs -a 是默认的输出信息 打印出当前系统中所有的进程间通信方式的信息

ipcs -m 打印出使用共享内存进行进程间通信的信息

ipcs -q 打印出使用消息队列进行进程间通信的信息

ipcs -s 打印出使用信号进行进程间通信的信息

删除命令为ipcrm

ipcrm -M shmkey 移除用shmkey创建的共享内存段

ipcrm -m shmid 移除用shmid标识的共享内存段

ipcrm -Q msgkey 移除用msqkey创建的消息队列

ipcrm -q msqid 移除用msqid标识的消息队列

ipcrm -S semkey 移除用semkey创建的信号

ipcrm -s semid 移除用semid标识的信号

相关推荐

快递查询教程,批量查询物流,一键管理快递

作为商家,每天需要查询许许多多的快递单号,面对不同的快递公司,有没有简单一点的物流查询方法呢?小编的回答当然是有的,下面随小编一起来试试这个新技巧。需要哪些工具?安装一个快递批量查询高手快递单号怎么快...

一键自动查询所有快递的物流信息 支持圆通、韵达等多家快递

对于各位商家来说拥有一个好的快递软件,能够有效的提高自己的工作效率,在管理快递单号的时候都需要对单号进行表格整理,那怎么样能够快速的查询所有单号信息,并自动生成表格呢?1、其实方法很简单,我们不需要一...

快递查询单号查询,怎么查物流到哪了

输入单号怎么查快递到哪里去了呢?今天小编给大家分享一个新的技巧,它支持多家快递,一次能查询多个单号物流,还可对查询到的物流进行分析、筛选以及导出,下面一起来试试。需要哪些工具?安装一个快递批量查询高手...

3分钟查询物流,教你一键批量查询全部物流信息

很多朋友在问,如何在短时间内把单号的物流信息查询出来,查询完成后筛选已签收件、筛选未签收件,今天小编就分享一款物流查询神器,感兴趣的朋友接着往下看。第一步,运行【快递批量查询高手】在主界面中点击【添...

快递单号查询,一次性查询全部物流信息

现在各种快递的查询方式,各有各的好,各有各的劣,总的来说,还是有比较方便的。今天小编就给大家分享一个新的技巧,支持多家快递,一次能查询多个单号的物流,还能对查询到的物流进行分析、筛选以及导出,下面一起...

快递查询工具,批量查询多个快递快递单号的物流状态、签收时间

最近有朋友在问,怎么快速查询单号的物流信息呢?除了官网,还有没有更简单的方法呢?小编的回答当然是有的,下面一起来看看。需要哪些工具?安装一个快递批量查询高手多个京东的快递单号怎么快速查询?进入快递批量...

快递查询软件,自动识别查询快递单号查询方法

当你拥有多个快递单号的时候,该如何快速查询物流信息?比如单号没有快递公司时,又该如何自动识别再去查询呢?不知道如何操作的宝贝们,下面随小编一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号若干...

教你怎样查询快递查询单号并保存物流信息

商家发货,快递揽收后,一般会直接手动复制到官网上一个个查询物流,那么久而久之,就会觉得查询变得特别繁琐,今天小编给大家分享一个新的技巧,下面一起来试试。教程之前,我们来预览一下用快递批量查询高手...

简单几步骤查询所有快递物流信息

在高峰期订单量大的时候,可能需要一双手当十双手去查询快递物流,但是由于逐一去查询,效率极低,追踪困难。那么今天小编给大家分享一个新的技巧,一次能查询多个快递单号的物流,下面一起来学习一下,希望能给大家...

物流单号查询,如何查询快递信息,按最后更新时间搜索需要的单号

最近有很多朋友在问,如何通过快递单号查询物流信息,并按最后更新时间搜索出需要的单号呢?下面随小编一起来试试吧。需要哪些工具?安装一个快递批量查询高手快递单号若干怎么快速查询?运行【快递批量查询高手】...

连续保存新单号功能解析,导入单号查询并自动识别批量查快递信息

快递查询已经成为我们日常生活中不可或缺的一部分。然而,面对海量的快递单号,如何高效、准确地查询每一个快递的物流信息,成为了许多人头疼的问题。幸运的是,随着科技的进步,一款名为“快递批量查询高手”的软件...

快递查询教程,快递单号查询,筛选更新量为1的单号

最近有很多朋友在问,怎么快速查询快递单号的物流,并筛选出更新量为1的单号呢?今天小编给大家分享一个新方法,一起来试试吧。需要哪些工具?安装一个快递批量查询高手多个快递单号怎么快速查询?运行【快递批量查...

掌握批量查询快递动态的技巧,一键查找无信息记录的两种方法解析

在快节奏的商业环境中,高效的物流查询是确保业务顺畅运行的关键。作为快递查询达人,我深知时间的宝贵,因此,今天我将向大家介绍一款强大的工具——快递批量查询高手软件。这款软件能够帮助你批量查询快递动态,一...

从复杂到简单的单号查询,一键清除单号中的符号并批量查快递信息

在繁忙的商务与日常生活中,快递查询已成为不可或缺的一环。然而,面对海量的单号,逐一查询不仅耗时费力,还容易出错。现在,有了快递批量查询高手软件,一切变得简单明了。只需一键,即可搞定单号查询,一键处理单...

物流单号查询,在哪里查询快递

如果在快递单号多的情况,你还在一个个复制粘贴到官网上手动查询,是一件非常麻烦的事情。于是乎今天小编给大家分享一个新的技巧,下面一起来试试。需要哪些工具?安装一个快递批量查询高手快递单号怎么快速查询?...

取消回复欢迎 发表评论: