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
复制代码

展开阅读全文

没有更多推荐了,返回首页