1. 程式人生 > >Linux中程序間通訊機制----訊息佇列

Linux中程序間通訊機制----訊息佇列

一、什麼是訊息

訊息(message)是一個格式化的可變長的資訊單元。訊息機制允許由一個程序給其它任意的程序傳送一個訊息。當一個程序收到多個訊息時,可將它們排成一個訊息佇列。

1、訊息機制的資料結構

(1)訊息首部

記錄一些與訊息有關的資訊,如訊息的型別、大小、指向訊息資料區的指標、訊息佇列的連結指標等。

(2)訊息佇列頭表

其每一項作為一個訊息佇列的訊息頭,記錄了訊息佇列的有關資訊,如指向訊息佇列中第一個訊息和指向最後一個訊息的指標、佇列中訊息的數目、佇列中訊息資料的總位元組數、佇列所允許訊息資料的最大位元組總數,還有最近一次執行傳送操作的程序識別符號和時間、最近一次執行接收操作的程序識別符號和時間等。

2、訊息佇列的描述符

UNIX中,每一個訊息佇列都有一個稱為關鍵字(key)的名字,是由使用者指定的;訊息佇列有一訊息佇列描述符,其作用與使用者檔案描述符一樣,也是為了方便使用者和系統對訊息佇列的訪問。

二、涉及的系統呼叫

下面的函式使用的標頭檔案:

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/msg.h>

  1. msgget( )

建立一個訊息,獲得一個訊息的描述符。

系統呼叫格式:int msgqid=msgget(key,flag)

引數定義

key_t key;

int flag;

key是使用者指定的訊息佇列的名字;flag是使用者設定的標誌和訪問方式。如

IPC_CREAT |0400     是否該佇列已被建立。無則建立,是則開啟;

IPC_EXCL |0400        是否該佇列的建立應是互斥的。

msgqid 是該系統呼叫返回的描述符,失敗則返回-1。

  1. msgsnd()

傳送一訊息。向指定的訊息佇列傳送一個訊息,並將該訊息連結到該訊息佇列的尾部。

系統呼叫格式:int msgsnd(msgqid,msgp,size,flag)

引數定義:

int msgqid,size,flag;

struct msgbuf * msgp;

其中msgqid是返回訊息佇列的描述符;msgp是指向使用者訊息緩衝區的一個結構體指標,包括訊息型別和訊息正文,即

{

long  mtype;             /*訊息型別*/

char  mtext[ ];           /*訊息的文字*/

}

size指示由msgp指向的字元陣列的長度;即訊息的長度。這個陣列的最大值由MSG-MAX( )系統可呼叫引數來確定。

flag 規定當核心用盡內部緩衝空間時應執行的動作: 程序是等待,還是立即返回。若在標誌flag中未設定IPC_NOWAIT位,則當該訊息佇列中的位元組數超過最大值時,或系統範圍的訊息數超過某一最大值時,呼叫msgsnd程序睡眠。若是設定IPC_NOWAIT,則在此情況下,msgsnd立即返回。

對於msgsnd( ),核心須完成以下工作:

(1)對訊息佇列的描述符和許可權及訊息長度等進行檢查。若合法才繼續執行,否則返回;

(2)核心為訊息分配訊息資料區。將使用者訊息緩衝區中的訊息正文,拷貝到訊息資料區;

(3)分配訊息首部,並將它鏈入訊息佇列的末尾。在訊息首部中須填寫訊息型別、訊息大小和指向訊息資料區的指標等資料;

(4)修改訊息佇列頭中的資料,如佇列中的訊息數、位元組總數等。最後,喚醒等待訊息的程序。

  1. msgrcv( )

接受一訊息。從指定的訊息佇列中接收指定型別的訊息。

系統呼叫格式:int msgrcv(msgqid,msgp,size,type,flag)

引數定義:

int msgqid,size,flag;

struct msgbuf *msgp;

long type;

其中,msgqid,msgp,size,flag與msgsnd中的對應引數相似,type是規定要讀的訊息型別,

flag規定倘若該佇列無訊息,核心應做的操作。如此時設定了IPC_NOWAIT標誌,則立即返回,若在flag中設定了MS_NOERROR,且所接收的訊息大於size,則核心截斷所接收的訊息。

對於msgrcv系統呼叫,核心須完成下述工作:

(1)對訊息佇列的描述符和許可權等進行檢查。若合法,就往下執行;否則返回;

