c-linux-IPC-消息列队MQ-学习

###概念### 

   消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。

    消息队列是一个存放在内核中的消息链表,每个消息队列由消息队列标识符标识。与管道不同的是消息队列存放在内核中,

只有在内核重启(即操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正删除。


###头文件###   

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

###数据结构### 

1.消息缓冲结构 (发送、接收消息都要以这个 数据结构模板 进行)

struct  msgstru{
     long  mtype; //大于0 //消息类型
     char  mtext[2048]; //消息文本
};

说明:消息文本可以为任意类型,由用户根据需要定义,如字符数组、结构体等;

           消息队列中的消息的大小是受限制的,在内核中存在宏定义。

$ ipcs -l 

2、msqid_ds -内核数据结构

linux内核中,每个消息队列都维护一个结构体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 */
};

各个字段的含义:

msg_perm:是一个ipc_perm(定义在头文件sys/ipc.h)的结构,保存了消息队列的存取权限,以及队列的用户ID、组ID等信息。

msg_first:指向队列中的第一条消息

msg_last:指向队列中的最后一条消息

msg_stime:向消息队列发送最后一条信息的时间

msg_rtime:从消息队列取最后一条消息的时间

msg_ctime:最后一次变更消息队列的时间

msg_lcbytes:消息队列中所有消息占的字节数

msg_qnum:消息队列中的消息数目

msg_qbytes:消息队列的最大字节数

msg_lspid:向消息队列发送最后一条消息的进程ID

msg_lrpid:从消息队列读取最后一条消息的进程ID

3、ipc_perm -内核数据结构

结构体ipc_perm保存着消息队列的一些重要的信息,比如消息队列关联的键值,消息队列的用户ID,组ID等。

定义在sys/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;
};

几个主要字段的含义如下:

key:创建消息队列用到的键值key

uid:消息队列的用户ID

gid:消息队列的组ID

cuid:创建消息队列的进程用户ID

cgid:创建消息队列进程组ID


###常用接口函数### 

1. 判断消息列队是否存在  存在返回消息列队msqid,否则返回负值

int msqid = msgget(MSGKEY,IPC_EXCL); //通过key键MSGKEY(整数)与msqid关联


2.创建消息列队  成功返回消息列队msqid,否则返回负值

int msqid = msgget(MSGKEY,IPC_CREAT| 0666); /*创建消息列队*/ //权限读写


3.发送消息  发送失败返回负值

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

//msqid:消息队列的标识码

//msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,结构由用户根据消息模板自行定义

//msgsz:消息的大小

//msgflg:用来指明核心程序在队列满了的情况下所应采取的行动。如果msgflg为常数IPC_NOWAIT,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,并设定错误码(errno)为ENOMSG;如果msgflg为0,则在msgsnd()执行时若是消息队列已满,采取阻塞等待的处理模式。

4.接收消息

size_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

//msqid:消息队列的标识码

//msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,结构由用户根据消息模板自行定义

//msgsz:消息的大小

//msgtyp: 0--接收所有消息类型的消息  正数--只接收该类型的消息

// msgflg:用来指明核心程序在队列为空的情况下所应采取的行动。如果msgflg为常数IPC_NOWAIT,则在执行msgrcv()时发现列队为空,不做等待马上返回-1,并设定错误码为ENOMSG;当msgflg为0时,采取阻塞等待的处理模式。

5.删除消息列队

msgctl(msqid,IPC_RMID, 0);

###测试例程###

/*MQ snd.c*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#define MSGKEY 201844  //2018/04/04

struct msgstru{
    long msgtype;
    char msgtext[2048];
};

int main(){
    struct msgstru msg;
    int mtype;
    char str[256];
    int ret;
    int mqid;

    mqid = msgget(MSGKEY,IPC_EXCL);  /*检查消息队列是否存在*/
    if(mqid < 0){
        mqid = msgget(MSGKEY,IPC_CREAT|0666);  /*创建消息列队*/
        if(mqid < 0){
            printf("create mq failed: errno=%d,%s \n",errno,strerror(errno));
            exit(-1);
        }
    } 

    while(1){
        printf("input msgtype:[终止程序输入0]");
        scanf("%d",&mtype);
        if(mtype == 0){
            break;
        }
        printf("input msgtext:");
        scanf("%s",str);
        msg.msgtype=mtype;
        strcpy(msg.msgtext,str);

        //发送消息  //列队满时不阻塞,返回-1
        ret=msgsnd(mqid,&msg,sizeof(struct msgstru),IPC_NOWAIT);
        if(ret < 0){
            printf("msgsnd() write msg failed,errno=%d,%s \n",errno,strerror(errno));
            exit(-1);  
        }
    }

    msgctl(mqid,IPC_RMID,0);   //删除消息列队

    return 0;
}
/*MQ mqrcv.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <errno.h>

#define MSGKEY 201844

struct msgstru{
    long msgtype;
    char msgtext[2048];
};

void childproc(){
    struct msgstru msg;
    int mqid;
    int ret;
    char str[512];

    while(1){
        mqid = msgget(MSGKEY,IPC_EXCL);
        if(mqid < 0){
            printf("msq not existed! errno=%d [%s]\n",errno,strerror(errno)); 
            sleep(3);
            continue;
        } 

        //ret=msgrcv(mqid,&msg,sizeof(struct msgstru),0,0);  //列队为空 阻塞
        //只接收消息类型为1的消息
        ret=msgrcv(mqid,&msg,sizeof(struct msgstru),1,0);  //列队为空 阻塞
        printf("pid=[%d]  mtype=[%ld]  mtext=[%s] \n",getpid(),msg.msgtype,msg.msgtext);
    }

    exit(0);
}

int main(){
    int i,cpid;
    cpid=0;  //局部变量初始化

    //创建5个子进程 监听消息列队
    for(i=0;i<5;i++){
        cpid=fork();
        if(cpid < 0){  //主进程
            printf("fork failed! \n");
        }else if(cpid == 0){  //子进程走这
            childproc();
        }
    }

    return 0;
}

####参考####

https://www.cnblogs.com/lpshou/archive/2013/06/20/3145651.html

https://www.cnblogs.com/zhangxuan/p/6724068.html

猜你喜欢

转载自blog.csdn.net/qq_24243483/article/details/79816209
今日推荐