linux進程間通訊之消息隊列

文件名到鍵值

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (char*pathname, char proj);

它返回與路徑pathname相對應的一個鍵值。該函數不直接對消息隊列操作,但在調用msgget()來獲得消息隊列描述字前,往往要調用該函數.

消息隊列API

  系統消息隊列API共有四個,使用時需要包括幾個頭文件:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

1)int msgget(key_t key, int msgflg)

參數key是一個鍵值,由ftok獲得;msgflg參數是一些標誌位。該調用返回與健值key相對應的消息隊列描述字。

在以下兩種情況下,該調用將創建一個新的消息隊列:

  • 如果沒有消息隊列與健值key相對應,並且msgflg中包含了IPC_CREAT標誌位;
  • key參數爲IPC_PRIVATE;

參數msgflg可以爲以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或結果。

調用返回:成功返回消息隊列描述字,否則返回-1。

注:參數key設置成常數IPC_PRIVATE並不意味着其他進程不能訪問該消息隊列,只意味着即將創建新的消息隊列。

2)int msgrcv(int msgid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);

該系統調用從msgid代表的消息隊列中讀取一個消息,並把消息存儲在msgp指向的msgbuf結構中。

msqid爲消息隊列描述字;消息返回後存儲在msgp指向的地址,msgsz指定msgbuf的mtext成員的長度(即消息內容的長度),msgtyp爲請求讀取的消息類型;讀消息標誌msgflg可以爲以下幾個常值的或:

  • IPC_NOWAIT 如果沒有滿足條件的消息,調用立即返回,此時,errno=ENOMSG
  • IPC_EXCEPT 與msgtyp>0配合使用,返回隊列中第一個類型不爲msgtyp的消息
  • IPC_NOERROR 如果隊列中滿足條件的消息內容大於所請求的msgsz字節,則把該消息截斷,截斷部分將丟失。

msgrcv手冊中詳細給出了消息類型取不同值時(>0; <0; =0),調用將返回消息隊列中的哪個消息。

msgrcv()解除阻塞的條件有三個:

  1. 消息隊列中有了滿足條件的消息;
  2. msqid代表的消息隊列被刪除;
  3. 調用msgrcv()的進程被信號中斷;

調用返回:成功返回讀出消息的實際字節數,否則返回-1。

3)int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);

向msgid代表的消息隊列發送一個消息,即將發送的消息存儲在msgp指向的msgbuf結構中,消息的大小由msgze指定。

對發送消息來說,有意義的msgflg標誌爲IPC_NOWAIT,指明在消息隊列沒有足夠空間容納要發送的消息時,msgsnd是否等待。造成msgsnd()等待的條件有兩種:

  • 當前消息的大小與當前消息隊列中的字節數之和超過了消息隊列的總容量;
  • 當前消息隊列的消息數(單位"個")不小於消息隊列的總容量(單位"字節數"),此時,雖然消息隊列中的消息數目很多,但基本上都只有一個字節。

msgsnd()解除阻塞的條件有三個:

  1. 不滿足上述兩個條件,即消息隊列中有容納該消息的空間;
  2. msqid代表的消息隊列被刪除;
  3. 調用msgsnd()的進程被信號中斷;

調用返回:成功返回0,否則返回-1。

4)int msgctl(int msqid, int cmd, struct msqid_ds *buf);

該系統調用對由msqid標識的消息隊列執行cmd操作,共有三種cmd操作:IPC_STAT、IPC_SET 、IPC_RMID。

  1. IPC_STAT:該命令用來獲取消息隊列信息,返回的信息存貯在buf指向的msqid結構中;
  2. IPC_SET:該命令用來設置消息隊列的屬性,要設置的屬性存儲在buf指向的msqid結構中;可設置屬性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同時,也影響msg_ctime成員。
  3. IPC_RMID:刪除msqid標識的消息隊列;

調用返回:成功返回0,否則返回-1。

例子:

TestQueueClient.cpp

#include "msgqueue.h"
#include <stdio.h>
#include <pthread.h>
int msgid = 0;
IpcMsgQueue msgQueue;
void* Receive(void* arg){
	char buff[1024] = {0};
	fflush(stdout);
	msgQueue.ReceiveMessage(msgid,buff,_SERVER_);
	printf("Server#%s\n",buff);
	return 0;
}

void* Send(void* arg){
	char buff[1024] = {0};
	printf("Client#");
	fflush(stdout);
	ssize_t s = read(1,buff,sizeof(buff));
	buff[s-1] = 0;
	msgQueue.SendMessage(msgid,buff,_CLIENT_);
	return 0;
}

int main(){
	key_t key = ftok(PATHNAME,PROJNAME);
	if(key < 0){
		perror("ftok");
		return 1;
	}
	msgid = msgQueue.GetMessage(key);
	pthread_t c1,c2;
	for(int i=0; i<10; ++i){
		pthread_create(&c1,NULL,Send,NULL);
		pthread_create(&c2,NULL,Receive,NULL);
		pthread_join(c1,NULL);
		pthread_join(c2,NULL);
	}
	return 0;
}

TestQueueServer.cpp

