消息传递是进程间数据传递的一种方法,进程采用消息(message)的方法,由发送进程向接收进程的消息队列发送一个消息,接收进程在合适的时候取出。
因此,UNIX系统的消息传递就是消息队列,在进程通信中可不使用共享地址空间的方式通信。 消息队列又称为直接消息传递,而信箱称为间接消息传递。信箱不能算是发送进程的,也不能算是接收进程的,这与消息队列不同。消息队列必须由接收进程,有时候是发送进程向操作系统申请,因此属于这两个进程之一。消息队列是
进程通信的方法。
原语
消息队列的原语包括发送原语和接收原语,具有原子性,不可分割。
1.数据结构
把消息缓冲作为进程通信工具,首先由Hansan提出,并在RC4000系统实现,广泛应用在系统内进程之间的通信。
消息缓冲的数据结构是消息缓冲区,其描述为:
type message buffer=record
sender: 消息发送者名
size:消息长度
text:消息正文
next:指向下一个消息缓冲区的指针
end
为实现消息缓冲通信,还应在PCB中增加一些数据项,包括两种信号量,其描述为:
type processcontrol block=record
…….
mq: 消息队列队首指针
mutex:消息队列的互斥信号量,消息队列是临界资源
Sm: 消息队列的计数信号量,发送进程和接收进程同步
……
end
2. 发送原语
首先在进程的地址空间设置一个发送区a,再填写相关内容,然后调用发送原语send(receiver,message)把消息发送到接收进程的消息队列PCB(B).mq中。发送原语描述如下:
procedure send(receiver,a)
begin
getbuf(a.size,i); 根据发送区a的长度申请消息缓冲区i
i.sender:=a.sender;
i.size:=a.size;
i.text:=a.text;
i.next:=0;
getid(PCB set,receiver,j); 获得接收进程内部标识符j
P(j.mutex);
insert(j.mq.i);
V(j.mutex);
V(j.sm);
end
3. 接收原语
接收进程从自己的消息队列中摘下第一个消息缓冲区,并将它复制到指定的消息接收区b内,b在接收进程的地址空间内。所以消息队列并不在进程的地址空间中。
procedure receiver(b)
begin
j:=internal name;
P(j.sm);
P(j.mutex);
move(j.mq,i);
V(j.mutex);
b.sender:=i.sender;
b.size:=i.size;
b.text:=i.text;
end
UNIX System V消息队列
消息队列提供一种机制允许进程不必通过共享地址空间实现通信和同步,允许进程以消息的形式传递数据。
1.数据结构
消息缓冲区的数据结构:
struct mymsh{
long mtype; //消息类型
char mtext[]; //消息正文,可以是一个结构
}
消息的数据结构
struct msg_msg{
struct list_head m_list;
long m_type; //消息类型
int m_ts; //消息大小
struct msg_message* next; //下一个消息位置
void *security; //真正消息位置
}
消息队列的数据结构
2. 系统调用
(1)创建一个消息队列
#include
#include
int msgget(key_t key, int msgflg);
返回值:成功,返回消息队列标识符;错误,返回-1。
第一个参数是IPC——PRIVATE或者ftok()返回的一个键。Msgflg参数是消息队列权限,可标记为OR( | ),IPC_CREAT,IPC_EXCL等。
(2)发送消息
int msgsnd(int msqid, void *msgp,size_t msgsz, int msgflg);
返回值:成功返回0,错误返回-1。
参数1:指定的消息队列标识符,由msgget()生成。参数2用户定义的缓冲区,参数3:消息长度,参数4:越界处理。
将新的消息发送到接收进程消息队列中。
(3)接收消息
ssize_t msgrcv(int msqid, void *msgp,size_t maxmagsz,long msgtyp,int msgflg)
返回值:成功接收的字符数,错误返回-1。
参数1:从哪一个消息队列中读取消息,参数2:读取消息的缓冲区。参数4:消息类型。
接收进程从消息队列取走消息。
3.消息队列编程
struct msgmbuf
{
long msg type;
char msg_text[512];
};
main()
{
int qid;
key_t key;
int len;
struct msgmbuf msg;
if((key=ftok(".",a))==-1)
{
printf("ftok fail
");
exit(-1);
}
if(qid=msgget(key,IPC_CREAT|0666))==-1)
{
printf("msgget fail
");
exit(-2);
}
printf("the message quene is %d
",qid);
puts("please input the message:");
if((fgets((&msg)->msg_text,512,stdin))==NULL)
{
puts("no message");
exit(-3);
}
msg.msg_type=getpid();
len=strlen(msg.msg.text);
if((msgsnd(qid,&msg,len,0))<0) //向消息队列中发消息,注意这个程序是简化的程序,并没有向接收进程的消息队列发消息
{
printf("msgsnd fail
");
exit(-4);
}
if((msgrcv(qid,&msg,512,0,0))<0) //读消息队列的消息
{
printf(“msgrcv fail
");
exit(-5);
}
printf("reading message:%s
",(&msg)->msg_text);//显示输出消息内容
if((msgctl(qid,IPC_RMID,NULL))<0)
{
printf("msgctl fail
");
exit(-6);
}
exit(0);
}
4.常用命令
显示系统中所有的消息队列
$>ipcs –q
$>./svmsg_lsaw
Linux系统最多有16个消息队列,每个消息最大为8192字节。