1.概念
由内核创建的用于存放消息的链表,分为:System V消息队列,Posix消息队列。消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。
2.特点
优点:
1)我们可以通过发送消息来几乎完全避免命名管道的同步和阻塞问题。
2) 我们可以用一些方法来提前查看紧急消息。
缺点:
1)与管道一样,每个数据块有一个最大长度的限制,
2)系统中所有队列所包含的全部数据块的总长度也有一个上限。
3.相关函数
函数原型:int msgget(key_t key, int msgflag)
功能: 用于创建一个新的或打开一个已经存在的消息队列,此消息队列与key相对应。
参数:
key:函数ftok的返回值或IPC_PRIVATE。
msgflag: IPC_CREAT:创建新的消息队列。 IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。 IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。
返回值: 调用成功返回队列标识符,否则返回-1.
函数原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)
*功能:msgrcv()可以从消息队列中读取消息。
函数原型:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:msgsnd()将一个新的消息写入队列。
参数:msqid:消息队列的识别码。
msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下
struct msgbuf {
long mtype; // 消息类型,必须 > 0
char mtext[1]; // 消息文本
};
msgsz:消息的大小。
msgtyp:消息类型。
msgtyp等于0 则返回队列的最早的一个消息。
msgtyp大于0,则返回其类型为msgtyp的第一个消息,而后的消息不再接收。
msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息。
msgflg:这个参数依然是控制函数行为的标志,取值可以是:0表示忽略。
函数原型:int msgctl(int msqid,int cmd,struct msqid_ds *buf),msgctl系统调用对msqid标识的消息队列执行cmd操作列。
功能:我们使用系统调用msgctl ( )中的IPC_RMID命令删除消息队列。
4.使用步骤:
使用msgget函数创建新的消息队列,或者获取已经存在的消息队列,并返回唯一标识符(ID),后续收发消息需要指定的标识符,以说明操作的是哪个消息队列。
发送:进程先封装一条消息,调用API发送。
接收:根据消息队列的ID和消息ID,调用API接收消息。
使用msgctl函数,根据消息列表ID,删除指定消息队列。
父子进程间的通讯
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct{
long id;
char dat[100];
}Msg;
#define MSG_KEY 10
int main(){
Msg *msgp = NULL;
int msgqid = msgget(MSG_KEY,0644|IPC_CREAT);
if(msgqid == -1){
perror("msgget error!\n");
exit(1);
}
pid_t pid = fork();
if(pid == -1){
perror("fork error!\n");
exit(1);
}
if(pid == 0){//read
msgp = malloc(sizeof(Msg));
while(1){
msgrcv(msgqid,msgp,sizeof(Msg),0,0);
if(strcmp(msgp->dat,"exit") == 0){
break;
}
printf("receive:%s\n",msgp->dat);
memset(msgp->dat,0,sizeof(msgp->dat)); //消息清空
}
}else if(pid > 0){//write
msgp = malloc(sizeof(Msg));
int i = 0;
while(1){
scanf("%s",msgp->dat);
msgp->id = ++i; //必须从第一个开始接收 id>0
msgsnd(msgqid,msgp,sizeof(Msg),0);
if(strcmp(msgp->dat,"exit") == 0){
break;
}
}
if(msgctl(msgqid,IPC_RMID,NULL) == -1){
perror("msgctl error!\n");
exit(1);
}
}
return 0;
}
无关进程间的通讯
系统命令:
查看所有消息队列:ipcs -q
删除消息队列:ipcrm -q(-Q)msgid