什么是消息
消息(message)是一個格式化的可變長的信息單元。消息機制允許由一個進程給其它任意的進程發送一個消息。當一個進程收到多個消息時,可將它們排成一個消息隊列。消息使用二種重要的數據結構:一是消息首部,其中記錄了一些與消息有關的信息,如消息數據的字節數;二個消息隊列頭表,其每一表項是作為一個消息隊列的消息頭,記錄了消息隊列的有關信息。
消息機制的數據結構
-
消息首部: 記錄一些與消息有關的信息,如消息的類型、大小、指向消息數據區的指針、消息隊列的鏈接指針等。
-
消息隊列頭表: 其每一項作為一個消息隊列的消息頭,記錄了消息隊列的有關信息如指向消息隊列中第一個消息和指向最后一個消息的指針、隊列中消息的數目、隊列中消息數據的總字節數、隊列所允許消息數據的最大字節總數,還有最近一次執行發送操作的進程標識符和時間、最近一次執行接收操作的進程標識符和時間等。
消息隊列的描述符
UNIX中,每一個消息隊列都有一個稱為關鍵字(key)的名字,是由用戶指定的;消息隊列有一消息隊列描述符,其作用與用戶文件描述符一樣,也是為了方便用戶和系統對消息隊列的訪問。
涉及的系統調用
使用頭文件均為:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
msgget( )
創建一個消息,獲得一個消息的描述符。核心將搜索消息隊列頭表,確定是否有指定名字的消息隊列。若無,核心將分配一新的消息隊列頭,並對它進行初始化,然后給用戶返回一個消息隊列描述符,否則它只是檢查消息隊列的許可權便返回。
系統調用格式:
msgqid=msgget(key,flag);
參數定義
int msgget(key,flag)
key_t key;
int flag;
其中:
key是用戶指定的消息隊列的名字;flag是用戶設置的標志和訪問方式。如 IPC_CREAT |0400
: 是否該隊列已被創建。無則創建,是則打開;
msgqid 是該系統調用返回的描述符,失敗則返回-1。
msgsnd()
發送一消息。向指定的消息隊列發送一個消息,並將該消息鏈接到該消息隊列的尾部。
系統調用格式:
msgsnd(msgqid,msgp,size,flag);
參數定義:
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( ),核心須完成以下工作:
- 對消息隊列的描述符和許可權及消息長度等進行檢查。若合法才繼續執行,否則返回;
- 核心為消息分配消息數據區。將用戶消息緩沖區中的消息正文,拷貝到消息數據區;
- 分配消息首部,並將它鏈入消息隊列的末尾。在消息首部中須填寫消息類型、消息大小和指向消息數據區的指針等數據;
- 修改消息隊列頭中的數據,如隊列中的消息數、字節總數等。最后,喚醒等待消息的進程。
msgrcv( )
接受一消息。從指定的消息隊列中接收指定類型的消息。
系統調用格式:
msgrcv(msgqid,msgp,size,type,flag)
參數定義:
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系統調用,核心須完成下述工作:
-
對消息隊列的描述符和許可權等進行檢查。若合法,就往下執行;否則返回;
-
根據type的不同分成三種情況處理:
type=0,接收該隊列的第一個消息,並將它返回給調用者;
type為正整數,接收類型type的第一個消息;
type為負整數,接收小於等於type絕對值的最低類型的第一個消息。
- 當所返回消息大小等於或小於用戶的請求時,核心便將消息正文拷貝到用戶區,並從消息隊列中刪除此消息,然后喚醒睡眠的發送進程。但如果消息長度比用戶要求的大時,則做出錯返回。
msgctl( )
消息隊列的操縱。讀取消息隊列的狀態信息並進行修改,如查詢消息隊列描述符、修改它的許可權及刪除該隊列等。
系統調用格式:
msgctl(msgqid,cmd,buf);
參數定義:
int msgctl(msgqid,cmd,buf);
int msgqid,cmd;
struct msgqid_ds *buf;
其中,函數調用成功時返回0,不成功則返回-1。buf是用戶緩沖區地址,供用戶存放控制參數和查詢結果;cmd是規定的命令。命令可分三類:
-
IPC_STAT: 查詢有關消息隊列情況的命令。如查詢隊列中的消息數目、隊列中的最大字節數、最后一個發送消息的進程標識符、發送時間等;
-
IPC_SET: 按buf指向的結構中的值,設置和改變有關消息隊列屬性的命令。如改變消息隊列的用戶標識符、消息隊列的許可權等;
-
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;} /*由系統使用*/
}
參考程序
通過消息機制實現進程間的數據通信,client向server發送“I am client!”
client.c
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <string.h>
#define MSGKEY 75
struct msgform {
long mtype;
char mtext[1000];
} msg;
int msgqid;
void client() {
msgqid = msgget(MSGKEY, 0777); /*打開75#消息隊列*/
msg.mtype = 1;
// printf("(client)sent\n");
// msgsnd(msgqid,msgp,size,flag)
strcpy(msg.mtext, "I am client!\0");
msgsnd(msgqid, &msg, 1024, 0); /*發送消息*/
exit(0);
}
void main() {
client();
}
server.c
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#define MSGKEY 75
struct msgform {
long mtype;
char mtext[1000];
} msg;
int msgqid;
void server() {
msgqid = msgget(MSGKEY, 0777 | IPC_CREAT); /*創建75#消息隊列*/
// msgrcv(msgqid,msgp,size,type,flag)
msgrcv(msgqid, &msg, 1030, 0, 0); /*接收消息*/
printf("(server)received:\n");
printf("%s", msg.mtext);
printf("\n");
msgctl(msgqid, IPC_RMID, 0); /*刪除消息隊列,歸還資源*/
exit(0);
}
void main() {
server();
}
運行結果