http://blog.csdn.net/stonecao/article/details/10364287
1.消息隊列簡介
實現linux進程通信的方式有5種:
--信號(Singal)
--管道(Pipe)
--消息隊列(Message)
--信號量(Semaphore)
每種進程通信方式實現方式和功能不一樣,帶來適用的場景也有所不同,消息隊列是鏈表隊列,它通過內核提供一個struct msqid_ds *msgque[MSGMNI]向量維護內核的一個消息隊列列表,因此linux系統支持的最大消息隊列數由msgque數組大小來決定,每一個msqid_ds表示一個消息隊列,並通過msqid_ds.msg_first、msg_last維護一個先進先出的msg鏈表隊列,當發送一個消息到該消息隊列時,把發送的消息構造成一個msg結構對象,並添加到msqid_ds.msg_first、msg_last維護的鏈表隊列,同樣,接收消息的時候也是從msg鏈表隊列尾部查找到一個msg_type匹配的msg節點,從鏈表隊列中刪除該msg節點,並修改msqid_ds結構對象的數據。
2.消息隊列的數據結構
--1.
struct msqid_ds *msgque[MSGMNI]
向量:
msgque[MSGMNI]是一個msqid_ds結構的指針數組,每個msqid_ds結構指針代表一個系統消息隊列,msgque[MSGMNI]的大小爲MSGMNI=128,也就是說系統最多有MSGMNI=128個消息隊列
--2.
struct msqid_ds
一個消息隊列結構
struct 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*/ };
--3.
struct msg
消息節點結構:
msqid_ds.msg_first,msg_last維護的鏈表隊列中的一個鏈表節點
struct msg { msg *msg_next; /*下一個msg*/ long msg_type; /*消息類型*/ *msg_spot; /*消息體開始位置指針*/ msg_ts; /*消息體長度*/ message; /*消息體*/ }
--4.msgbuf消息內容結構:
msg 消息節點中的消息體,也是消息隊列使用進程(消息隊列發送接收進程)發送或者接收的消息
struct msgbuf { long mtype; --消息類型 char mtext[n]; --消息內容 }
3.消息隊列的使用
--1.消息隊列Key的獲取:
在程序中若要使用消息隊列,必須要能知道消息隊列key,因爲應用進程無法直接訪問內核消息隊列中的數據結構,因此需要一個消息隊列的標識,讓應用進程知道當前操作的是哪個消息隊列,同時也要保證每個消息隊列key值的唯一性
----a.通過ftok函數獲取
key_t key; key=ftok(".","a")
該函數通過一個路徑名稱映射出一個消息隊列key(我的理解是使用路徑映射的方式比較容易獲取一個唯一的消息隊列key)
----b.直接定義key:
#define MSG_KEY 123456
自定義key的方式要注意避免消息隊列的重複。
--2.獲取或者打開一個消息隊列
----a.使用說明
qid=msgget(key_t key, int msgflag)
--key: 消息隊列key
--msgflag:
·IPC_PRIVATE:創建一個該進程獨佔的消息隊列,其它進程不能訪問該消息隊列
·IPC_CREAT:若消息隊列不存在,創建一個新的消息隊列,若消息隊列存在,返回存在的消息隊列
·IPC_CREAT | IPC_EXCL: IPC_EXCL標誌本身沒有多大意義,與IPC_CREAT一起使用,保證只創建新的消息隊列,若對應key的消息隊列已經存在,則返回錯誤
·IPC_NOWAIT:小隊列以非阻塞的方式獲取(若不能獲取,立即返回錯誤)
----b.函數原理
------1)如果key==IPC_PRIVATE,則申請一塊內存,創建一個新的消息隊列(數據結構msqid_ds),將其初始化後加入到msgque向量表中的某個空位置處,返回標示符。
------2)在msgque向量表中找鍵值爲key的消息隊列,如果沒有找到,結果有二:
·msgflag表示不創建新的隊列,則錯誤返回。
·msgflag表示要創建新的隊列(IPC_CREAT),則創建新消息隊列,創建過程如1)。
-------3)如果在msgque向量表中找到了鍵值爲key的消息隊列,則有以下情況:
·如果msgflg表示一定要創建新的消息隊列而且不允許有相同鍵值的隊列存在,則錯誤返回。
·如果找到的隊列是不能用的或已經損壞的隊列,則錯誤返回。
·認證和存取權限檢查,如果該隊列不允許msgflg要求的存取,則錯誤返回。
·正常,返回隊列的標識符。
--3.發送一個消息到消息隊列
----a.使用說明:
int msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
------msqid爲消息隊列的qid
------msgp對應消息內容結構體指針
------msgsz消息的大小即msgp指針指向的消息結構體的大小
------msgflg消息標誌
·0:忽略該標誌位,以阻塞的方式發送消息到消息隊列
·IPC_NOWAIT:以非阻塞的方式發送消息,若消息隊列滿,函數立即返回。
------返回:
·0: 成功
·-1:非阻塞方式訪問滿消息隊列返回
·EACCES:沒有該消息隊列寫權限
·EFAULT:消息隊列地址無法獲取
·EIDRM:消息隊列已經被刪除
·EINTR:消息隊列等待寫入的時候被中斷
·ENOMEM:內存不夠
----b.函數原理:
1)計算id = (unsigned int) msqid % MSGMNI,然後根據id在linux系統消息隊列向量msgque[MSGMNI]中查找對應的消息隊列,並進行認證檢查,合法性檢查
2)如果隊列已滿,以可中斷等待狀態(TASK_INTERRUPTIBLE)將當前進程掛起在wwait等待隊列(發送消息等待隊列)上(msgflag==0)。
3)否則根據msgbuf的大小申請一塊空間,並在其上創建一個消息數據結構struct msg(內核空間),將消息緩衝區中的消息內容拷貝到該內存塊中消息頭的後面(從用戶空間拷貝到內核空間)。
4)將消息數據結構加入到消息隊列的隊尾,修改隊列msqid_ds的相應參數。
5)喚醒在該消息隊列的rwait進程隊列(讀等待進程隊列)上等待讀的進程,並返回。
--4.從消息隊列接收一個消息到msgbuf*
----a.使用說明
int msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,long msgtyp, int msgflg)
----msqid爲消息隊列的qid
----msgp是接收到的消息將要存放的緩衝區
----msgsz是消息的大小
----msgtyp是期望接收的消息類型
----msgflg是標誌
·0:表示忽略
·IPC_NOWAIT:如果消息隊列爲空,不阻塞等待,返回一個ENOMSG
----返回
·0:成功
·-1:消息長度大於msgsz
·EACCES:沒有該消息隊列讀權限
·EFAULT:消息隊列地址無法獲取
·EIDRM:消息隊列已經被刪除
·EINTR:消息隊列等待寫入的時候被中斷
·ENOMEM:內存不夠
----b.函數原理:
1)計算id = (unsigned int) msqid % MSGMNI,然後根據id在msgque[MSGMNI]中查找對應的消息隊列,並進行認證檢查,合法性檢查
2)根據msgtyp搜索消息隊列,情況有二:
----如果找不到所要的消息,則以可中斷等待狀態(TASK_INTERRUPTIBLE)將當前進程掛起在rwait等待隊列
----如果找到所要的消息,則將消息從隊列中摘下,調整隊列msqid_ds參數,喚醒該消息隊列的wwait進程隊列上等待寫的進程,將消息內容拷貝到用戶空間的消息緩衝區msgp中,釋放內核中該消息所佔用的空間,返回
--5.消息的控制
----a.使用說明:
int msgctl (int msqid, int cmd, struct msqid_ds *buf)
------msqid:爲消息隊列的qid
------cmd:爲該函數要對消息隊列執行的操作
·IPC_STAT:取出消息隊列的msqid_ds結構體並將參數存入buf所指向的msqid_ds結構對象中
·IPC_SET:設定消息隊列的msqid_ds 數據中的msg_perm 成員。設定的值由buf 指向的msqid_ds
結構給出。
·IPC_EMID:將隊列從系統內核中刪除。
----buf:消息隊列msqid_ds結構體指針
----b.函數作用
對消息隊列進行設置以及相關操作,具體操作由cmd指定。