进程通信(IPC)之消息队列

消息队列:

消息队列是消息的链表,它是一个链式队列,和管道类似,可用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配合使用。消息队列的总数,消息的大小,单个队列的容量都是有限制的。消息队列是无连接的,内核不会维护引用其进程数。则其消息队列怎么安全删除,怎么确保不用就删除就成了一个问题。

 

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页