Linux作業系統--訊息佇列
1、訊息佇列的特點
(1)訊息佇列是訊息的連結串列,具有特定的格式,存放在記憶體中並由訊息佇列識別符號標識.
(2)訊息佇列允許一個或多個程序向它寫入與讀取訊息.
(3)管道和命名管道都是通訊資料都是先進先出的原則。
(4)訊息佇列可以實現訊息的隨機查詢,訊息不一定要以先進先出的次序讀取,也可以按訊息的型別讀取.比FIFO更有優勢。
目前主要有兩種型別的訊息佇列:POSIX訊息佇列以及System V訊息佇列,System V訊息佇列目前被大量使用。System V訊息佇列是隨核心持續的,只有在核心重起或者人工刪除時,該訊息佇列才會被刪除。
二、訊息佇列的建立
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t, key, int msgflg);
引數key為訊息佇列的鍵值,通常是一個長整型,可以設定為任何整數值。該引數可以使用者直接指定,也可以呼叫ftok函式來生成,如果直接設為IPC_RPIVATE,表示總是建立新的訊息佇列。
引數msgflg用來建立訊息佇列並設定存取許可權,例如IPC_CREAT|0666,它表示建立一個當前使用者,使用者組以及其他使用者有讀寫許可權的訊息佇列。如果加上IPC_EXCL,則表示只有在指定的訊息佇列不存在時,才會建立新的訊息佇列。函式執行成功後,返回訊息佇列的識別符號,否則返回-1.
ftok函式的一般形式如下:
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname,int proj_id);
引數pathname用來指定程序有存取許可權的一個路徑。
引數proj_id用來指定某個特定字元。函式執行成功後,返回一個訊息佇列的鍵值,否則返回-1.
//使用msgget函式來建立一個訊息佇列 #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> int main() { int qid; key_t key; key=ftok("/home/", 'a'); //生成訊息佇列的鍵值 if (key<0) { perror("ftok error"); exit(1); } qid =msgget(key,IPC_CREAT|0666); //建立一個訊息佇列 if (qid<0) { perror("msgget error"); exit(1); } else { printf("Done!\n"); } return 0; }
通過ipcs -q 可以看到建立的訊息佇列的鍵值為 0X61020005,識別符號為65536.
三、訊息佇列的控制
1、msgctl函式 該函式用來對訊息佇列進行各種操作,例如修改訊息佇列的屬性,清除佇列中的所有訊息等。#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msgid, int command, struct msgid_ds *buf);
引數msqid為訊息佇列的識別符號;
引數cmd為所要進行的操作,包括以下三種:
IPC_STAT :獲取訊息佇列的狀態,返回的資訊將會儲存在buf指向的msqid_ds結構中。
IPC_SET :設定訊息佇列的屬性,要設定的屬性儲存在buf指向的msqid_ds結構中。
IPC_RMID: 刪除訊息佇列,同時清除佇列中的所有訊息。
msqid_ds結構的定義如下:
struct msqid_ds
{
struct ipc_perm msg_perm; //存取許可權
struct msg *msg_first; //訊息佇列頭指標
struct msg *msg_last; //訊息佇列尾指標
__kernel_time_t msg_stime; //最後一次插入訊息佇列訊息的時間
__kernel_time_t msg_rtime; //最後一次接收訊息即刪除佇列中一個訊息的時間
__kernel_time_t msg_ctime; //最後一次修改的時間
struct wait_queue *wwait; //傳送訊息等待程序佇列
struct wait_queue *rwait;
unsigned short msg_cbytes; //當前佇列的位元組數
unsigned short msg_qnum; //訊息佇列中的訊息個數
unsigned short msg_qbytes; //佇列的最大位元組數
__kernel_ipc_pid_t msg_lspid; //最後一次訊息傳送程序的pid
__kernel_ipc_pid_t msg_lrpid; //最後一次訊息傳送程序的pid
};
訊息佇列被建立後,即使不再使用,系統也不會自動清理。
//清除建立的訊息佇列
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main()
{
int qid;
int status;
printf("請輸入需要刪除的訊息佇列的識別符號:");
scanf("%d",&qid);
status=msgctl(qid, IPC_RMID, NULL); //刪除指定的訊息佇列
if(status<0)
{
perror("msgctl error");
exit(1);
}
printf("removed!\n");
return 0;
}
四、訊息佇列的讀寫
1、msgsnd函式
該函式用來把訊息新增到訊息佇列中。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsend(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
引數msqid為訊息佇列的識別符號,要寫入的訊息儲存在引數msgp所指向的msgbuf結構中,訊息的 大小由引數msgsz決定,引數msgflg用來設定訊息佇列沒有足夠空間時msgsnd函式執行的動作,例如是否等待。
msgbuf結構用來包含一個訊息,其定義如下:
struct msgbuf
{
long mtype; //訊息的型別
char mtext[];//訊息的內容
};
當程序呼叫msgsnd函式寫入一個訊息時,系統會首先檢查程序對該訊息佇列是否有寫許可權,接著檢視訊息的長度是否超過系統允許的範圍,以及訊息佇列的剩餘空間情況等。如果條件都符合,系統會為訊息分配訊息頭和訊息資料區,將訊息從使用者空間複製到訊息資料區後,並鏈入訊息佇列的尾部。同時,在訊息頭中填寫訊息的型別、大小以及指向訊息資料區的指標等。
如果msgsnd函式被阻塞,則在下面某個條件滿足時解除阻塞。
(1)訊息佇列中有容納要寫入訊息的空間。
(2)訊息佇列被刪除。
(3)程序被訊號中斷。
msgrcv函式用來從訊息佇列中讀取(接收)一個訊息。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msqid,struct msgbuf *msgp,int msgsz,long msgtyp,int msgflg);
引數msqid為為訊息佇列的識別符號,訊息返回後將會儲存在引數msgp指向的msgbuf結構中,該結構mtext成員的長度由引數msgsz決定,引數msgtyp為請求讀取訊息的型別,有如下3種情況:
(1)msgtyp=0:返回訊息佇列中的第一個訊息。
(2)msgtyp>0:返回訊息佇列中該型別的第一個訊息。
(3)msgtyp<0:在型別小於等於msgtyp絕對值的所有訊息中,返回型別值最小的第一個訊息。
當程序呼叫msgrcv函式讀取一個訊息時,如果返回訊息小於等於使用者請求,系統會將訊息複製到使用者空間,然後從訊息佇列中刪除該訊息,並喚醒阻塞的寫入程序;如果訊息大於使用者請求,則返回錯誤資訊。
如果msgrcv函式被阻塞,則在下面某個條件滿足時解除阻塞:
(1)訊息佇列中有了滿足條件的訊息。
(2)訊息佇列被刪除。
(3)程序被訊號中斷。
示例:
傳送訊息程式:
//實現兩個程序間的訊息傳遞,訊息傳送程式
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSG_SIZE 128
struct msgbuf //定義訊息結構
{
long mtype; //訊息型別
char mtext[MSG_SIZE];//訊息的內容
};
int main()
{
int qid;
key_t key;
int ret;
struct msgbuf buf; //訊息緩衝區
key=ftok("/home", 'a'); //生成訊息佇列的鍵值
if (key<0)
{
perror("ftok error");
exit(1);
}
qid=msgget(key, IPC_CREAT|0666); //建立一個訊息佇列
if (qid<0)
{
perror("msgget error");
exit(1);
}
while (1)
{
printf("input the message:");
fgets(buf.mtext,MSG_SIZE,stdin); //從鍵盤輸入訊息的內容
if (strncmp(buf.mtext, "exit",4)==0) //如果鍵盤輸入exit,退出迴圈
{
buf.mtype=getpid();
ret=msgsnd(qid, &buf, MSG_SIZE, 0);
break;
}
buf.mtype=getpid(); //訊息的型別,這裡設定為當前程序的識別符號
ret=msgsnd(qid, &buf, MSG_SIZE, 0); //向訊息佇列中傳送一個訊息
if (ret<0)
{
perror("msgsnd error");
exit(1);
}
else
{
printf("send!\n");
}
}
return 0;
}
接收端:
//實現兩個程序間的訊息傳遞,編寫訊息接收程式
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define MSG_SIZE 128
struct msgbuf //定義訊息結構
{
long mtype; //訊息型別
char mtext[MSG_SIZE];//訊息的內容
};
int main()
{
int qid;
key_t key;
int ret;
struct msgbuf buf;
key=ftok("/home", 'a');
if (key<0)
{
printf("ftok error");
exit(1);
}
qid=msgget(key,IPC_EXCL|0666); //開啟訊息佇列
if (qid<0)
{
perror("msgget error");
exit(1);
}
while (1)
{
memset(&buf, 0, sizeof(buf));
ret=msgrcv(qid, &buf, MSG_SIZE, 0, 0); //讀取訊息佇列中的一個的訊息
if (ret<0)
{
perror("msgrcv error");
exit(1);
}
else
{
if (strncmp(buf.mtext, "exit",4)==0)
{
break;
}
printf("received message:\n");
printf("type=%ld,length=%ld,text:%s\n",buf.mtype,strlen(buf.mtext)-1,buf.mtext); //輸入訊息
}
}
return 0;
}