消息傳遞

消息傳遞

進程通信的方法包括共享數據結構、消息隊列等。消息傳遞是進程間傳遞數據的方法。消息隊列有發送原語和接收原語。UNIX/Linux消息傳遞的編程有所不同。

正文


消息傳遞是進程間數據傳遞的一種方法,進程採用消息(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\n");
exit(-1);
}
if(qid=msgget(key,IPC_CREAT|0666))==-1)
{
printf("msgget fail\n");
exit(-2);
}
printf("the message quene is %d\n",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\n");
exit(-4);
}
if((msgrcv(qid,&msg,512,0,0))<0) //讀消息隊列的消息
{
printf(“msgrcv fail\n");
exit(-5);
}
printf("reading message:%s\n",(&msg)->msg_text);//顯示輸出消息內容
if((msgctl(qid,IPC_RMID,NULL))<0)
{
printf("msgctl fail\n");
exit(-6);
}
exit(0);
}

4.常用命令

顯示系統中所有的消息隊列
$>ipcs –q
$>./svmsg_lsaw
Linux系統最多有16個消息隊列,每個消息最大為8192位元組。