消息隊列
消息隊列:消息隊列是一個存放在內核中的消息鏈表,每個消息隊列由消息隊列標識符標識。與管道不同的是消息隊
列存放在內核中,只有在內核重啟(即操作系統重啟)或者顯式地刪除一個消息隊列時,該消息隊列才會被真正的刪除。
Linux內核中,每個消息隊列都維護一個結構體msqid_ds,此結構體保存着消息隊列當前的狀態信息。該結構定義在
頭文件linux/msg.h中,具體如下:
struct msqid_ds { struct_ipc_perm msg_perm; //是一個ipc_perm的結構,保存了消息隊列的存取權限,以及隊列的用戶ID、組ID等信息 struct_msg *msg_first; //指向隊列中的第一條消息 struct_msg *msg_last; //指向隊列中的最后一條消息 __kernel_t time_t msg_stime; //向消息隊列發送最后一條信息的時間 __kernel_t time_t msg_rtime; //從消息隊列取最后一條信息的時間 __kernel_t time_t msg_ctime; //最后一次變更消息隊列的時間 unsigned long msg_lcbytes; unsigned long msg_lqbytes; unsigned short msg_cbytes; //消息隊列中所有消息占的字節數 unsigned short msg_qnum; //消息隊列中消息的數目 unsigned short msg_qbytes; //消息隊列的最大字節數 __kernel_ipc_pid_t msg_lspid; //向消息隊列發送最后一條消息的進程ID __kernel_ipc_pid_t msg_lrpid; //從消息隊列讀取最后一條信息的進程ID };
消息隊列是隨着內核的存在而存在的,每個消息隊列在系統范圍內對應惟一的鍵值。要獲得一個消息隊列的描述符,
只需提供該消息隊列的鍵值即可,該鍵值通常由函數ftok返回,該函數原形為:
#include <sys/ipc.h>
key_t ftok(const char *pathname,int proj_id);
創建一個新的消息隊列或訪問一個已存在的消息隊列前需要使用ftok函數得到key值,下面是ftok函數的包裹函數:
key_t Ftok(const char *pathname,int proj_id) { key_t key= ftok(pathname,proj_id); if(key== -1) { perror("ftok."); exit(1); } return key; }
消息隊列的創建或打開:
使用函數msgget進行消息隊列的創建或打開。該函數定義在頭文件<sys/msg.h>中,該函數的原形為:
#include <sys/ipc.h>
#include <sys/msg.h>
int semget(key_t key,int msgflg);
該函數如果調用成功則返回一個消息隊列的描述符,否則返回-1。函數的第一個參數為ftok()函數得到的鍵值,第二
個參數msgflg是一個標志參數,可以取如下值:
- IPC_CREAT:如果內核中不存在鍵值與key相等的消息隊列,則新建一個消息隊列;如果存在這樣的消息隊列,返回該
消息隊列的描述符。
- IPC_EXCL:和IPC_CREAT一起使用,如果對應鍵值的消息隊列已經存在,則出錯,返回-1。
消息隊列的讀寫:
創建了一個消息隊列后,就可以對消息隊列進行讀寫了,函數msgsnd用於向消息隊列發送寫數據,該函數定義在頭文
件<sys/msg.h>中,函數原形為:
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
函數中,參數msgqid為函數向msgid標識的消息隊列發送一個消息(msqid是由msgget返回的標識符);參數msgp指向
發送的消息是一個結構指針,該結構如下所示;參數msgsz為要發送的消息的大小,不包含消息類型占用的4個字節;參數
msgflg為操作標志位,可以設置為0或者IPC_NOWAIT,如果msgflg為0,則當消息隊列已滿時,msgsnd將會阻塞,直到消息
可以寫進消息隊列,如果msgflg為IPC_NOWAIT,當消息隊列已滿的時候,msgsnd函數將不等待立即返回。msgsnd函數成功
返回0,失敗返回-1。
struct msgbuf { long mtype; //代表消息類型,給消息指定類型,可以使得消息在一個隊列中重復使用 char mtext[1]; //消息內容 };
消息隊列中放入數據后,其它進程就可以讀取其中的消息了。讀取消息的系統調用為msgrcv()函數,該函數定義在頭
文件<sys/msg.h>中,函數原形為:
ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
函數中,參數msqid為消息隊列描述符(由msgget返回的標識符);參數msgp同上面的參數相同;參數msgsz為消息緩
沖區的大小;參數msgtyp為請求讀取的消息類型;參數msgflg為操作標志位,msgflg可以為IPC_NOWAIT、IPC_EXCEPT、IP
C_NOERROR三個常量,IPC_NOWAIT:如果沒有滿足條件的消息,調用立即返回,此時錯誤碼為ENOMSG.IPC_EXCEPT:與msgt
yp配合使用,返回隊列中第一個類型不為msgtyp的消息.IPC_NOERROR:如果隊列中滿足條件的消息內容大於所請求的msgs
z字節,則把該消息截斷,截斷部分將被丟棄。msgrcv函數成功會返回讀出消息的實際字節數,否則返回-1。
消息隊列的控制:
消息隊列的屬性保存在系統維護的數據結構msqid_ds中,用戶可以通過函數msgctl獲取或設置消息隊列的屬性。msg
ctl定義在頭文件<sys/msg.h>中,函數原形為:
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
函數中,第一個參數為由msgget返回的標識符;第二個參數為執行的cmd操作;第三個參數為上面已經定義的msqid_d
s的結構體類型。系統中定義了3種cmd操作:IPC_STAT、IPC_SET、IPC_RMID,它們的含義如下:
- IPC_STAT:該命令用來獲取消息隊列對應的msqid_ds數據結構,並將其保存到buf指向的地址空間。
- IPC_SET:該命令用來設置消息隊列的屬性,要設置的屬性存儲在buf中,可設置的屬性包括:msg_perm.uid、msg_pe
rm.gid、msg_perm.mode和msg_qbytes。
- IPC_RMID:從內核中刪除msqid標識的消息隊列。
下面是使用消息隊列進行發送消息的例子:
// myipc.h #pragma once #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #include <sys/msg.h> union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; key_t Ftok(const char *pathname,int proj_id) { key_t key= ftok(pathname,proj_id); if(key== -1) { perror("ftok."); exit(1); } return key; }
//utili.h #pragma once #define MSG_BUFFER_LEN 256 #define SERVER_SEND_FLAG 100 #define SERVER_RECV_FLAG 200 #define CLIENT_SEND_FLAG 200 #define CLIENT_RECV_FLAG 100 typedef struct Msg { long msg_type; char msg_text[MSG_BUFFER_LEN]; }Msg;
//server.c #include "myipc.h" #include "utili.h" int main(int argc,char *argv[]) { key_t msg_key= Ftok(argv[1],atoi(argv[2])); int msg_id= msgget(msg_key,IPC_CREAT|0755); if(msg_id== -1) { perror("msgget"); exit(1); } Msg msg; while(1) { printf("Ser:"); scanf("%s",msg.msg_text); msg.msg_type= SERVER_SEND_FLAG; msgsnd(msg_id,&msg,strlen(msg.msg_text)+ 1,0); msgrcv(msg_id,&msg,MSG_BUFFER_LEN,SERVER_RECV_FLAG,0); printf("Cli:%s\n",msg.msg_text); } msgctl(msg_id,IPC_RMID,NULL); return 0; }
//client.c #include "myipc.h" #include "utili.h" int main(int argc,char *argv[]) { key_t msg_key= Ftok(argv[1],atoi(argv[2])); int msg_id= msgget(msg_key,0); Msg msg; while(1) { msgrcv(msg_id,&msg,MSG_BUFFER_LEN,CLIENT_RECV_FLAG,0); printf("Ser:%s\n",msg.msg_text); printf("Cli:"); scanf("%s",msg.msg_text); msg.msg_type= CLIENT_SEND_FLAG; msgsnd(msg_id,&msg,strlen(msg.msg_text)+ 1,0); } return 0; }
