消息队列:
消息队列是消息的链表,它是一个链式队列,和管道类似,可用cat/proc/sys/kernel/msgmax查看
与管道不同的是,消息对立进行通信是面向消息得,接受得是整条消息(且存在边界,管道通信则没有),不能读取部分消息或读取多条消息。在消息队列中,可以把消息分类型,则可以分类型读取想要的信息。它的主要特点是:一旦被创建,需要需要用户调用接口函数或命令将其删除,
消息队列可以实现双向通信
常用函数:
int msgget(key_t key, int flag)
int msgctl(int msgqid, int cmd, struct msqid_ds *buf)
int msgsnd(int msgqid, const void *msgp, size_t size, int flag)
int msgrcv(int msgqid, void *msgp, size_t size, long msgtype, int flag)
下面为使用消息队列实现文件服务器应用程序:
服务端:
#include<string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "curr_time.h"
#include "error_functions.h"
#include "get_num.h"
#include <signal.h>
#include "tlpi_hdr.h"
#include <sys/types.h>
#define SERVER_KEY 0x1aaaaaa1
#define PATH_MAX 4096
struct requestMsg{
long mtype;
int clientid;
char pathname[PATH_MAX];
};
#define REQ_MSG_SIZE (offsetof(struct requestMsg,pathname)-offsetof(struct requestMsg,clientid)+PATH_MAX)
#define RESP_MSG_SIZE 8192
struct responseMsg{
long mytype;
char data[RESP_MSG_SIZE];
};
struct requestMsg req;
#define RESP_MT_FAILUR 1
#define RESP_MT_DATA 2
#define RESP_MT_END 3
static void grimReaper(int sig)
{
int savedErrno;
savedErrno=errno;
while(waitpid(-1,NULL,WNOHANG)>0)
continue;
errno=savedErrno;
}
static void serveRequest(const struct requestMsg *req)
{
int fd;
ssize_t numRead;
struct responseMsg resp;
fd=open(req->pathname,O_RDONLY);
if(fd<0)
{
resp.mytype=RESP_MT_FAILUR;
snprintf(resp.data,sizeof(resp.data),"%s","Couldn't open");
msgsnd(req->clientid,&resp,strlen(resp.data)+1,0);
exit(EXIT_SUCCESS);
}
printf("open success\n");
resp.mytype=RESP_MT_DATA;
while((numRead=read(fd,resp.data,RESP_MSG_SIZE))>0)
if(msgsnd(req->clientid,&resp,numRead,0)==-1)
break;
resp.mytype=RESP_MT_END;
msgsnd(req->clientid,&resp,0,0);
}
int main(int argc,char **argv)
{
struct requestMsg req;
pid_t pid;
ssize_t msglen;
int serverId;
struct sigaction sa;
serverId=msgget(SERVER_KEY,IPC_CREAT|S_IRUSR|S_IWGRP|S_IWUSR);
if(serverId<0)
{
errExit("msgget");
}
//初始信号量集
sigemptyset(&sa.sa_mask);
sa.sa_flags=SA_RESTART;
sa.sa_handler=grimReaper;
if(sigaction(SIGCHLD,&sa,NULL)==-1)
{
errExit("sigaction");
}
for(;;)
{
msglen=msgrcv(serverId,&req,REQ_MSG_SIZE,0,0);
if(msglen==-1)
{
if(errno==EINTR)
continue;
errMsg("msgrv");
break;
}
pid=fork();
if(pid==-1)
{
errMsg("fork");
break;
}
if(pid==0)
{
serveRequest(&req);
_exit(EXIT_SUCCESS);
}
}
//删除消息队列
if(msgctl(serverId,IPC_RMID,NULL)==-1)
errExit("msgctl");
exit(EXIT_SUCCESS);
}
客服端:
#include<string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "curr_time.h"
#include "error_functions.h"
#include "get_num.h"
#include <signal.h>
#include "tlpi_hdr.h"
#include <sys/types.h>
#define SERVER_KEY 0x1aaaaaa1
#define PATH_MAX 4096
struct requestMsg{
long mtype;
int clientid;
char pathname[PATH_MAX];
};
#define REQ_MSG_SIZE (offsetof(struct requestMsg,pathname)-offsetof(struct requestMsg,clientid)+PATH_MAX)
#define RESP_MSG_SIZE 8192
struct responseMsg{
long mytype;
char data[RESP_MSG_SIZE];
};
struct requestMsg req;
#define RESP_MT_FAILUR 1
#define RESP_MT_DATA 2
#define RESP_MT_END 3
static int chinetId;
static void removeQueue(void)
{
if(msgctl(chinetId,IPC_RMID,NULL)==-1)
errExit("msgctl");
}
int main(int argc,char **argv)
{
struct requestMsg req;
struct responseMsg resp;
int serverId,numMsgs;
ssize_t msgLen,totBytes;
if(argc!=2||strcmp(argv[1],"--help")==0)
{
usageErr("%s pathname\n",argv[0]);
}
if(strlen(argv[1])>sizeof(req.pathname)-1)
{
printf("pathname too (max :%ld byte)\n",(long)sizeof(req.pathname));
}
serverId=msgget(SERVER_KEY,S_IWUSR);
if(serverId==-1)
{
errExit("msgget==server message queue");
}
chinetId=msgget(IPC_PRIVATE,S_IRUSR|S_IWUSR|S_IWGRP);
if(chinetId==-1)
{
errExit("msgget --client message queue");
}
if(atexit(removeQueue)!=0)
errExit("atexit");
req.mtype=1;
req.clientid=chinetId;
strncpy(req.pathname,argv[1],sizeof(req.pathname)-1);
req.pathname[sizeof(req.pathname)]='\0';
if(msgsnd(serverId,&req,REQ_MSG_SIZE,0)==-1)
{
errExit("msgsnd");
}
msgLen=msgrcv(chinetId,&resp,RESP_MSG_SIZE,0,0);
if(msgLen==-1)
{
errExit("msgLen");
}
if(resp.mytype==RESP_MT_FAILUR)
{
printf("%s\n",resp.data);
if(msgctl(chinetId,IPC_RMID,NULL)==-1)
{
errExit("msgctl");
}
exit(EXIT_FAILURE);
}
totBytes=msgLen;
for(numMsgs=1;resp.mytype==RESP_MT_DATA;numMsgs++)
{
msgLen=msgrcv(chinetId,&resp,RESP_MSG_SIZE,0,0);
if(msgLen==-1)
{
errExit("msgrcv");
}
totBytes+=msgLen;
}
printf("Receive %ld byte(%d message)\n",(long)totBytes,numMsgs);
exit(EXIT_SUCCESS);
}
使用消息队列存在的问题:
进程间数据传输有多种方式:一般常见的为分隔符的字节流形式(管道,FIFO以及流socket),分隔符的消息形式(System V消息队列,POSIX消息队列,数据报socket)。System V消息队列中有一个数据类型的选项,对于读数据就可以根据类型来读数据。下面就是缺点了,消息队列通过标识符引用(标识符创建比较麻烦),大多数I/O机制使用的是文件描述符,将不能与poll,select配合使用。消息队列的总数,消息的大小,单个队列的容量都是有限制的。消息队列是无连接的,内核不会维护引用其进程数。则其消息队列怎么安全删除,怎么确保不用就删除就成了一个问题。