IPC通信:Posix消息队列

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

 

你可能感兴趣的:(消息队列)