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