消息message是一个格式化的可变长信息单位。消息机制允许一个进程向任何其他进程发送一个消息。本质上,消息队列很像是内核维护的一个信箱,任何进程都可以向内核发送一封信,这个信包括消息的类型(用long型表示),和消息的内容(char型数组)。任何进程通过IPC对象标识符,就可以向指定的信箱发送消息。类似的,任何进程可以通过IPC对象标识符,来从信箱中收取指定类型的消息。这里的任何进程,是指具有访问权限的任何进程。
下面通过一个例子来了解消息队列。
程序server创建消息队列,其他进程可以通过server进程创建的消息队列来进行通信。这时写消息队列的程序send可以发送消息,任何时候,读消息队列的程序receive可以读取消息。
程序server的源代码msgq.c
#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
key_t key;
int msgid;
int retval;
printf("my pid is %d\n", getpid());
key = ftok(".", 'q');
printf("IPC key = 0x%x\n", key);
/* create a message queue with the IPC key */
msgid = msgget(key, IPC_CREAT | 00666);
if(-1 == msgid)
{
perror("msg creat error");
return;
}
else
{
printf("Message Queue msqid=%d\n", msgid);
}
printf("use \"id\", \"ipcs -q [-i msgid]\" to see more details\n");
printf("press Enter to remove the message queue...\n");
getchar();
/* delete the msg queue */
retval = msgctl(msgid, IPC_RMID, NULL);
if(-1 == retval)
{
perror("unlink msg queue error");
}
else
{
printf("msg queue [%d] unlinked OK\n", msgid);
}
return;
}
receive.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>
#include <sys/types.h>
#define LEN 512
int main(int argc, char *argv[])
{
int sflag, ret;
struct msgbuf
{
int mtype;
char mtext[LEN];
}msg_buf;
/*receive message from msg queue */
if(2 != argc)
{
printf("Usage: %s msqID\n", argv[0]);
return;
}
sflag = IPC_NOWAIT | MSG_NOERROR;
do
{
ret = msgrcv(atoi(argv[1]), &msg_buf, sizeof(msg_buf.mtext), 0, sflag);
if(-1 == ret)
{
perror("exit when type=100. msgrcv error");
sleep(1);
}
else
{
printf("pid:%d received [%d] Byte from msqid [%d]\n", \
getpid(), ret, atoi(argv[1]));
printf("content:[%s]\n", msg_buf.mtext);
}
if(100 == msg_buf.mtype)
break;
}
while(1);
return ;
}
send.c
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <sys/types.h>
#define LEN 512
int main(int argc, char *argv[])
{
int sflag, ret;
struct msgbuf
{
int mtype;
char mtext[LEN];
}msg_buf;
/*send id message*/
if(4 != argc)
{
/* 0 1 2 3 */
printf("Usage: %s msqID type message\n", argv[0]);
return;
}
sflag = IPC_NOWAIT;
msg_buf.mtype = atoi(argv[2]);
strcpy(&msg_buf.mtext[0], argv[3]);
ret = msgsnd(atoi(argv[1]), &msg_buf, strlen(msg_buf.mtext), sflag);
if(-1 == ret)
{
perror("msgsnd error");
return ;
}
else
{
printf("pid:%d sent msg to msqid [%d] OK\n", getpid(), atoi(argv[1]));
}
return ;
}
对应的Makefile
SERVER = server
SEND = s
RECEIVE = r
all:server send receive
server: msgq.c
gcc msgq.c -o $(SERVER)
send: send.c
gcc send.c -o $(SEND)
receive: receive.c
gcc receive.c -o $(RECEIVE)
clean:
rm -f $(SERVER) $(SEND) $(RECEIVE)
随时的,可以使用命令ipcs -q来查看系统所有的消息队列。
先运行server:
test@test:~$ ./server
my pid is 11609
IPC key = 0x710400db
Message Queue msqid=163840
use "id", "ipcs -q [-i msgid]" to see more details
press Enter to remove the message queue...
进程server会建立消息队列后会停止运行,其他进程就可以通过导出的msgq id来进行通信。
然后从另一个终端中运行send,就可以发送消息到id为163840的这个消息队列
test@test:~$ ./s 163840 10 hello
这时通过ipcs -q -i 163840
就可以看到163840这个消息队列的很多信息,比如现在消息队列中有多少byte数据,最后一个发送消息的进程进程号是多少。
可以再从第三个终端中运行receive,指定从163840这个消息队列中收取消息。
test@test:~$ ./r 163840
pid:11616 received [5] Byte from msqid [163840]
content:[hello]
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
exit when type=100. message receive error: No message of desired type
r进程收不到类型为100的消息就会循环读取。这时可从第二个终端中查看下消息队列的信息,也可以继续发送其他消息。
当发送类型100的消息时,收消息进程满足退出条件就退出了。
再返回第一个终端中敲下回车,释放申请的IPC资源。