IPC通信:Posix消息队列
消息队列可以认为是一个链表。进程(线程)可以往里写消息,也可以从里面取出消息。一个进程可以往某个消息队列里写消息,然后终止,另一个进程随时可以从消息队列里取走这些消息。这里也说明了,消息队列具有随内核的持续性,也就是系统不重启,消息队列永久存在。
创建(并打开)、关闭、删除一个消息队列
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <mqueue.h> //头文件
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <errno.h>
9
10 #define MQ_NAME ("/tmp")
11 #define MQ_FLAG (O_RDWR | O_CREAT | O_EXCL) // 创建MQ的flag
12 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) // 设定创建MQ的权限
13
14 int main()
15
16 {
17 mqd_t posixmq;
18 int rc = 0;
19
20 /*
21 函数说明:函数创建或打开一个消息队列
22 返回值:成功返回消息队列描述符,失败返回-1,错误原因存于errno中
23 */
24 posixmq = mq_open(MQ_NAME, MQ_FLAG, FILE_MODE, NULL);
25
26 if(-1 == posixmq)
27 {
28 perror("创建MQ失败");
29 exit(1);
30 }
31
32 /*
33 函数说明:关闭一个打开的消息队列,表示本进程不再对该消息队列读写
34 返回值:成功返回0,失败返回-1,错误原因存于errno中
35 */
36 rc = mq_close(posixmq);
37 if(0 != rc)
38 {
39 perror("关闭失败");
40 exit(1);
41 }
42
43 /*
44 函数说明:删除一个消息队列,好比删除一个文件,其他进程再也无法访问
45 返回值:成功返回0,失败返回-1,错误原因存于errno中
46 */
47 rc = mq_unlink(MQ_NAME);
48 if(0 != rc)
49 {
50 perror("删除失败");
51 exit(1);
52 }
53
54 return 0;
55 }
编译并执行:
1 root@linux:/mnt/hgfs/C_libary# gcc -o crtmq crtmq.c
2 /tmp/ccZ9cTxo.o: In function `main':
3 crtmq.c:(.text+0x31): undefined reference to `mq_open'
4 crtmq.c:(.text+0x60): undefined reference to `mq_close'
5 crtmq.c:(.text+0x8f): undefined reference to `mq_unlink'
6 collect2: ld returned 1 exit status
7 因为mq_XXX()函数不是标准库函数,链接时需要指定;库-lrt;
8 root@linux:/mnt/hgfs/C_libary# gcc -o crtmq crtmq.c -lrt
9
10 root@linux:/mnt/hgfs/C_libary# ./crtmq
11 最后程序并没有删除消息队列(消息队列有随内核持续性),如再次执行该程序则会给出错误信息:
12 root@linux:/mnt/hgfs/C_libary# ./crtmq
13 创建MQ失败: File exit(0)
编译这个程序需要注意几点:
1、消息队列的名字最好使用“/”打头,并且只有一个“/”的名字。否则可能出现移植性问题;(还需保证在根目录有写权限,为了方便我在root权限下测试)
2、创建成功的消息队列不一定能看到,使用一些方法也可以看到,本文不做介绍;
消息队列的名字有如此规定,引用《UNIX网络编程 卷2》的相关描述: mq_open,sem_open,shm_open这三个函数的第一个参数是
一个IPC名字,它可能是某个文件系统中的一个真正存在的路径名,也可能不是。Posix.1是这样描述Posix IPC名字的。
1)它必须符合已有的路径名规则(最多由PATH_MAX个字节构成,包括结尾的空字节)
2)如果它以斜杠开头,那么对这些函数的不同调用将访问同一个队列,否则效果取决于实现(也就是效果没有标准化)
3)名字中的额外的斜杠符的解释由实现定义(同样是没有标准化) 因此,为便于移植起见,Posix IPC名字必须以一个斜杠打头,并且不能再包含任何其他斜杠符。
IPC通信:Posix消息队列读,写
创建消息队列的程序:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <mqueue.h> //头文件
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <errno.h>
9
10 #define MQ_NAME ("/tmp")
11 #define MQ_FLAG (O_RDWR | O_CREAT | O_EXCL) // 创建MQ的flag
12 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) // 设定创建MQ的权限
13
14 int main()
15
16 {
17 mqd_t posixmq;
18 int rc = 0;
19
20 /*
21 函数说明:函数创建或打开一个消息队列
22 返回值:成功返回消息队列描述符,失败返回-1,错误原因存于errno中
23 */
24 posixmq = mq_open(MQ_NAME, MQ_FLAG, FILE_MODE, NULL);
25
26 if(-1 == posixmq)
27 {
28 perror("创建MQ失败");
29 exit(1);
30 }
31
32 /*
33 函数说明:关闭一个打开的消息队列,表示本进程不再对该消息队列读写
34 返回值:成功返回0,失败返回-1,错误原因存于errno中
35 */
36 rc = mq_close(posixmq);
37 if(0 != rc)
38 {
39 perror("关闭失败");
40 exit(1);
41 }
42
43 #if 0
44 /*
45 函数说明:删除一个消息队列,好比删除一个文件,其他进程再也无法访问
46 返回值:成功返回0,失败返回-1,错误原因存于errno中
47 */
48 rc = mq_unlink(MQ_NAME);
49 if(0 != rc)
50 {
51 perror("删除失败");
52 exit(1);
53 }
54
55 return 0;
56 #endif
57 }
编译并执行:
1 root@linux:/mnt/hgfs/C_libary# gcc -o crtmq crtmq.c -lrt
2 root@linux:/mnt/hgfs/C_libary# ./crtmq
3 程序并没有删除消息队列(消息队列有随内核持续性),如再次执行该程序则会给出错误信息:
4 root@linux:/mnt/hgfs/C_libary# ./crtmq
5 创建MQ失败: File exit(0)
向消息队列写消息的程序:
消息队列的读写主要使用下面两个函数:
/*头文件*/
#include <mqueue.h>
/*返回:若成功则为消息中字节数,若出错则为-1 */
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);
/*返回:若成功则为0, 若出错则为-1*/
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);
/*消息队列属性结构体*/
struct mq_attr {
long mq_flags; /* Flags: 0 or O_NONBLOCK */
long mq_maxmsg; /* Max. # of messages on queue */
long mq_msgsize; /* Max. message size (bytes) */
long mq_curmsgs; /* # of messages currently in queue */
};
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <mqueue.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <errno.h>
9
10 /*向消息队列发送消息,消息队列名及发送的信息通过参数传递*/
11 int main(int argc, char *argv[])
12 {
13 mqd_t mqd;
14 char *ptr;
15 size_t len;
16 unsigned int prio;
17 int rc;
18
19 if(argc != 4)
20 {
21 printf("Usage: sendmq <name> <bytes> <priority>\n");
22 exit(1);
23 }
24
25 len = atoi(argv[2]);
26 prio = atoi(argv[3]);
27
28 //只写模式找开消息队列
29 mqd = mq_open(argv[1], O_WRONLY);
30 if(-1 == mqd)
31 {
32 perror("打开消息队列失败");
33 exit(1);
34 }
35
36 // 动态申请一块内存
37 ptr = (char *) calloc(len, sizeof(char));
38 if(NULL == ptr)
39 {
40 perror("申请内存失败");
41 mq_close(mqd);
42 exit(1);
43 }
44
45 /*向消息队列写入消息,如消息队列满则阻塞,直到消息队列有空闲时再写入*/
46 rc = mq_send(mqd, ptr, len, prio);
47 if(rc < 0)
48 {
49 perror("写入消息队列失败");
50 mq_close(mqd);
51 exit(1);
52 }
53
54 // 释放内存
55 free(ptr);
56 return 0;
57 }
编译并执行:
1 root@linux:/mnt/hgfs/C_libary# gcc -o sendmq sendmq.c -lrt
2 root@linux:/mnt/hgfs/C_libary# ./sendmq /tmp 30 15
3 root@linux:/mnt/hgfs/C_libary# ./sendmq /tmp 30 16
4 root@linux:/mnt/hgfs/C_libary# ./sendmq /tmp 30 17
5 root@linux:/mnt/hgfs/C_libary# ./sendmq /tmp 30 18
上面先后向消息队列“/tmp”写入了四条消息,因为先前创建的消息队列只允许存放3条消息,本次第四次写入时程序会阻塞。直到有另外进程从消息队列取走消息后本次写入才成功返回。
读消息队列:
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/*读取某消息队列,消息队列名通过参数传递*/
int main(int argc, char *argv[])
{
mqd_t mqd;
struct mq_attr attr;
char *ptr;
unsigned int prio;
size_t n;
int rc;
if(argc != 2)
{
printf("Usage: readmq <name>\n");
exit(1);
}
/*只读模式打开消息队列*/
mqd = mq_open(argv[1], O_RDONLY);
if(mqd < 0)
{
perror("打开消息队列失败");
exit(1);
}
// 取得消息队列属性,根据mq_msgsize动态申请内存
rc = mq_getattr(mqd, &attr);
if(rc < 0)
{
perror("取得消息队列属性失败");
exit(1);
}
/*动态申请保证能存放单条消息的内存*/
ptr = calloc(attr.mq_msgsize, sizeof(char));
if(NULL == ptr)
{
printf("动态申请内存失败\n");
mq_close(mqd);
exit(1);
}
/*接收一条消息*/
n = mq_receive(mqd, ptr, attr.mq_msgsize, &prio);
if(n < 0)
{
perror("读取失败");
mq_close(mqd);
free(ptr);
exit(1);
}
printf("读取 %ld 字节\n 优先级为 %u\n", (long)n, prio);
return 0;
}
编译并执行:
1 root@linux:/mnt/hgfs/C_libary# vi readmq.c
2 root@linux:/mnt/hgfs/C_libary# vi readmq.c
3 root@linux:/mnt/hgfs/C_libary# gcc -o readmq readmq.c -lrt
4 root@linux:/mnt/hgfs/C_libary# ./readmq /tmp
5 读取 30 字节
6 优先级为 18
7 root@linux:/mnt/hgfs/C_libary# ./readmq /tmp
8 读取 30 字节
9 优先级为 17
10 root@linux:/mnt/hgfs/C_libary# ./readmq /tmp
11 读取 30 字节
12 优先级为 16
13 root@linux:/mnt/hgfs/C_libary# ./readmq /tmp
14 读取 30 字节
15 优先级为 15
16 root@linux:/mnt/hgfs/C_libary# ./readmq /tmp
程序执行五次,第一次执行完,先前阻塞在写处的程序成功返回。第五次执行,因为消息队列已经为空,程序阻塞。直到另外的进程向消息队列写入一条消息。另外,还可以看出Posix消息队列每次读出的都是消息队列中优先级最高的消息。
IPC通信:Posix消息队列的属性设置
Posix消息队列的属性使用如下结构存放:
struct mq_attr
{
long mq_flags; /*阻塞标志位,0为非阻塞(O_NONBLOCK)*/
long mq_maxmsg; /*队列所允许的最大消息条数*/
long mq_msgsize; /*每条消息的最大字节数*/
long mq_curmsgs; /*队列当前的消息条数*/
};
队列可以在创建时由mq_open()函数的第四个参数指定mq_maxmsg,mq_msgsize。 如创建时没有指定则使用默认值,一旦创建,则不可再改变。
队列可以在创建后由mq_setattr()函数设置mq_flags
#include <mqueue.h>
/*取得消息队列属性,放到mqstat地fh*/
int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);
/*设置消息队列属性,设置值由mqstat提供,原先值写入omqstat*/
int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, struct mq_attr *omqstat);
均返回:若成功则为0,若出错为-1
程序获取和设置消息队列的默认属性:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <mqueue.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <errno.h>
9
10 #define MQ_NAME ("/tmp")
11 #define MQ_FLAG (O_RDWR | O_CREAT | O_EXCL) // 创建MQ的flag
12 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) // 设定创建MQ的权限
13
14 int main()
15 {
16 mqd_t posixmq;
17 int rc = 0;
18
19 struct mq_attr mqattr;
20
21 // 创建默认属性的消息队列
22 posixmq = mq_open(MQ_NAME, MQ_FLAG, FILE_MODE, NULL);
23 if(-1 == posixmq)
24 {
25 perror("创建MQ失败");
26 exit(1);
27 }
28
29 // 获取消息队列的默认属性
30 rc = mq_getattr(posixmq, &mqattr);
31 if(-1 == rc)
32 {
33 perror("获取消息队列属性失败");
34 exit(1);
35 }
36
37 printf("队列阻塞标志位:%ld\n", mqattr.mq_flags);
38 printf("队列允许最大消息数:%ld\n", mqattr.mq_maxmsg);
39 printf("队列消息最大字节数:%ld\n", mqattr.mq_msgsize);
40 printf("队列当前消息条数:%ld\n", mqattr.mq_curmsgs);
41
42 rc = mq_close(posixmq);
43 if(0 != rc)
44 {
45 perror("关闭失败");
46 exit(1);
47 }
48
49 rc = mq_unlink(MQ_NAME);
50 if(0 != rc)
51 {
52 perror("删除失败");
53 exit(1);
54 }
55 return 0;
56 }
编译并执行:
1 root@linux:/mnt/hgfs/C_libary# gcc -o attrmq attrmq.c -lrt
2 root@linux:/mnt/hgfs/C_libary# ./attrmq
3 队列阻塞标志位:0
4 队列允许最大消息数:10
5 队列消息最大字节数:8192
6 队列当前消息条数:0
7 root@linux:/mnt/hgfs/C_libary#
设置消息队列的属性:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <mqueue.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <errno.h>
9
10 #define MQ_NAME ("/tmp")
11 #define MQ_FLAG (O_RDWR | O_CREAT | O_EXCL) // 创建MQ的flag
12 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) // 设定创建MQ的权限
13
14 int main()
15 {
16 mqd_t posixmq;
17 int rc = 0;
18
19 struct mq_attr mqattr;
20
21 // 创建默认属性的消息队列
22 mqattr.mq_maxmsg = 5; // 注意不能超过系统最大限制
23 mqattr.mq_msgsize = 8192;
24 //posixmq = mq_open(MQ_NAME, MQ_FLAG, FILE_MODE, NULL);
25 posixmq = mq_open(MQ_NAME, MQ_FLAG, FILE_MODE, &mqattr);
26
27 if(-1 == posixmq)
28 {
29 perror("创建MQ失败");
30 exit(1);
31 }
32
33 mqattr.mq_flags = 0;
34 mq_setattr(posixmq, &mqattr, NULL);// mq_setattr()只关注mq_flags,adw
35
36 // 获取消息队列的属性
37 rc = mq_getattr(posixmq, &mqattr);
38 if(-1 == rc)
39 {
40 perror("获取消息队列属性失败");
41 exit(1);
42 }
43
44 printf("队列阻塞标志位:%ld\n", mqattr.mq_flags);
45 printf("队列允许最大消息数:%ld\n", mqattr.mq_maxmsg);
46 printf("队列消息最大字节数:%ld\n", mqattr.mq_msgsize);
47 printf("队列当前消息条数:%ld\n", mqattr.mq_curmsgs);
48
49 rc = mq_close(posixmq);
50 if(0 != rc)
51 {
52 perror("关闭失败");
53 exit(1);
54 }
55
56 rc = mq_unlink(MQ_NAME);
57 if(0 != rc)
58 {
59 perror("删除失败");
60 exit(1);
61 }
62
63 return 0;
64 }
复制代码
编译运行:
复制代码
1 root@linux:/mnt/hgfs/C_libary# gcc -o setattrmq setattrmq.c -lrt
2 root@linux:/mnt/hgfs/C_libary# ./setattrmq
3 队列阻塞标志位:0
4 队列允许最大消息数:5
5 队列消息最大字节数:8192
6 队列当前消息条数:0
本文转自:http://blog.csdn.net/liuhongxiangm/article/details/8716232