#include "msgqueue.h"
#include <stdio.h>
#include <pthread.h>
int msgid = 0;
IpcMsgQueue msgQueue;
void* Receive(void* arg){
	char buff[1024] = {0};
	msgQueue.ReceiveMessage(msgid,buff,_CLIENT_);
	printf("Client#%s\n",buff);
	return 0;
}

void* Send(void* arg){
	char buff[1024] = {0};
	printf("Server#");
	fflush(stdout);
	ssize_t s = read(1,buff,sizeof(buff));
	buff[s-1] = 0;
	msgQueue.SendMessage(msgid,buff,_SERVER_);
	return 0;
}

int main(){
	key_t key = ftok(PATHNAME,PROJNAME);
	if(key < 0){
		perror("ftok");
		return 1;
	}
	msgid = msgQueue.CreateMessage(key);
	pthread_t c1,c2;
	for(int i=0; i<10; ++i){
		pthread_create(&c1,NULL,Send,NULL);
		pthread_create(&c2,NULL,Receive,NULL);
		pthread_join(c1,NULL);
		pthread_join(c2,NULL);
	}
	msgQueue.DestoryMessage(msgid);
	return 0;
}

msgqueue.h

#ifndef __PUB_MSGQUEUE_H__
#define __PUB_MSGQUEUE_H__
/*
	desc:進程間通訊--消息隊列
	writer:Lake
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include "../Log.h"

#define _SERVER_ 1
#define _CLIENT_ 2
#define PATHNAME "/"
#define PROJNAME 0

//消息結構體
struct IpcMsgbuf
{
        long mtype;      //消息類型,由用戶自定義
        char mtext[1024];//發送的消息(長度、類型可以自行指定)
};
//進程間消息隊列通訊類
class IpcMsgQueue
{
	public:
		IpcMsgQueue()
		{
			
		}
		virtual ~IpcMsgQueue()
		{
			
		}
	public:
		//創建與打開消息隊列公共函數
		int MessageCommon(key_t key,int flag);
		//創建全新的消息隊列(服務端)
		int CreateMessage(key_t qid);
		//打開已有的消息隊列(客戶端)
		int GetMessage(key_t qid);
		//發送消息
		void SendMessage(int msgid,const char* msg,int who);
		//接收消息
		void ReceiveMessage(int msgid,char* msg,int who);
		//消息隊列的刪除
		void DestoryMessage(int msgid);
};

#endif

msgqueue.cpp

#include "msgqueue.h"

//創建與打開消息隊列公共函數
int IpcMsgQueue::MessageCommon(key_t key,int flag)
{
    int ret = 0;
    if((ret=msgget(key,flag))==-1){
        //LOG (LOG_ERROR, __FILE__, __LINE__, "MessageCommon--error[%s]",strerror(errno));
    }
    return ret;
}
//創建全新的消息隊列(服務端)
int IpcMsgQueue::CreateMessage(key_t qid)
{
    //消息隊列也是具有權限結構的,因此在創建時給666權限
    return MessageCommon(qid,IPC_CREAT|IPC_EXCL|0666);
}
//打開已有的消息隊列(客戶端)
int IpcMsgQueue::GetMessage(key_t qid)
{
    return MessageCommon(qid,IPC_CREAT);
}
//發送消息
void IpcMsgQueue::SendMessage(int msgid,const char* msg,int who)
{
    IpcMsgbuf buf;
    buf.mtype = who;                //消息類型
    strcpy(buf.mtext,msg);        //消息內容
    if(msgsnd(msgid,&buf,sizeof(buf.mtext),0) == -1){
    	//LOG (LOG_ERROR, __FILE__, __LINE__, "SendMessage--error[%s]",strerror(errno));
        DestoryMessage(msgid);
    }
}
//接收消息
void IpcMsgQueue::ReceiveMessage(int msgid,char* msg,int who)
{
    IpcMsgbuf buf;
    if(msgrcv(msgid,&buf,sizeof(buf.mtext),who,0)==-1){
    	//LOG (LOG_ERROR, __FILE__, __LINE__, "SendMessage--error[%s]",strerror(errno));
        DestoryMessage(msgid);
    }
    strcpy(msg,buf.mtext);
}
//消息隊列的刪除
void IpcMsgQueue::DestoryMessage(int msgid)
{
    if(msgctl(msgid,IPC_RMID,NULL) == -1){
        //LOG (LOG_ERROR, __FILE__, __LINE__, "DestoryMessage--error[%s]",strerror(errno));
    }
}

Makefile

INCLUDE = -I ../ -I ../../
LIB = -lpthread
CLIENTOBJS = TestQueueClient.cpp ../msgqueue.o
SERVEROBJS = TestQueueServer.cpp ../msgqueue.o
G = g++
CFLAGS = -Wall -O -g

all:Client Server

Client:$(CLIENTOBJS)
	$(G) -o Client $(CLIENTOBJS) $(INCLUDE) $(LIB)
Server:$(SERVEROBJS)
	$(G) -o Server $(SERVEROBJS) $(INCLUDE) $(LIB)
msgqueue.o:
	$(G) $(CFLAGS) -c  $(INCLUDE) -o msgqueue.o msgqueue.cpp
clean:
	rm -f Client Server
	rm -f ../*.o
makefile,TestQueueClient.cpp,TestQueueServer.cpp文件,放在同msgqueue.h,msgqueue.cpp目錄test文件夾下,即可完成編譯測試.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章