1.1. 什麼是消息隊列
消息隊列提供了一種從一個程序向另一個程序發送一個資料塊的方法。每個資料塊都被認為含有一個類型,接收程序可以獨立地接收含有不同類型的資料結構。我們可以通過發送消息來避免命名管道的同步和阻塞問題。但是消息隊列與命名管道一樣,每個資料塊都有一個最大長度的限制。
1.2. 相關的接口函數介紹
Linux提供了一系列消息隊列的函數接口來讓我們友善地使用它來實作程序間的通信。它的用法與其他兩個System V PIC機制,即信号量和共享記憶體相似。
1、 ftok函數: 動态擷取key值
|
2、 msgget函數:建立消息
|
3、 msgsnd函數:發送消息
|
4、 msgrcv函數:接收消息
|
5、 msgctl函數:控制消息
|
1.3. 核心限制
消息隊列是IPC資源資訊中的一種,是以可以通過ipcs确定系統的目前IPC限制及已使用的資源狀況。
檢視IPC:ipcs [-u](檢視目前的IPC資源狀況)、[-l](可以檢視IPC限制值)
建立IPC:ipcmk [ [-q msqid] [-m shmid] [-s semid] [-Qmsgkey] [-M shmkey] [-S semkey] ... ]
删除IPC:ipcrm [[-Q] [-S <size>] [-M <nsems>]]
|
修改消息隊列參數如下:
1、 永久修改:
在root使用者下修改/etc/sysctl.conf配置檔案
具體方法為:在sysctl.conf中加上 kernel.msgmni=10kernel.msgmax=2048 kernel.msgmnb=65535,然後運作sysctl –p即可修改。
在Red Hat中,rc.sysinit初始化腳本将自動讀取/etc/sysctl.conf檔案
而在SUSE Linux中,還需要激活 boot.sysctl服務(chkconfig boot.sysctl on)
2、 臨時修改:
root使用者下sysctl -w kernel.msgmni=10 kernel.msgmax=2048 kernel.msgmnb=65535
1.4. 執行個體
發送資訊的程式MsgSend.c的源代碼如下:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/msg.h>
#include <errno.h>
#define MSGKEY 1024
#define MAX_TEXT 512
struct msg_st
{
long int msg_type; //第一個參數必須是long的消息類型
char text[MAX_TEXT]; //此後可以定義多個數組和其他值
};
#define SPACE " "
#define PRINT_X(var1, var2) printf(""#var2"%.*s: 0X%08X\n", \
strlen(SPACE) - strlen(#var2), SPACE, var1.var2)
#define PRINT_D(var1, var2) printf(""#var2"%.*s: %d\n", \
strlen(SPACE) - strlen(#var2), SPACE, var1.var2)
void Show(int msqid)
{
int iRet = 0;
struct msqid_ds buf;
iRet = msgctl(msqid, IPC_STAT, &buf);
if(0 != iRet)
{
printf("msgctl error, errno=%d [%s]\n", errno, strerror(errno));
return ;
}
printf("\n");
PRINT_X(buf.msg_perm, __key);
PRINT_D(buf.msg_perm, uid);
PRINT_D(buf.msg_perm, gid);
PRINT_D(buf.msg_perm, cuid);
PRINT_D(buf.msg_perm, cgid);
PRINT_X(buf.msg_perm, mode);
PRINT_X(buf.msg_perm, __seq);
PRINT_X(buf, msg_stime);
PRINT_X(buf, msg_rtime);
PRINT_X(buf, msg_ctime);
PRINT_D(buf, __msg_cbytes);
PRINT_D(buf, msg_qnum);
PRINT_D(buf, msg_qbytes);
PRINT_X(buf, msg_lspid);
PRINT_X(buf, msg_lrpid);
printf("\n");
}
int main()
{
int running = 1;
struct msg_st data;
char buffer[MAX_TEXT];
int msgid = -1;
//建立消息隊列
msgid = msgget(MSGKEY, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed, error=%d [%s]\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
Show(msgid);
//向消息隊列中寫消息,直到寫入end
while(running)
{
//輸入資料
printf("Enter some text: ");
fgets(buffer, MAX_TEXT, stdin);
data.msg_type = 1; //注意此處設定消息類型
strcpy(data.text, buffer);
/* 向隊列發送資料, 如果是字元串,則參數3可以修改為字元串的長度,可以節省記憶體
msgsnd(msgid, (void*)&data, strlen(data.text), 0) */
if(msgsnd(msgid, (void*)&data, MAX_TEXT, 0) == -1)
{
fprintf(stderr, "msgsnd failed, error=%d [%s]\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
Show(msgid);
//輸入end結束輸入
if(strncmp(buffer, "end", 3) == 0)
{
running = 0;
}
sleep(1);
}
exit(EXIT_SUCCESS);
}
接收資訊的程式MsgReceive.c的源代碼如下:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#define MSGKEY 1024
#define MAX_TEXT 512
struct msg_st
{
long int msg_type;
char text[MAX_TEXT];
};
int main()
{
int running = 1;
int msgid = -1;
struct msg_st data;
long int msgtype = 0; //此處設定為0,表示接收所有消息
//建立消息隊列
msgid = msgget(MSGKEY, 0666 | IPC_CREAT);
if(msgid == -1)
{
fprintf(stderr, "msgget failed, error=%d [%s]\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
//從隊列中擷取消息,直到遇到end消息為止
while(running)
{
if(msgrcv(msgid, (void*)&data, MAX_TEXT, msgtype, 0) == -1)
{
fprintf(stderr, "msgrcv failed, error=%d [%s]\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
printf("You wrote: %s\n",data.text);
//遇到end結束
if(strncmp(data.text, "end", 3) == 0)
{
running = 0;
}
}
//删除消息隊列
if(msgctl(msgid, IPC_RMID, 0) == -1)
{
fprintf(stderr, "msgctl(IPC_RMID) failed, error=%d [%s]\n",
errno, strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
1.5. 注意事項
一、每次msgrcv一個消息時:
1、那個消息會在核心中移除
2、每次msgrcv都隻會給一個消息出來,不管你用多大的buf來接收,都是可以的。如果msgrcv的bufSize小于實際的該消息的大小,那麼可以設定一個标志(MSG_NOERROR):表示截斷。 如果不設定,那麼會報錯,取不出來
消息滿了,則預設0為阻塞,直到有了空間位置,才能snd消息進入到核心
消息空了,則預設0為阻塞,直到有了一個消息位置,才能 rcv消息進入到程序記憶體
二、如果指定msgflg = MSG_NOERROR,如果函數取得的消息長度大于msgsz,将隻傳回msgsz 長度的資訊,剩下的部分被丢棄了。如果不指定這個參數,E2BIG 将被傳回,而消息則留在隊列中不被取出
三、如果使用msgctl控制單個隊列的大小(msg_qbytes),當設定的值小于系統設定的值時,可以生效;當設定的值大于系統設定的值時,設定會失敗,此時還是原來系統設定的值