Linux中消息隊列的使用
今天帶小伙伴們學習了消息隊列相關的內容,先簡單介紹下消息隊列,然后對消息隊列相關的結構及函數進行學習,最后擼代碼使用一下這些函數使用一下消息隊列,希望對大家有所幫助哈!
1 消息隊列的概念及使用過程
1)消息隊列的概念
消息隊列就是一個消息的鏈表。一條消息可以看作一個數據記錄,此數據具有特定的格式。
進程可以按照特定的規則向隊列中添加(寫入)消息;其他的進程則可以從消息隊列中讀走消息。
2)消息隊列的應用場景
消息隊列本身就是IPC通信中的內容,所以它主要用于進程間的通信。
消息有讀寫,所以發送的消息可以用于動作的通知信號,也可以接收數據然后做其他處理。
2 消息隊列相關的結構及函數
0)消息隊列相關的結構
每個隊列都有一個msqid_ds結構與其相關聯,結構如下。
- struct msqid_ds
- {
- struct ipc_perm msg_perm; // 消息隊列的存取權限以及其他一些信息
- time_t msg_stime; // 最近一次隊列接受消息的時間
- time_t msg_rtime; // 最近一次從隊列中取出消息的時間
- time_t msg_ctime; // 最近一次隊列發生改動的時間
- unsigned long __msg_cbytes; // 隊列中消息的占用內存的字節數
- msgqnum_t msg_qnum; // 隊列中當前的消息數
- msglen_t msg_qbytes; // 隊列所占用內存的最大字節數
- pid_t msg_lspid; // 最近一次向隊列發送消息的進程的pid msgsnd
- pid_t msg_lrpid; // 最近一次從隊列中取出消息的進程的pid
- };
- struct ipc_perm
- {
- key_t key;
- ushort uid; // 用戶id,有效的用戶ID和有效的組id(euid和egid)
- ushort gid;
- ushort cuid; // 創建者的euid和egid
- ushort cgid;
- ushort mode; // 訪問模式參見模式標志
- ushort seq; // IPC對象使用頻率信息
- };
1)msgget函數
msgget函數用于創建或打開消息隊列。
① 函數原型。
- int msgget(key_t key,int msgflg)
② 頭文件。
- include <sys/ipc.h>
- include <sys/msg.h>
- include <sys/types.h>
③ 參數。
key:鍵值。
msgflg:打開標志。IPC_CREAT:表明新創建的一個消息隊列。
④ 返回值。
成功:返回消息隊列的id。
失敗:-1。
2)msgsnd函數
msgsnd函數用于發送消息,即寫消息到消息隊列。
① 函數原型。
- int msgsnd(int msgid,const void *msgp,size_t msgsz,int msgflg)
② 頭文件。
- include <sys/ipc.h>
- include <sys/types.h>
- include <sys/msg.h>
③ 參數。
msgid:消息隊列的id。
msgp:指向要發送的消息。
msgsz:消息的長度。
msgflg:標志位。
④ 返回值。
成功:0。
失敗:-1。
3)msgrcv函數
msgrcv函數用于讀消息隊列,即從消息隊列接收消息。
① 函數原型。
- int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
- ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,intmsgflg)
② 頭文件。
- #include <sys/ipc.h>
- #include <sys/types.h>
- #include <sys/msg.h>
③ 參數。
msqid:消息隊列的id。
msgp:存放消息。
msgsz:希望取到的消息的最大長度。
msgtyp:消息的類型,分下面三種情況:
當 msgtyp = 0:忽略類型,直接取隊列中的第一條消息。
當 msgtyp > 0: 取消息隊列中類型等于msgtyp的第一條消息。
當 msgtyp < 0: 取類型比msgtyp的絕對值要小或等于的消息,如果有多條消息
滿足該條件,取類型最小的一條。
④ 返回值。
成功:實際接收到的消息的數據長度。
失敗:-1。
4)msgctl函數
msgctl函數用于操作消息隊列,比如進行消息隊列的刪除等等。
① 函數原型。
- int msgctl(int msqid,int cmd,struct msqid_ds *buf)
② 頭文件。
- #include <sys/ipc.h>
- #include <sys/msg.h>
- #include <sys/types.h>
③ 參數。
msqid:消息隊列的id。
cmd:消息隊列的操作命令,此參數指定對msqid指定的隊列要執行的命令。
IPC_STAT:取此隊列的msqidds結構,并將它存放在buf指向的結構中。
IPCSET:將字段msg_perm.uid、msg_perm.gid、msg_perm.mode和msg_qbytes從buf指向的結構復制到與這個隊列相關的msqid_ds結構中。
此命令只能由下列兩種進程執行:
一種是其有效用戶ID等于msg_perm.cuid或msg perm.uid。
另一種是具有超級用戶特權的進程。只有超級用戶才能增加msg_qbytes的值。
IPC_RMID:從系統中刪除該消息隊列以及仍在該隊列中的所有數據。這種刪除立即生效。
仍在使用這一消息隊列的其他進程在它們下一次試圖對此隊列進行操作時,將得到EIDRM錯誤。
此命令只能由下列兩種進程執行:
一種是其有效用戶ID等于msg_perm.cuid或msg_perm.uid。
另一種是具有超級用戶特權的進程。這3條命令(IPC_STAT、IPC_SET和IPC_RMID)也可用于信號量和共享存儲。
buf:獲取內核中的msqid_ds結構,通常不用。
④ 返回值。
成功:0。
失敗:-1。
3 實例代碼
下面用兩個進程,給大家演示下消息隊列的使用過程。
實例代碼如下,說明都在代碼注釋中了,圖片。
SendQueue.c。
- #include<stdio.h>
- #include<sys/types.h>
- #include<sys/ipc.h>
- #include<sys/msg.h>
- #include<string.h>
- // 消息結構體
- struct msg
- {
- long msgtype; //消息的類型
- char msgtext[1024]; //消息的長度
- };
- void main(int argc, char *argv[])
- {
- int msgid;
- char str[256];
- struct msg msgst;
- key_t key = ftok("/tmp",600);
- //創建消息隊列
- msgid = msgget(key,0666 | IPC_CREAT);
- //鍵盤輸入消息
- while(1)
- {
- //獲取消息數據
- printf("\nPlease enter a message to send,input 'end' to quit!\n\n");
- scanf("%s",str);
- strcpy(msgst.msgtext,str);
- if(strncmp(str, "end", 3) == 0)
- {
- printf("\n");
- break;
- }
- //發送消息
- msgsnd(msgid,&msgst,sizeof(struct msg),0);
- }
- //輸出消息隊列
- msgctl(msgid,IPC_RMID,0);
- }
ReceiveQueue.c。
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/msg.h>
- // 消息結構體
- struct msg
- {
- long msgtype;
- char msgtext[1024];
- };
- int main(int argc, char *argv[])
- {
- int RunFlag = 1; // 循環標志
- int msgid = -1; // 消息id
- long msgtp = 0; // 消息類型
- struct msg msgst; // 消息結構體變量
- key_t key = ftok("/tmp",600); // 創建一個鍵值
- msgid = msgget(key, 0666 | IPC_CREAT); //建立消息隊列
- if(msgid == -1)
- {
- exit(1); // 異常退出
- }
- while(RunFlag) // 從隊列中獲取消息,直到遇到end消息為止
- {
- if(msgrcv(msgid,&msgst,sizeof(struct msg), msgtp, 0) == -1)
- {
- exit(1); // 異常退出
- }
- printf("\nThe message received is: %s\n\n",msgst.msgtext);
- if(strncmp(msgst.msgtext, "end", 3) == 0) // 遇到end結束
- {
- RunFlag = 0; // 置退出循環標志
- }
- }
- if(msgctl(msgid, IPC_RMID, 0) == -1) // 刪除消息隊列
- {
- exit(1); // 異常退出
- }
- exit(0); // 正常退出
- }
編譯程序,先運行接收程序,再運行發送程序,輸入要發送的消息,退出輸入end。
① 兩個終端運行結果如下:
② 單個終端運行結果如下:
本文轉載自微信公眾號「嵌入式雜牌軍」,可以通過以下二維碼關注。轉載本文請聯系嵌入式雜牌軍公眾號。