前言:
消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息
structmsqid_ds (这是消息队列的结构体,了解下就行,用到了再详看,具体含义我的文档里面有)
{
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first messageon queue,unused */
struct msg *msg_last; /* last messagein queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of byteson queue */
unsigned short msg_qnum; /* number of messagesin queue */
unsigned short msg_qbytes; /* max number of bytes onqueue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd*/
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
1.创建新消息队列或取得已存在消息队列
原型:int msgget(key_t key, int msgflg);
参数:
key:可以认为是一个端口号,也可以由函数ftok生成。
例如
msgid = msgget((key_t)1234, 0666 | IPC_CREAT); (红色字体就是我们用户自己指定的端口号),如果要用ftok函数生成,见下面的函数
成功:消息队列的标识符 失败:返回-1 错误原因存于error中
msgflg:IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。
IPC_EXCL值,若没有该队列,则返回-1;若已存在,则返回0。
IPC_CREAT如果内核中没有此队列,则创建它。
IPC_EXCL当和IPC_CREAT一起使用时(IPC_EXCL|IPC_CREAT),如果队列已经存在,则失败。
一起使用,如例子:
if((msgid=msgget(key,PERM|IPC_CREAT|IPC_EXCL))==-1)
如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符。如果IPC_EXCL和IPC_CREAT一起使用,则msgget()要么创建一个新的消息队列,要么如果队列已经存在则返回一个失败值-1。IPC_EXCL单独使用是没有用处的。
返回值:如果成功,返回消息队列标识符;如果失败,则返回-1:errno=
EACCESS(权限不允许)
EEXIST(队列已经存在,无法创建)
EIDRM(队列标志为删除)
ENOENT(队列不存在)
ENOMEM(创建队列时内存不够)
ENOSPC(超出最大队列限制)
函数原型:
key_t ftok( const char * pathname , int proj_id );
参数:
pathname 就时你指定的文件名(该文件必须是存在而且可以访问的),id是子序号,虽然为int,但是只有8个比特被使用(0-255)。
返回值: 成功时候返回key_t 类型的key值,失败返回-1
在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。如指定文件的索引节点号为65538,换算成16进制为 0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
查询文件索引节点号的方法是: ls -i filename
在成功获取到key之后,就可以使用该key作为某种方法的进程间通信的key值
经过上面不这些东西,我们的队列就建立好了
/*******************************************************************/
2、读写操作
消息读写操作非常简单,对开发人员来说,每个消息都类似如下的数据结构:
struct msgbuf{
long mtype;
char mtext[512];
};
mtype成员代表消息类型,从消息队列中读取消息的一个重要依据就是消息的类型;mtext是消息内容,当然长度不一定为1。因此,对于发送消息来说,首先预置一个msgbuf缓冲区并写入消息类型和内容,调用相应的发送函数即可;对读取消息来说,首先分配这样一个msgbuf缓冲区,然后把消息读入该缓冲区即可。
intmsgsnd(int msqid , const void * ptr , size_t nbytes, int flag) ;
成功:返回0 失败:返回-1 错误原因存于error中
下面是各个参数的说明:
msqid :消息队列号,这里不用说了,上面的有
ptr : 指向我们定义的(用户定义的)消息结构体的指针,若果我们定义的是上面的结构体(红色部分),那么ptr指向的就是msgbuf
nbytes :要发送消息的大小,不含消息类型占用的4个字节(long mtype占用的4个字节) 指的是mtext的长度,如这个例 子nbytes的值就是512
flag :flag有以下取值
0: 当消息队列满时,msgsnd将会阻塞,直到消息能写进消
IPC_NOWAIT: 当消息队列已满的时候,msgsnd函数不等待立即返回
IPC_NOERROR: 若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程
/****************************************************************************************************/
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
函数功能:从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除
下面是各个参数的说明:
msqid : 消息队列标识符
msgp : 存放消息的结构体,结构体类型要与msgsnd函数发送的类型相同
msgsz : 要接收消息的大小,不含消息类型占用的4个字节
msgtyp :
0:接收第一个消息
>0:接收类型等于msgtyp的第一个消息
<0:接收类型等于或者小于msgtyp绝对值的第一个消息
msgflg :
0: 阻塞式接收消息,没有该类型的消息msgrcv函数一直阻塞等待
IPC_NOWAIT: 如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG
IPC_EXCEPT: 与msgtype配合使用返回队列中第一个类型不为msgtype的消息
IPC_NOERROR: 如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃
msgrcv()解除阻塞的条件有以下三个:
① 消息队列中有了满足条件的消息。
② msqid代表的消息队列被删除。
③ 调用msgrcv()的进程被信号中断。
intmsgctl(int msqid, int cmd, struct msqid_ds * buf ) ;(这个函数一般是没怎么用到,用到的时候再好好看看用法(见我的文档unix高级编程))
cmd参数指定对于由msqid规定的队列要执行的命令:
• IPC_STAT :取此队列的m s q i d _ d s结构,并将其存放在b u f指向的结构中。
• IPC_SET : 按由b u f指向的结构中的值,设置与此队列相关的结构中的下列四个字段:m s g _ p e rm . u i d、 m s g _ p e r m . g id、 m s g _ p e r m ; mode和m s g _ q byt e s。此命令只能由下列两种进程执
行:一种是其有效用户I D等于m s g _ p e rm . c u i d或m s g _ p e r m . u i d ;另一种是具有超级用户特权的进程。只有超级用户才能增加m s g _ q b yt e s的值
• IPC_RMID
从系统中删除该消息队列以及仍在该队列上的所有数据。这种删除立即生效。仍在使用这一消息队列的其他进程在它们下一次试图对此队列进行操作时,将出错返回E I D R M。此命令只能由下列两种进程执行:一种是其有效用户I D等于m s g _ p e r m . c u i d或m s g _ p e rm . u i d ;另一种是具有超级用户特权的进程。这三条命令(I P C _ S TAT、I P C _ S E T和I P C _ R M I D)也可用于信号量和共享存储。
调用m s g s n d将数据放到消息队列上。
下面的实例代码来自网页
http://blog.csdn.net/sty23122555/article/details/51132158 (详细代码去这个网站上看)
代码例子以后自己可以在网上搜搜就行
1./*send.c*/
2.#include <stdio.h>
3.#include <sys/types.h>
4.#include <sys/ipc.h>
5.#include <sys/msg.h>
6.#include <errno.h>
7.
8.#define MSGKEY 1024
9.
10. struct msgstru
11. {
12. long msgtype;
13. char msgtext[2048];
14. };
15.
16. main()
17. {
18. struct msgstru msgs;
19. int msg_type;
20. char str[256];
21. int ret_value;
22. int msqid;
23.
24. msqid=msgget(MSGKEY,IPC_EXCL); /*检查消息队列是否存在*/
25. if(msqid < 0){
26. msqid = msgget(MSGKEY,IPC_CREAT|0666);/*创建消息队列*/
27. if(msqid <0){
28. printf("failed to create msq | errno=%d [%s]\n",errno,strerror(errno));
29. exit(-1);
30. }
31. }
32.
33. while (1){
34. printf("input message type(end:0):");
35. scanf("%d",&msg_type);
36. if (msg_type == 0)
37. break;
38. printf("input message to be sent:");
39. scanf ("%s",str);
40. msgs.msgtype = msg_type;
41. strcpy(msgs.msgtext, str);
42. /* 发送消息队列 */
43. ret_value = msgsnd(msqid,&msgs,sizeof(struct msgstru),IPC_NOWAIT);
44. if ( ret_value < 0 ) {
45. printf("msgsnd() write msg failed,errno=%d[%s]\n",errno,strerror(errno));
46. exit(-1);
47. }
48. }
49. msgctl(msqid,IPC_RMID,0); //删除消息队列
50. }
消息接收端 receive.c
[cpp] view plain copy
1./*receive.c */
2.#include <stdio.h>
3.#include <sys/types.h>
4.#include <sys/ipc.h>
5.#include <sys/msg.h>
6.#include <errno.h>
7.
8.#define MSGKEY 1024
9.
10. struct msgstru
11. {
12. long msgtype;
13. char msgtext[2048];
14. };
15.
16. /*子进程,监听消息队列*/
17. void childproc(){
18. struct msgstru msgs;
19. int msgid,ret_value;
20. char str[512];
21.
22. while(1){
23. msgid = msgget(MSGKEY,IPC_EXCL );/*检查消息队列是否存在 */
24. if(msgid < 0){
25. printf("msq not existed! errno=%d [%s]\n",errno,strerror(errno));
26. sleep(2);
27. continue;
28. }
29. /*接收消息队列*/
30. ret_value = msgrcv(msgid,&msgs,sizeof(struct msgstru),0,0);
31. printf("text=[%s] pid=[%d]\n",msgs.msgtext,getpid());
32. }
33. return;
34. }
35.
36. void main()
37. {
38. int i,cpid;
39.
40. /* create 5 child process */
41. for (i=0;i<5;i++){
42. cpid = fork();
43. if (cpid < 0)
44. printf("fork failed\n");
45. else if (cpid ==0) /*child process*/
46. childproc();
47. }
48. }
若父进程退出,子进程尚未结束,则子进程会被init进程领养,也就是说init进程将成为该子进程的父进程。
若希望父进程退出,子进程也退出的话,可以使用线程,因为若进程结束,则还没结束的线程一定会立刻结束。
另外:以上是systemv标准下的消息队列接口,如果要使用POSIX标准来创建消息队列,1、mq_open来创建非默认个数大小消息队列:
函数原型
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
第4个参数为 mq_attr 指针
structmq_attr{
longmq_flags;
longmq_maxmsg;
longmq_msgsize;
longmq_curmsgs; };
当第四个参数为空指针时,就使用默认属性。
当指向mq_attr结构的指针作为参数时,允许我们在该函数的实际操作时创建一个新队列时,给它指定mq_maxmsg和mq_msgsize属性.mq_open忽略该结构的另外两个成员.
(1)attr.mq_maxmsg不能超过文件 /proc/sys/fs/mqueue/msg_max中的数值;
(2)attr.mq_msgsize不能超过 /proc/sys/fs/mqueue/msgsize_max的数值;
(3)消息队列名称前面必须加上斜杆。
在POSIX消息队列中 msg_max 默认为 10 ,msgsize_max 默认为8192,否则会报错!!!
可以在/proc/sys/fs/mqueue# cat msg_max /proc/sys/fs/mqueue# cat msgsize_max查看
修改的话,要使用:echo 1000> /proc/sys/fs/mqueue/msg_max往里面写。
2、获取消息队列的属性
一个进程在发送和接收消息之前,需要了解消息对象的属性,如消息的最大长度。以便
设定接收和发送的buffer大小。 mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);参数:
Mqdes:打开消息队列时获取的描述符。
Attr:指向结构struct mq_attr的指针,用来获取消息队列的四个属性
struct mq_attr {
long mq_flags; // 0或者O_NONBLOCK
long mq_maxmsg; //队列中包含的消息数的最大限制数
long mq_msgsize; //每个消息大小的最大限制数
long mq_curmsgs; //当前队列中的消息数
}
3、设置消息队列属性
我们可以设置消息队列的属性,实际只能设置flag标志,说明队列中没有消息时,接收消息的进程是否在队列上继续等待。
mqd_t mq_setattr(mqd_t mqdes, structmq_attr *newattr, struct mq_attr *oldattr);
参数:
Mqdes:打开消息队列时获取的描述符。
Attr:指向结构struct mq_attr的指针,用来获取消息队列的最大消息个数和最大消息长
度。放到数据结构的mq_maxmsg和mq_msgsize中。
struct mq_attr {
long mq_flags; // 0或者O_NONBLOCK,只能设置这个
long mq_maxmsg; //队列中包含的消息数的最大限制数
long mq_msgsize; //每个消息大小的最大限制数
long mq_curmsgs; //当前队列中的消息数
}
oldattr:用来保存设置之前的attr值,可以为NULL.
4、发送消息
进程在打开消息队列后,可以使用下面的函数发送消息
int mq_send(mqd_tmqdes, const char *ptr, size_t len, unsigned int prio);
参数:
mqdes: 打开消息队列时获得的描述符。
ptr: 指向发送缓冲区的指针,发送缓冲区存放了要发送的数据。
Len: 要发送的数据的长度。 prio:消息的优先级;它是一个小于 MQ_PRIO_MAX 的数,数值越大,优先级越高。
POSIX 消息队列在调用 mq_receive时总是返回队列中最高优先级的最早消息 。如果消息不需要设定优先级,那么可以在 mq_send 是置 prio 为 0 ,mq_receive 的 prio 置为 NULL 。 返回值:发送成功,返回0,失败,返回-1.
5、接收消息
进程在打开消息队列后,可以使用下面的函数接收消息。
ssize_t mq_receive(mqd_t mqdes, char*ptr, size_t len, unsigned int *prio);
参数:
mqdes: 打开消息队列时获得的描述符。
ptr: 指向接收缓冲区的指针。接收缓冲区用来存放收到的消息。
Len: 接收缓冲区的长度。 len不能小于mq_msgsize,否则会返回EMSGSIZE prio :消息的优先级;它是一个小于 MQ_PRIO_MAX 的数,数值越大,优先级越高。 POSIX 消息队列在调用 mq_receive 时总是返回队列中 最高优先级的最早消息。如果消息不需要设定优先级,那么可以在 mq_send 是置 prio 为 0 , mq_receive 的prio 置为 NULL 。 返回值: 接收成功,返回0,失败,返回-1.
6、消息队列的关闭
mqd_t mq_close(mqd_t mqdes); 关闭消息队列,但不能删除它 成功返回0,失败返回-1
7、删除消息队列
mqd_t mq_unlink(const char *name);成功返回0,失败返回-1
当某个进程还没有关闭此消息队列时,调用mq_unlink时,不会马上删除队列,当最后一个进程关闭队列时,该队列被删除
[cpp] view plain copy
1.编译:
2.
3.gcc -o consumer consumer.c -lrt
4.
5.gcc -o producer producer.c -lrt
6.
7./*
8.
9. *
10. * Filename: producer.c
11. *
12. * Description: 生产者进程
13. *
14. * Version: 1.0
15. * Created: 09/30/2011 04:52:23 PM
16. * Revision: none
17. * Compiler: gcc(g++)
18. *
19. * Author: |Zhenghe Zhang|, |zhenghe.zhang@gmail.com|
20. * Company: |Shenzhen XXX Technology Co., Ltd.|
21. *
22. */
23.
24. #include <stdio.h>
25. #include <mqueue.h>
26. #include <sys/stat.h>
27. #include <stdlib.h>
28. #include <unistd.h>
29. #include <time.h>
30. #include <string.h>
31.
32. #define MAXSIZE 10 //定义buf大小
33. #define BUFFER 8192 //定义Msg大小
34.
35. struct MsgType{
36. int len;
37. char buf[MAXSIZE];
38. char x;
39. short y;
40. };
41.
42. int main()
43. {
44. /*消息队列*/
45. mqd_t msgq_id;
46. struct MsgType msg;
47.
48. unsigned int prio = 1;
49. unsigned int send_size = BUFFER;
50.
51. struct mq_attr msgq_attr;
52. const char *file = "/posix";
53.
54. /*mq_open() for creating a new queue (using default attributes) */
55. /*mq_open() 创建一个新的 POSIX 消息队列或打开一个存在的队列*/
56. msgq_id = mq_open(file, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG, NULL);
57. if(msgq_id == (mqd_t)-1)
58. {
59. perror("mq_open");
60. exit(1);
61. }
62.
63. /* getting the attributes from the queue -- mq_getattr() */
64. if(mq_getattr(msgq_id, &msgq_attr) == -1)
65. {
66. perror("mq_getattr");
67. exit(1);
68. }
69.
70. printf("Queue \"%s\":\n\t- stores at most %ld messages\n\t- \
71. large at most %ld bytes each\n\t- currently holds %ld messages\n",
72. file, msgq_attr.mq_maxmsg, msgq_attr.mq_msgsize, msgq_attr.mq_curmsgs);
73.
74. /*setting the attributes of the queue -- mq_setattr() */
75. /*mq_setattr() 设置消息队列的属性,设置时使用由 newattr 指针指向的 mq_attr 结构的信息。*/
76. /*属性中只有标志 mq_flasgs 里的 O_NONBLOCK 标志可以更改,其它在 newattr 内的域都被忽略 */
77. if(mq_setattr(msgq_id, &msgq_attr, NULL) == -1)
78. {
79. perror("mq_setattr");
80. exit(1);
81. }
82.
83. int i = 0;
84. while(i < 10)
85. {
86. msg.len = i;
87. memset(msg.buf, 0, MAXSIZE);
88. sprintf(msg.buf, "0x%x", i);
89. msg.x = (char)(i + 'a');
90. msg.y = (short)(i + 100);
91.
92. printf("msg.len = %d, msg.buf = %s, msg.x = %c, msg.y = %d\n", msg.len, msg.buf, msg.x, msg.y);
93.
94. /*sending the message -- mq_send() */
95. /*mq_send() 把 msg_ptr 指向的消息加入由 mqdes 引用的消息队列里。*/
96. /*参数 msg_len 指定消息 msg_ptr 的长度:这个长度必须小于或等于队列 mq_msgsize 属性的值。零长度的消息是允许。*/
97. if(mq_send(msgq_id, (char*)&msg, sizeof(struct MsgType), prio) == -1)
98. {
99. perror("mq_send");
100. exit(1);
101. }
102.
103. i++;
104. sleep(1);
105. }
106.
107. sleep(30); //等待消费者进程退出
108.
109. /*closing the queue -- mq_close() */
110. /*mq_close() 关闭消息队列描述符 mqdes。如果调用进程在消息队列 mqdes 绑定了通知请求,*/
111. /*那么这个请求被删除,此后其它进程就可以绑定通知请求到此消息队列。*/
112. if(mq_close(msgq_id) == -1)
113. {
114. perror("mq_close");
115. exit(1);
116. }
117.
118. /*mq_unlink() 删除名为 name 的消息队列。消息队列名将被直接删除。*/
119. /*消息队列本身在所有引用这个队列的描述符被关闭时销毁。*/
120. if(mq_unlink(file) == -1)
121. {
122. perror("mq_unlink");
123. exit(1);
124. }
125.
126. return 0;
127. }
128.
129.
[cpp] view plain copy
1./*
2. *
3. * Filename: consumer.c
4. *
5. * Description: 消费者进程
6. *
7. * Version: 1.0
8. * Created: 09/30/2011 04:52:23 PM
9. * Revision: none
10. * Compiler: gcc(g++)
11. *
12. * Author: |Zhenghe Zhang|, |zhenghe.zhang@gmail.com|
13. * Company: |Shenzhen XXX Technology Co., Ltd.|
14. *
15. */
16.
17. #include <stdio.h>
18. #include <mqueue.h>
19. #include <sys/stat.h>
20. #include <stdlib.h>
21. #include <unistd.h>
22. #include <time.h>
23. #include <string.h>
24.
25. #define MAXSIZE 10 //定义buf大小
26. #define BUFFER 8192 //定义Msg大小
27.
28. struct MsgType{
29. int len;
30. char buf[MAXSIZE];
31. char x;
32. short y;
33. };
34.
35. int main()
36. {
37. /*消息队列*/
38. mqd_t msgq_id;
39. struct MsgType msg;
40.
41. unsigned int sender;
42. struct mq_attr msgq_attr;
43.
44. unsigned int recv_size = BUFFER;
45. const char *file = "/posix";
46.
47. /* mq_open() for opening an existing queue */
48. msgq_id = mq_open(file, O_RDWR);
49. if(msgq_id == (mqd_t)-1)
50. {
51. perror("mq_open");
52. exit(1);
53. }
54.
55. /* getting the attributes from the queue -- mq_getattr() */
56. if(mq_getattr(msgq_id, &msgq_attr) == -1)
57. {
58. perror("mq_getattr");
59. exit(1);
60. }
61.
62. printf("Queue \"%s\":\n\t- stores at most %ld messages\n\t- \
63. large at most %ld bytes each\n\t- currently holds %ld messages\n",
64. file, msgq_attr.mq_maxmsg, msgq_attr.mq_msgsize, msgq_attr.mq_curmsgs);
65.
66. if(recv_size < msgq_attr.mq_msgsize)
67. recv_size = msgq_attr.mq_msgsize;
68.
69. int i = 0;
70. while(i < 10) //运行一个consumenr,为 10 ,同时运行两个consumer进程,为 5
71. {
72. msg.len = -1;
73. memset(msg.buf, 0, MAXSIZE);
74. msg.x = ' ';
75. msg.y = -1;
76.
77. /* getting a message */
78.
79. /*mq_receive() 从由描述符 mqdes 引用的队列时删除优先级最高的最老的消息,并把放置到 msg_ptr 的缓存区内。*/
80. /*参数 msg_len 指定缓冲区 msg_ptr 的大小:它必须大于队列的 mq_msgsize 属性(参数 mq_getattr)。*/
81. /*如果 prio 不是 NULL,那么它指向的内存用于返回收到消息相关的优先级。*/
82. if (mq_receive(msgq_id, (char*)&msg, recv_size, &sender) == -1)
83. {
84. perror("mq_receive");
85. exit(1);
86. }
87.
88. printf("msg.len = %d, msg.buf = %s, msg.x = %c, msg.y = %d\n", msg.len, msg.buf, msg.x, msg.y);
89.
90. i++;
91. sleep(2);
92. }
93.
94. if(mq_close(msgq_id) == -1)
95. {
96. perror("mq_close");
97. exit(1);
98. }
99.
100. return 0;
101. }
1、僵尸进程;2、共享内存;3、延时函数;4、kill信号;5、fork子进程;6信号量;