言之者无罪,闻之者足以戒。 ——《诗序》
消息队列:
链式队列:
msqid ds 维护消息队列的结构体,队列的第一个消息指针msg_first,最后一个消息指针msg_last
消息中有一个成员指针next
每一个消息中包含有哪些内容:
Data 数据
Length 数据的长度
Type 数据的类型
消息的接收端可以根据消息的类型来接收。
消息队列与文件IO的对比:
文件I/O | 消息队列 |
open(打开文件) | msg_get(创建消息队列) |
read(读数据) | msgrcv(从消息队列中读数据) |
write(写数据) | msgsnd(向消息队列中写数据) |
close(关闭文件) | msgctl(删除消息对列) |
1、msgget:创建消息队列
所需头文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> |
函数原型 | int msgget(key_t key, int flag); |
函数参数 | key:和消息队列关联的key值;可以用宏定义IPC_PRIVATE,也可以用ftok()函数 |
| flag:消息队列的访问权限 |
函数返回值 | 成功:消息队列ID |
| 出错:-1 |
2、msgctl:删除消息队列
所需头文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> | |
函数原型 | int msgctl ( int msqid, int cmd, struct msqid_ds *buf ); | |
函数参数 | msqid:消息队列的队列ID | |
| cmd: | IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。 |
|
| IPC_SET:设置消息队列的属性。这个值取自buf参数。 |
|
| IPC_RMID:从系统中删除消息队列。 |
| buf:消息队列缓冲区 | |
函数返回值 | 成功:0 | |
| 出错:-1 |
下面我们来看一下关于这两个函数的程序:
#include "sys/types.h"
#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int msgid;
msgid=msgget(IPC_PRIVATE,0777);
if(msgid < 0)
{
printf("creat message queue failure\n");
return -1;
}
printf("creat message queue sucess msgid=%d\n",msgid);
system("ipcs -q");
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
3、msgsnd:向消息队列中写入数据
所需头文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> | |
函数原型 | int msgsnd(int msqid, const void *msgp, size_t size, int flag); | |
函数参数 | msqid:消息队列的ID | |
| msgp:指向消息的指针。常用消息结构msgbuf如下: struct msgbuf { long mtype; //消息类型 char mtext[N] //消息正文 }; | |
| size:发送的消息正文的字节数 | |
| flag: | IPC_NOWAIT 消息没有发送完成函数也会立即返回。 |
|
| 0:直到发送完成函数才返回 |
函数返回值 | 成功:0 | |
| 出错:-1 |
4、msgrcv:从消息队列中读取数据
所需头文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> | |
函数原型 | int msgrcv(int msqid, void* msgp, size_t size, long msgtype, int flag); | |
函数参数 | msqid:消息队列的ID | |
| msgp:接收消息的缓冲区 | |
| size:要接收的消息的字节数 | |
| msgtype: | 0:接收消息队列中第一个消息。 |
|
| 大于0:接收消息队列中第一个类型为msgtype的消息. |
|
| 小于0:接收消息队列中类型值不大于msgtype的绝对值且类型值又最小的消息。 |
| flag: | 0:若无消息函数会一直阻塞 |
|
| IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG。 |
函数返回值 | 成功:接收到的消息的长度 | |
| 出错:-1 |
消息队列中数据读后,数据也不存在了
下面看一下程序:
#include "sys/types.h"
#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include <string.h>
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
struct msgbuf sendbuf,recvbuf;
msgid=msgget(IPC_PRIVATE,0777);
if(msgid < 0)
{
printf("creat message queue failure\n");
return -1;
}
printf("creat message queue sucess msgid=%d\n",msgid);
system("ipcs -q");
sendbuf.type=100;
printf("please input message:\n");
fgets(sendbuf.voltage,124,stdin);
msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
memset(recvbuf.voltage,0,124);
readret=msgrcv(msgid,(void *)&recvbuf,124,100,0);
printf("recv:%s",recvbuf.voltage);
printf("readret=%d\n",readret);
msgrcv(msgid,(void *)&recvbuf,124,100,0);
printf("second read after\n");
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
(1)下面我们用一个消息队列实现无亲缘关系的进程的单向通信:
write函数:
#include "sys/types.h"
#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include <string.h>
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
struct msgbuf sendbuf,recvbuf;
int key;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -2;
}
msgid=msgget(key,IPC_CREAT | 0777);
if(msgid < 0)
{
printf("creat message queue failure\n");
return -1;
}
printf("creat message queue sucess msgid=%d\n",msgid);
system("ipcs -q");
sendbuf.type=100;
while(1)
{
memset(sendbuf.voltage,0,124);//clear send buffer
printf("please input message:\n");
fgets(sendbuf.voltage,124,stdin);//input from key panel
msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
}
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
read函数:
#include "sys/types.h"
#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include <string.h>
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
struct msgbuf sendbuf,recvbuf;
int key;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -2;
}
msgid=msgget(key,IPC_CREAT | 0777);
if(msgid < 0)
{
printf("creat message queue failure\n");
return -1;
}
printf("creat message queue sucess msgid=%d\n",msgid);
system("ipcs -q");
sendbuf.type=100;
while(1)
{
memset(recvbuf.voltage,0,124);//clear receive buffer
msgrcv(msgid,(void *)&recvbuf,124,100,0);
printf("receive data from message queue:%s",recvbuf.voltage);
}
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
(2)用消息队列实现父子进程的双通信
server函数:
#include "sys/types.h"
#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include <string.h>
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
int pid;
struct msgbuf sendbuf,recvbuf;
int key;
key=ftok("./b.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -2;
}
msgid=msgget(key,IPC_CREAT | 0777);
if(msgid < 0)
{
printf("creat message queue failure\n");
return -1;
}
printf("creat message queue sucess msgid=%d\n",msgid);
system("ipcs -q");
pid=fork();
if(pid > 0)//parent process write 100
{
sendbuf.type=100;
//write message queue
while(1)
{
memset(sendbuf.voltage,0,124);
printf("please input message:\n");
fgets(sendbuf.voltage,124,stdin);
msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
}
}
if(pid == 0)//child process read 200
{
while(1)
{
memset(recvbuf.voltage,0,124);
msgrcv(msgid,(void *)&recvbuf,124,200,0);
printf("receive message from message queue:%s",recvbuf.voltage);
}
}
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
client函数:
#include "sys/types.h"
#include "sys/msg.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include <string.h>
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
int pid;
struct msgbuf sendbuf,recvbuf;
int key;
key=ftok("./b.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -2;
}
msgid=msgget(key,IPC_CREAT | 0777);
if(msgid < 0)
{
printf("creat message queue failure\n");
return -1;
}
printf("creat message queue sucess msgid=%d\n",msgid);
system("ipcs -q");
pid=fork();
if(pid == 0)//child process write 200
{
sendbuf.type=200;
//write message queue
while(1)
{
memset(sendbuf.voltage,0,124);
printf("please input message:\n");
fgets(sendbuf.voltage,124,stdin);
msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
}
}
if(pid > 0)//parent process read 100
{
while(1)
{
memset(recvbuf.voltage,0,124);
msgrcv(msgid,(void *)&recvbuf,124,100,0);
printf("receive message from message queue:%s",recvbuf.voltage);
}
}
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
上面的两个程序实现了父子进程的双通信;在server程序中父进程以type为100来发送数据,子进程以type为200来接收数据;在client程序中父进程以type为100来接收数据,子进程以type为200来发送数据。这样就实现了在一个消息队列中的双通信。