(2)根據type的不同分成三種情況處理:

type=0,接收該佇列的第一個訊息,並將它返回給呼叫者;

type為正整數,接收型別type的第一個訊息;

type為負整數,接收小於等於type絕對值的最低型別的第一個訊息。

(3)當所返回訊息大小等於或小於使用者的請求時,核心便將訊息正文拷貝到使用者區,並從訊息佇列中刪除此訊息,然後喚醒睡眠的傳送程序。但如果訊息長度比使用者要求的大時,則做出錯返回。

  1. msgctl( )

訊息佇列的操縱。讀取訊息佇列的狀態資訊並進行修改,如查詢訊息佇列描述符、修改它的許可權及刪除該佇列等。

系統呼叫格式: int msgctl(msgqid,cmd,buf);

引數定義:

int msgqid,cmd;

struct msgqid_ds *buf;

其中,函式呼叫成功時返回0,不成功則返回-1。buf是使用者緩衝區地址,供使用者存放控制引數和查詢結果;cmd是規定的命令。命令可分三類:

(1)IPC_STAT。查詢命令。如查詢佇列中的訊息數目、佇列中的最大位元組數、最後一個傳送訊息的程序識別符號、傳送時間等;

(2)IPC_SET。按buf指向的結構中的值,設定和改變有關訊息佇列屬性的命令。如改變訊息佇列的使用者識別符號、訊息佇列的許可權等;

(3)IPC_RMID。消除訊息佇列的識別符號。

msgqid_ds 結構定義如下:

struct msgqid_ds

{

 struct  ipc_perm  msg_perm;     /*許可權結構*/

  short  pad1[7];                     /*由系統使用*/

  ushort msg_qnum;               /*佇列上訊息數*/

  ushort msg_qbytes;              /*佇列上最大位元組數*/

  ushort msg_lspid;               /*最後傳送訊息的PID*/

  ushort msg_lrpid;               /*最後接收訊息的PID*/

  time_t msg_stime;               /*最後傳送訊息的時間*/

  time_t msg_rtime;               /*最後接收訊息的時間*/

  time_t msg_ctime;               /*最後更改時間*/

};

struct ipc_perm

{

  ushort uid;                     /*當前使用者*/

  ushort gid;                     /*當前程序組*/

  ushort cuid;                    /*建立使用者*/

  ushort cgid;                    /*建立程序組*/

  ushort mode;                   /*存取許可權*/

  { short pid1; long pad2;}         /*由系統使用*/  

}

三、編寫程式

建立兩個子程序,子程序1建立一個訊息佇列,並使用訊息機制向子程序2傳送一條訊息,傳送結束後輸出列印:Data sent! 子程序2接收到訊息後,輸出列印接收的訊息字元,並輸出列印:Data received!

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/msg.h> #include <sys/ipc.h> #define MSGKEY 75 struct msgform { long mtype; char mtext[1000]; }; void main( ) { int p1,p2; while((p1=fork( ))-1); //建立程序1 if (p1>0) //父程序1 { wait(NULL); //等待子程序1結束 while((p2=fork( ))-1); //建立程序2 if(p2==0) //父程序2 { wait(NULL); //等待子程序2結束 exit(0); } else //子程序2 { int msgqidRecs=msgget(MSGKEY,0777); //開啟訊息佇列 MSGKEY struct msgform msgRecs; //建立用於接收訊息的緩衝區 msgrcv(msgqidRecs,&msgRecs,1024,0,0); //接收訊息 printf("%s\n",msgRecs.mtext); //列印接收到的訊息 printf(“Data received!\n”); msgctl(msgqidRecs,IPC_RMID,0); //清楚訊息佇列,釋放資源 //printf("%d\n",msgqidRecs); //檢查是否是目標訊息佇列 } } else //子程序1 { int msgqidSend=msgget(MSGKEY,IPC_CREAT|0777); //建立訊息佇列MSGKEY,返回給msgqidSend struct msgform msgSend; //建立用於傳送訊息的緩衝區 strcpy(msgSend.mtext,“This is a message from child 2!”); //消設定息內容 msgsnd(msgqidSend,&msgSend,1024,0); //傳送訊息 printf(“Data sent!\n”); sleep(1); exit(1); //printf("%d\n",msgqidSend); //列印訊息佇列描述符 } }