什麼是消息隊列
消息隊列的概念
消息隊列(Message,簡稱MQ):消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。每個數據塊都被認爲是有一個類型,接收者進程接收的數據塊可以有不同的類型值。消息隊列也有管道一樣的不足,就是每個消息的最大長度是有上限的,每個消息隊列的總的字節數是有上限的,系統上消息隊列的總數也有一個上限。
消息隊列傳遞的是消息,消息即是我們需要在進程間傳遞的數據,消息隊列採用鏈表來實現消息隊列,該鏈表是由系統內核維護,系統中可能有很多的消息隊列,每個消息隊列用消息隊列描述符(消息隊列ID:qid)來區分,qid是唯一的,用來區分不同的消息隊列。在進行進程間通信時,一個進程將消息加到消息隊列尾端,另一個進程從消息隊列中取消息(不一定以先進先出來取消息,也可以按照消息類型字段取消息),這樣就實現了進程間的通信。
下面我畫了一個簡單的MQ模型,進程A向內核維護的消息隊列中發消息,進程B從消息隊列中取消息,從而實現了A和B的進程間通信。
消息隊列用到的函數
1、msgget
函數原型
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
函數說明:該函數用來創建消息隊列ID,成功返回一個非負整數,即該共享內存段的標識碼,失敗返回-1.
參數說明:第一個參數key是ftok()返回的key_t類型鍵值;
第二個參數msgflg是創建標誌:IPC_CREAT,不存在則創建,存在則返回已有的mqid,IPC_CREAT|IPC_EXCL,不存在則創建,存在則返回出錯。
2、msgsnd
函數原型
#include <sys/msg.h>
int msgsnd(int msqid ,const void *msgp, size_t msgsz, int msgflg);
函數說明:用來發送一個消息,必須要用寫消息隊列的權限。成功返回0,失敗返回-1,並設置errno。
參數說明:第一個參數msgid是由msgget函數返回的消息隊列ID;
第二個參數msgp是一個指針,它指向要發送的消息結構體類型的變量,消息結構在兩方面收到制約。首先,它必須小於系統規定的上限值;其次,它必須以一個long int 長整數開始,接收者函數將利用這個長整數確定消息的類型,其參考類型定義形式如下:
typedf struct s_msgbuf
{
long mtype;
char mtext[512];
}t_msgbuf
第三個參數msgsz是要發送消息的長度;
第四個參數msgflg控制着當前消息隊列滿或到達系統上限時即將要發生的事情,設置爲IPC_NOWAIT表示隊列滿不等待,返回EAGAIN錯誤。
3、msgrcv
函數原型
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
函數說明:用來從一個消息隊列接收消息,成功返回實際放到接收緩衝區裏去的字符個數,失敗返回-1,並設置errno。
參數說明:第一個參數msgid是由msgget函數返回的消息隊列ID;
第二個參數msgp是一個指針,它指向準備接收的消息;
第三個參數msgsz是msgp指向的消息長度,這個長度不含保存消息類型的那個long int長整型;
第四個參數msgtype是消息的類型,它可以實現接收優先級的簡單形式;
msgtype=0返回隊列第一條消息
msgtype>0返回隊列第一條類型等於msgtype的消息
msgtype<0返回隊列第一條類型小於等於msgtype絕對值的消息,並且是滿足條件的消息類型最小的消息
第五個參數msgflg控制着隊列中沒有相應類型的消息可供接收時將要發生的事;
msgflg=IPC_NOWAIT,隊列沒有可讀消息不等待,返回ENOMSG錯誤
msgflg=MSG_NOERROR,消息大小超過msgsz時被截斷
msgtype>0且msgflg=MSG_EXCEPT,接收類型不等於msgtype的第一條消息。
4、msgctl
函數原型
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
函數說明:該函數用於控制消息隊列
參數說明:第一個參數msgid是由msgget函數返回的消息隊列ID;
第二個參數cmd是要採取的操作,它可以採取下面的三個值:
IPC_STAT:把msqid_ds結構中的數據設置爲消息隊列的當前關聯值;
IPC_SET:如果進程有足夠的權限,就把消息隊列的當前關聯值設置爲msqid_ds結構中給出的值;
IPC_RMID:刪除消息隊列;
第三個參數:buf是一個結構指針,它指向存儲消息隊列的相關信息的buf;
消息隊列的代碼示例
瞭解了消息隊列和相應的函數,對於消息隊列的使用可以分爲四個步驟:
- 創建和訪問MQ
- 發送消息
- 接收消息
- 刪除MQ
下面我們將寫兩個進程,其中進程msgqueue_sender往內核的消息隊列裏寫入內容“Ping”,而進程msgqueue_recver則從消息隊列裏讀出並打印該消息。
第一個進程msgqueue_sender
/*********************************************************************************
* Copyright: (C) 2020 makun<[email protected]>
* All rights reserved.
*
* Filename: shared_mem_write.c
* Description: This file
*
* Version: 1.0.0(2020年03月21日)
* Author: makun <[email protected]>
* ChangeLog: 1, Release initial version on "2020年03月21日 02時56分59秒"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define FTOK_PATH "/dev/zero"
#define FTOK_PROJID 0x22
typedef struct s_msgbuf
{
long mtype;
char mtext[512];
}t_msgbuf;
int main (int argc, char **argv)
{
key_t key;
int msgid;
t_msgbuf msgbuf;
int msgtype;
int i;
if( (key =ftok(FTOK_PATH, FTOK_PROJID)) <0)
{
printf("ftok get IPC token failure:%s\n",strerror(errno));
return -1;
}
msgid =msgget(key, IPC_CREAT |0666);
if( msgid < 0)
{
printf("shmget create shared memroy failure:%s\n",strerror(errno));
return -2;
}
msgtype= (int)key;
printf("key %d msgid %d msgypte %d\n", (int)key,msgid,msgtype);
for(i=0; i<4; i++)
{
msgbuf.mtype =msgtype;
strcpy(msgbuf.mtext, "Ping");
if( msgsnd(msgid,&msgbuf,sizeof(msgbuf.mtext),IPC_NOWAIT) <0)
{
printf("msgsnd() send message failure: %s\n", strerror(errno));
break;
}
printf("send message :%s\n",msgbuf.mtext);
sleep(1);
}
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
第二個進程msgqueue_rever
/*********************************************************************************
* Copyright: (C) 2020 makun<[email protected]>
* All rights reserved.
*
* Filename: shared_mem_write.c
* Description: This file
*
* Version: 1.0.0(2020年03月21日)
* Author: makun <[email protected]>
* ChangeLog: 1, Release initial version on "2020年03月21日 02時56分59秒"
*
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define FTOK_PATH "/dev/zero"
#define FTOK_PROJID 0x22
typedef struct s_msgbuf
{
long mtype;
char mtext[512];
}t_msgbuf;
int main (int argc, char **argv)
{
key_t key;
int msgid;
t_msgbuf msgbuf;
int msgtype;
int i;
if( (key =ftok(FTOK_PATH, FTOK_PROJID)) <0)
{
printf("ftok get IPC token failure:%s\n",strerror(errno));
return -1;
}
msgid =msgget(key, IPC_CREAT |0666);
if( msgid < 0)
{
printf("shmget create shared memroy failure:%s\n",strerror(errno));
return -2;
}
msgtype= msgtype= (int)key;
printf("key %d msgid %d msgypte %d\n", (int)key,msgid,msgtype);
for(i=0; i<4; i++)
{
memset(&msgbuf, 0, sizeof(msgbuf));
if( msgrcv(msgid,&msgbuf,sizeof(msgbuf.mtext),msgtype,IPC_NOWAIT) <0)
{
printf("msgsnd() receive message failure: %s\n", strerror(errno));
break;
}
printf("receive message :%s\n",msgbuf.mtext);
sleep(1);
}
msgctl(msgid,IPC_RMID,NULL);
return 0;
}
進程一給進程二發消息
進程二收到消息後: