消息队列

消息队列

消息队列 提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。

  • 消息队列与命名管道一样,每个消息的最大长度MSGMAX,每个消息队列的总的字节数上限MSGMNB,消息队列的总数上限MSGMNI
  • 消息队列是基于消息的
  • 管道是基于字节流的
  • 消息队列的读取不一定是先入先出
  • 消息队列是用链表实现的
  • 消息的生命周期—-随内核
IPC对象数据结构

内核为每个IPC对象维护一个数据结构

///usr/include/linux/ipc.h  文件路径
struct ipc_perm
{
    __kernel_key_t  key;
    __kernel_uid_t  uid;
    __kernel_gid_t  gid;
    __kernel_uid_t  cuid;
    __kernel_gid_t  cgid;
    __kernel_mode_t mode; 
    unsigned short  seq;
};

消息队列、信号量、共享内存都有这样一个共同的数据结构

消息队列数据结构
///usr/include/linux/msg.h  文件路径
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 */
};
查看和删除消息队列
  • ipcs -q :查看消息队列
  • ipcrm -q (msqid) :删除消息队列
创建和获取消息队列

函数原型: int msgget(key_t key, int msgflg);

int commMsgQueue(int flags) 
{
    key_t key = ftok(PATHNAME, PROJ_ID);
    if(_k < 0){
        perror("ftok");
        return -1;
    }
    int msgqid = msgget(key, flags);
    if(msgqid < 0)
    {
        perror("msg");
        return -2;
    }
    return msg_id;
}
int createMsgQueue()   //创建消息队列
{
    return commMsgQueue(IPC_CREAT|IPC_EXCL|0666); 
}
int getMsgQueue()//获取消息队列
{
    return commMsgQueue(IPC_CREAT);
}
删除消息队列

函数原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);

int destoryMsgQueue(int msgid)   //删除消息队列
{
    if(msgctl(msgid, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    return 0;
}
向队列中读/写消息

函数原型:(写) int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数原型:(读)ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

int sendMsg(int msgid, long type, const char *_info)  //发送
{
    struct msgbuf msg;
    msg.mtype = type;
    strcpy(msg.mtext, _info);
    if(msgsnd(msgid, &msg, sizeof(msg.mtext), 0) < 0)
    {
        perror("msgsnd");
        return -1;
    }
    return 0;
}

int recvMsg(int msgid, long type, char out[])  //接收
{
    struct msgbuf msg;
    if(msgrcv(msgid, &msg, sizeof(msg.mtext), type, 0) <0)
    {
        perror("msgrcv");
        return -1;
    }
    strcpy(out, msg.mtext);
    return 0;
}

msggp:指向消息缓存区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构:
struct msgbuf{
long mtype;//大于0
char mtext[用户指定大小];
};

运用消息队列实现进程间通信
<comm.h>
#ifndef _COMM_H_
#define _COMM_H_

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

#define PATHNAME "."
#define PROJ_ID 0x6666
#define SIZE 1024
#define SERVER_TYPE 1
#define CLIENT_TYPE 2

struct msgbuf{
    long mtype;
    char mtext[SIZE];
};

int createMsgQueue();   //创建消息队列
int getMsgQueue();//获取消息队列
int sendMsg(int msgid, long type, const char *_info);  //发送
int recvMsg(int msgid, long type, char out[]); //接收
int destoryMsgQueue(int);//摧毁消息队列
#endif
<comm.c>
#include"comm.h"
int commMsgQueue(int flags) 
{
    key_t _k = ftok(PATHNAME, PROJ_ID);
    if(_k < 0){
        perror("ftok");
        return -1;
    }
    int msg_id = msgget(_k, flags);
    if(msg_id < 0)
    {
        perror("msg");
        return -2;
    }
    return msg_id;
}
int createMsgQueue()   //创建消息队列
{
    return commMsgQueue(IPC_CREAT|IPC_EXCL|0666); 
}
int getMsgQueue()//获取消息队列
{
    return commMsgQueue(IPC_CREAT);
}
int sendMsg(int msgid, long type, const char *_info)  //发送
{
    struct msgbuf msg;
    msg.mtype = type;
    strcpy(msg.mtext, _info);
    if(msgsnd(msgid, &msg, sizeof(msg.mtext), 0) < 0)
    {
        perror("msgsnd");
        return -1;
    }
    return 0;
}
int recvMsg(int msgid, long type, char out[])  //接收
{
    struct msgbuf msg;
    if(msgrcv(msgid, &msg, sizeof(msg.mtext), type, 0) <0)
    {
        perror("msgrcv");
        return -1;
    }
    strcpy(out, msg.mtext);
    return 0;
}
int destoryMsgQueue(int msgid)   //摧毁消息队列
{
    if(msgctl(msgid, IPC_RMID, NULL) < 0)
    {
        perror("msgctl");
        return -1;
    }
    return 0;
}
<server.c>
//先发送后接收
#include"comm.h"
int main()
{
    int msgid = createMsgQueue();    //创建消息队列
    printf("msgid : %d\n",msgid);
    char buf[SIZE];
    while(1) 
    {
        recvMsg(msgid, CLIENT_TYPE, buf);
        printf("client# %s\n",buf);
        printf("Please Enter$ ");
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof(buf)-1);
        if(s>0)
        {
            buf[s-1] = '\0'; 
            sendMsg(msgid,SERVER_TYPE, buf);
        }   
    }
    destoryMsgQueue(msgid);   //摧毁消息队列
    return 0;
}
<client.c>
//先接收后发送
#include"comm.h"
int main()
{
    int msgid = getMsgQueue();     //创建消息队列
    printf("msgid : %d\n",msgid);
    char buf[SIZE];
    while(1) //send --> recv
    {

        printf("Please Enter$ ");
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof(buf)-1);
        if(s>0)
        {
            buf[s-1] = '\0'; 
            sendMsg(msgid, CLIENT_TYPE, buf);
        }
        recvMsg(msgid, SERVER_TYPE, buf);
        printf("server# %s\n",buf);
    }
    destoryMsgQueue(msgid);  //摧毁消息队列
    return 0;
}
<makefile>
client_=client
server_=server
cc=gcc

cliSrc=client.c comm.c
serSrc=server.c comm.c

.PHONY:all
all:$(client_) $(server_)

$(client_):$(cliSrc)
    $(cc) -o $@ $^
$(server_):$(serSrc)
    $(cc) -o $@ $^ 

.PHONY:clean
clean:
    rm -f $(client_) $(server_)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值