概述:
进程间通信就像货物托运,不同通信方式代表了不同托运渠道,管道是最古老的一种。但管道出货不能分清货物批次,而消息队列,弥补了这些缺陷:
1. 消息队列是一种先进先出的队列数据结构,可以保证先送出的货物先到达,后送出的货物后到达,避免了插队现象。
2. 消息队列将输出的信息进行打包处理,这样可以保证每个消息为单位进行接收了。
3. 消息队列可以对货物进行分类服务,标记各种类别的货物,这样就可以根据货物类别分别出货,如果顾客只希望领取队列中的A类货物,他可以跳过消息队列头,直接取队列中,最接近队列头处的A类货物,并不影响其他货物。
广义上讲一切能使进程间相互交流的方法都是IPC,比如文件,管道,Socket等。狭义上讲IPC特指消息队列,信号量,共享内存三种对象。
消息队列用于不同进程间少量数据的顺序共享。
信号量用于进程间的同步与互斥的控制。
共享内存应用于进程间大批量数据的随机共享访问。
查询 IPC 对象 :
shell 命令 ipcs 可查当前系统的IPC对象信息 :
ipcs 【opions】
-q 只查询 消息队列 IPC对象
-s 只查询信号量IPC对象
-m只查询共享内存IPC对象
-a 查询IPC对象全部属性
默认 查询 三者基本属性,基于 “”-qsm“”
消息队列简介:
消息队列是一种先进先出的队列型数据结构,它实际上是系统内核中的一个内部链表,消息队列被顺序插入队列中,其中发送进程将消息添加到队列末尾,接收进程从队列读取消息。
多个进程可以同时向一个消息队列发送消息,也可以同时从一个消息队列中接收消息。发送进程把消息发送至队尾,接收进程从消息队列头部读取消息,消息一旦被独处就从队列中删除。
内核采用msqid_ds来管理消息队列,它的数据成员与命令“”ipcs -a -q“” 中显示的结果一一对应。
消息队列中各个消息存储在结构为msg结构的空间中个,它们组成一个线性的消息链表,其中msgid结构成员msg_first和msg_last 分别指向这个链表的表头和表尾的指针,结构msg的定义如下:
理论上可以通过结构msgid_ds成员msg_first,msg_last和结构msg的成员msg_msg_next遍历整个消息队列并完成管理和维护消息队列的功能,但实际这三个成员是内核直割数据,用户无权引用。
消息结构 :
消息队列本身由消息类型和消息数据组成,我们常常使用如下结构作为消息模板:
longmtype 指定了消息类型,它的取值为正整数。通常我们可以吧不同功能的消息,分别定义为不同消息类型,这样就可以共享一个消息队列了。
消息数据 :
成员metext制定了消息的数据,实际上只要就够的第一个成员维持long型不变,我们可以定义任意的数据类型甚至包括结构来描述消息数据:
相关函数 :
msgget |
作用 : 创建或打开消息队列 . |
头文件 : #include<sys/types.h> #include <sys/ipc.h> #include<sys/msg.h> |
函数原型 : int msgget(key_t key,int msgflg) |
key : 消息队列的键值 ,多个进程可通过他访问同一个消息队列,其中有个特殊值IPC_PRIVATE, |
用于创建当前进程的私有消息队列 . |
msgflg : 权限标志位 |
成功 : 消息队列 ID |
出错 : -1 |
msgsnd |
作用 : 添加消息 . 使用的函数是msgsnd() ,它把消息添加到已打开的消息队列末尾 . |
头文件 : #include<sys/types.h> #include <sys/ipc.h> #include<sys/msg.h> |
原型 : int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg) |
msg : 消息队列的队列 ID. |
msgp : 指向消息结构的指针 , 该消息结构msgbuf 通常如下 . |
struct msgbuf |
{ |
long mtype; /*消息类型,该结构必须从这个域开始*/ |
char mtext[1]; /*消息正文 自定大小 应该是*/ |
} |
msgsz : 消息正文的字节数 (不包括消息类型指针变量) |
msgflg : IPC_NOWAIT : 若消息无法立即发送(如当前消息队列已满), 函数会立即返回0 |
0 : msgsnd 调用阻塞直到发送成功为止 . |
msgrcv |
作用 : 读取消息。使用的函数是msgrcv(),它把消息从消息队列中取走,与FIFO不同的是,这里可以取走指定的某一条消息 |
函数原型 : int msgrcv(int msqid,void *msgp,size_t msgsz,long int msgtyp,int msgflg) |
msqid : 消息队列ID |
msgp : 消息缓冲区,同 msgsnd 中的msgp . |
msgsz : 消息正文的字节数 . |
msgtyp : 0 :接收消息队列中第一个消息 ;大于 0 : 接收消息队列中第一个类型为msgtyp的消息 |
小于0 : 接收消息队列中第一个类型值 不小于msgtyp绝对值且类型值最小的消息 . |
msgtyp 可以以非先进先出次序读消息. |
msgflg : IPC_NOWAIT 使操作不阻塞 , 如果没有指定类型的消息可用 ,则 msgrcv 返回 -1 . |
msgctl |
作用 : 控制消息队列。它可以完成多项功能。 |
函数原型 : int msgctl(int msgqid,int cmd,struct msgid_ds *buf) |
msqid : 消息队列 的 队列ID |
cmd : 命令参数 :IPC_STAT : 读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中 |
IPC_SET : 设置消息队列的数据结构msqid_ds中的ipc_pem域(IPC操作权限描述结构)值 ,这个值取自buf参数 |
IPC_RMID : 从系统内核中删除消息队列 |
buf : 描述消息队列的 msqid_ds 结构类型变量 |
成功 : 0 ; 出错 : -1 ; |
例 :
/********************************************************************************
*功能
* 本实验意在展示如何使用消息队列进行两个进程(发送端和接收端)之间的通信,
* 包括消息队列的创建、消息发送与读取、消息队列的撤销和删除等多种操作。
* 消息发送端进程和消息接收端进程不需要额外实现进程间的同步。
* 在本实验中,发送端发送的消息类型设置为该进程的进程号(可以取其他值),
* 因此接收端根据消息类型来确定消息发送者的进程号。注意这里使用了ftok()函数,
* 它可以根据不同的路径和关键字来产生标准的key。
********************************************************************************/
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BUFFSIZE 128
struct message
{
long msg_src; //消息类型
char msg_text[BUFFSIZE]; //消息正文
};
int main()
{
int qid; //消息队列ID
key_t key;
struct message msg;
//根据不同的路径和关键字产生标准的 key
if(( key = ftok(".",'b')) == -1 )
{
perror("frok");
exit(0);
}
//创建消息队列
if((qid=msgget(key,0666))==-1)
{
perror("msgget");
exit(0);
}
printf("open queue %d\n",qid);
while(1)
{
printf("Enter some message to queue : \n");
if((fgets(msg.msg_text,BUFFSIZE,stdin))==NULL)
{
perror("fgets error");
}
msg.msg_src = getpid();
if((msgsnd(qid,&msg,strlen(msg.msg_text),0))<0)
{
perror("message posted");
exit(0);
}
if(strncmp(msg.msg_text,"quit",4)==0)
{
break ;
}
}
exit(0);
}
接收程序 :
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BUFFSIZE 128
struct message
{
long msg_src; //消息进程来源
char msg_text[BUFFSIZE]; //消息正文
};
int main()
{
int qid;
key_t key;
struct message msg;
if((key = ftok(".",'b')) ==-1 )
{
perror("ftok");
exit(0);
}
if(( qid = msgget(key,0666|IPC_CREAT)) ==-1 )
{
perror("ftok");
exit(1);
}
printf("open queue %d\n",qid);
do
{
/*读取消息队列*/
memset(msg.msg_text,0,BUFFSIZE);
if(msgrcv(qid,(void *)&msg,BUFFSIZE,0,0)<0)
{
perror("msgrcv");
exit(1);
}
printf("The message from process %ld : %s",msg.msg_src,msg.msg_text);
}while(strncmp(msg.msg_text,"quit",4) < 0);
printf("I Will Remove the queue %d from kernel\n",qid);
//从系统内核移走消息队列
if(msgctl(qid,IPC_RMID,NULL)<0)
{
perror("msgctl");
exit(1);
}
exit(0);
}