認識消息隊列
消息隊列提供了從一個進程到另一個進程發送一個數據塊(整發整收)的能力。
每個數據塊被認爲有一個應用類型, 接收者進程接受的數據塊可以有不同的類型。
消息隊列的每一個發送和接收的數據塊是有最大字節限制的(MSGMAX), 整個消息隊列的大小也是有字節限制的(MSGMNB), 系統中的消息隊列數量也有最大限制(MSGMNI)。
與消息隊列相關的系統調用
1.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgfg);
key代表要創建的消息隊列的鍵
msgflag通常是通過IPC_CREAT或者IPC_EXCL來設置的
IPC_CREAT|IPC_EXCL代表創建消息隊列,如果這個消息隊列已經存在,則立即出錯返回
IPC_CREAT則代表創建消息隊列,不存在則創建,存在則直接打開
如果成功返回非負消息隊列標識符。
msgget函數返回的是進程在調用發收消息函數是需要使用的消息隊列標識。 但是爲了使多個進程可以在同一個IPC對象上匯聚。 所以要有IPC對象的外部名key。
ftok() 兩個參數的目的是爲了 生成表示唯一一個IP C對象的 的IPC key 兩個參數的二元組要唯一
#include<sys/ipc.h>
key_t ftok(const char* path, int id);
path和id表示通信的進程共同認同的路徑名和項目id。
2.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgctl函數對消息隊列執行多種操作。
msgid是消息隊列的標識符。
cmd參數是對msgid隊列要執行的操作
cmd:
IPC_RMID 從系統中刪除該消息隊列以及消息隊列中的數據。
IPC_STAT 取出此隊列的msqid_ds 結構 放在buf指向的結構體中.
IPC_SET 在進程權限允許的情況下將buf指向的結構體中的部分信息複製到與這個隊也有關的msgid_ds中。
3.
int msgsnd(int msgid, const void* msgp, size_t msgsz, long int msgtyp, int msgflg)
msgid 消息隊列標識。
msgp指向要發的數據塊(一個包含表示類型的long int 和char text[msgsz], )
msgsz 表示要發送的數據塊結構體中數據的大小字節數。
msgtyp 表示所發送消息的類型, 它可以實現接收優先級的簡單形式。
msgflg 用來表示當消息隊列被寫滿時會發生什麼。
msgflg == IPC_NOWAIT 時 表示隊列滿不等待 返回EAGIN錯誤。
msgflg == MSG_NOERROR 時 大於消息隊列要求的數據被截斷。
msgtyp ==0 時返回第一個數據 < 0 時返回第一個不大於概數絕對值類型大小的數字, > 0時返回隊列裏第一條等於msgtyp的數據塊(結構體)。
發送消息的數據塊(結構體) 需要自定義
struct msgbuf {
long mtype; /* message type, must be > 0 */類型必須大於0
char mtext[1]; /* message data */塊大小
};
4.
int msgrcv(int msgid, const void* msgp, size_t msgsz, long int msgtyp, int msgflg);
IPC 對象的數據結構
內核中每一個IPC對象都有一個ipc_prem結構體。
ipc_perm 結構定義於中,原型如下:
struct ipc_perm
{
key_t key; 調用shmget()時給出的關鍵字
uid_t uid; /*所有者的有效用戶ID */
gid_t gid; /*所有者所屬組的有效組ID*/
uid_t cuid; /* 創建 者的有效用戶ID*/
gid_t cgid; /* 創建者所屬組的有效組ID*/
unsigned short mode; /* Permissions + SHM_DEST和SHM_LOCKED標誌*/
unsignedshort seq; /* 序列號*/
};
構構消息隊列的數據結構
struct msqid_ds
{
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue,unused */
struct msg *msg_last; /* last message in queue,unused */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
unsigned long msg_lqbytes; /* ditto */
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
消息隊列的本質是一個結構體鏈表 發消息時往鏈表中放一個結構體 收消息時從裏面依據msgtyp拿一個結構體。
消息隊列的特性
1、生命週期隨內核
2、雙向通信
3、按塊大小讀寫//不像管道是面向字節流的
4、內核保證同步與互斥。
代碼:
server.c
#include"comm.h"
int main()
{
int msgid=CreateMsgQueue();
char buf[1024]={0};
while(1)
{
RecvMsg(msgid,CLIENT_TYPE,buf);
printf("client say :%s\n",buf);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1]=0;
SendMsg(msgid,SERVER_TYPE,buf);
}
}
Destroy(msgid);
return 0;
}
client.c
#include"comm.h"
int main()
{
int msgid=GetMsgQueue();
char buf[1024]={0};
while(1)
{
ssize_t w = read(0,buf,sizeof(buf)-1);
if(w>0)
{
buf[w-1]=0;
SendMsg(msgid,CLIENT_TYPE,buf);
}
RecvMsg(msgid ,SERVER_TYPE,buf);
printf("Server say:%s\n",buf);
}
return 0;
}
comm.c
#include"comm.h"
int ComMsgQueue(int flag)
{
key_t key=ftok(PATHNAME,PROJ_ID);
if(key<0)
{
perror("ftok");
return -1;
}
int msgid=msgget(key,0666|flag);
if(msgid<0)
{
perror("msgget");
return -2;
}
return msgid;
}
int CreateMsgQueue()
{
return ComMsgQueue(IPC_CREAT|IPC_EXCL);
}
int GetMsgQueue()
{
return ComMsgQueue(IPC_CREAT);
}
int DestroyMsgQueue(int msgid)
{
if(msgctl(msgid,IPC_RMID,NULL)<0)
{
perror("msgctl");
return -1;
}
return 0;
}
int SendMsg(int msgid,int type,const char*msg)
{
struct msgbuf _mb;
_mb.mytype=type;
strcpy(_mb.mtext,msg);
if(msgsnd(msgid,&_mb,sizeof(_mb.mtext),0)<0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int RecvMsg(int msgid,int type,char*out)
{
struct msgbuf _mb;
if(msgrcv(msgid,&_mb,sizeof(_mb.mtext),type,0)<0)
{
perror("msgrcv");
return -1;
}
strcpy(out,_mb.mtext);
return 0;
}
comm.h
#ifndef _COMM_H_
#define _COMM_H_
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
#define PATHNAME "."
#define PROJ_ID 0x6666
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>
#include<stdio.h>
struct msgbuf
{
long mytype;
char mtext[1024];
};
int CreatMsgQueue();
int DestroyMsgQueue();
int SendMsg();
int RecvMsg();
int GetMsgQueue();
#endif