• IPC进程间通信---消息队列


    消息队列

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

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

        Linux内核中,每个消息队列都维护一个结构体msqid_ds,此结构体保存着消息队列当前的状态信息。该结构定义在

    件linux/msg.h中,具体如下:

    struct msqid_ds
    {
       struct_ipc_perm  msg_perm;   //是一个ipc_perm的结构,保存了消息队列的存取权限,以及队列的用户ID、组ID等信息
       struct_msg  *msg_first;   //指向队列中的第一条消息
       struct_msg  *msg_last;   //指向队列中的最后一条消息
       __kernel_t time_t  msg_stime;   //向消息队列发送最后一条信息的时间
       __kernel_t time_t  msg_rtime;   //从消息队列取最后一条信息的时间
       __kernel_t time_t  msg_ctime;  //最后一次变更消息队列的时间
       unsigned long  msg_lcbytes;
       unsigned long  msg_lqbytes;
       unsigned short  msg_cbytes;    //消息队列中所有消息占的字节数
       unsigned short  msg_qnum;     //消息队列中消息的数目
       unsigned short  msg_qbytes;   //消息队列的最大字节数
       __kernel_ipc_pid_t  msg_lspid;  //向消息队列发送最后一条消息的进程ID
       __kernel_ipc_pid_t  msg_lrpid;   //从消息队列读取最后一条信息的进程ID
    };

       消息队列是随着内核的存在而存在的,每个消息队列在系统范围内对应惟一的键值。要获得一个消息队列的描述符,

    提供该消息队列的键值即可,该键值通常由函数ftok返回,该函数原形为:

       #include <sys/ipc.h>

       key_t ftok(const char *pathname,int proj_id);

       创建一个新的消息队列或访问一个已存在的消息队列前需要使用ftok函数得到key值,下面是ftok函数的包裹函数:

    key_t Ftok(const char *pathname,int proj_id)
    {
      key_t key= ftok(pathname,proj_id);
      if(key== -1)
      {
        perror("ftok.");
        exit(1);
      }
      return key;
    }

       消息队列的创建或打开:

       使用函数msgget进行消息队列的创建或打开。该函数定义在头文件<sys/msg.h>中,该函数的原形为:

       #include <sys/ipc.h>
       #include <sys/msg.h>
       int semget(key_t key,int msgflg);

       该函数如果调用成功则返回一个消息队列的描述符,否则返回-1。函数的第一个参数为ftok()函数得到的键值第二

    个参msgflg是一个标志参数,可以取如下值:

    • IPC_CREAT:如果内核中不存在键值与key相等的消息队列,则新建一个消息队列;如果存在这样的消息队列,返回该

    息队列的描述符。

    • IPC_EXCL:和IPC_CREAT一起使用,如果对应键值的消息队列已经存在,则出错,返回-1。

          消息队列的读写:

       创建了一个消息队列后,就可以对消息队列进行读写了,函数msgsnd用于向消息队列发送写数据,该函数定义在头文

    <sys/msg.h>中,函数原形为:

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

       函数中,参数msgqid为函数向msgid标识的消息队列发送一个消息(msqid是由msgget返回的标识符);参数msgp指向

    送的消息是一个结构指针,该结构如下所示;参数msgsz为要发送的消息的大小,不包含消息类型占用的4个字节;参数

    msgflg为操作标志位,可以设置为0或者IPC_NOWAIT,如果msgflg为0,则当消息队列已满时,msgsnd将会阻塞,直到消息

    可以写消息队列,如果msgflg为IPC_NOWAIT,当消息队列已满的时候,msgsnd函数将不等待立即返回。msgsnd函数成功

    返回0,失败返回-1。

    struct msgbuf
    {
       long mtype;           //代表消息类型,给消息指定类型,可以使得消息在一个队列中重复使用
       char mtext[1];        //消息内容
    };

        消息队列中放入数据后,其它进程就可以读取其中的消息了。读取消息的系统调用为msgrcv()函数,该函数定义在头

    <sys/msg.h>中,函数原形为:

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

        函数中,参数msqid为消息队列描述符(由msgget返回的标识符);参数msgp同上面的参数相同;参数msgsz为消息缓

    冲区的大小;参数msgtyp为请求读取的消息类型;参数msgflg为操作标志位,msgflg可以为IPC_NOWAIT、IPC_EXCEPT、IP

    C_NOERROR三个常量,IPC_NOWAIT:如果没有满足条件的消息,调用立即返回,此时错误码为ENOMSG.IPC_EXCEPT:与msgt

    yp配合使用,返回队列中第一个类型不为msgtyp的消息.IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的msgs

    z字节,则把该消息截断,截断部分将被丢弃。msgrcv函数成功会返回读出消息的实际字节数,否则返回-1。

        消息队列的控制:

        消息队列的属性保存在系统维护的数据结构msqid_ds中,用户可以通过函数msgctl获取或设置消息队列的属性。msg

    ctl在头文件<sys/msg.h>中,函数原形为:

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

        函数中,第一个参数为由msgget返回的标识符;第二个参数为执行的cmd操作;第三个参数为上面已经定义的msqid_d

    s构体类型。系统中定义了3种cmd操作:IPC_STAT、IPC_SET、IPC_RMID,它们的含义如下:

    • IPC_STAT:该命令用来获取消息队列对应的msqid_ds数据结构,并将其保存到buf指向的地址空间。
    • IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中,可设置的属性包括:msg_perm.uid、msg_pe

    rm.gid、msg_perm.mode和msg_qbytes。

    • IPC_RMID:从内核中删除msqid标识的消息队列。

       下面是使用消息队列进行发送消息的例子:

    // myipc.h
    #pragma once
    
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <sys/shm.h>
    #include <sys/msg.h>
    
    union semun
    {
        int val;
        struct semid_ds *buf;
        unsigned short *array;
        struct seminfo *__buf;
    };
    
    key_t Ftok(const char *pathname,int proj_id)
    {
        key_t key= ftok(pathname,proj_id);
        if(key== -1)
        {
            perror("ftok.");
            exit(1);
        }
        return key;
    }
    //utili.h
    #pragma once
    
    #define MSG_BUFFER_LEN 256
    #define SERVER_SEND_FLAG 100
    #define SERVER_RECV_FLAG 200
    
    #define CLIENT_SEND_FLAG 200
    #define CLIENT_RECV_FLAG 100
    
    typedef struct Msg
    {
        long msg_type;
        char msg_text[MSG_BUFFER_LEN];
    }Msg;
    //server.c
    #include "myipc.h"
    #include "utili.h"
    
    int main(int argc,char *argv[])
    {
        key_t msg_key= Ftok(argv[1],atoi(argv[2]));
        int msg_id= msgget(msg_key,IPC_CREAT|0755);
        if(msg_id== -1)
        {
            perror("msgget");
            exit(1);
        }
        Msg msg;
        while(1)
        {
            printf("Ser:");
            scanf("%s",msg.msg_text);
            msg.msg_type= SERVER_SEND_FLAG;
            msgsnd(msg_id,&msg,strlen(msg.msg_text)+ 1,0);
    
            msgrcv(msg_id,&msg,MSG_BUFFER_LEN,SERVER_RECV_FLAG,0);
            printf("Cli:%s
    ",msg.msg_text);
        }
        msgctl(msg_id,IPC_RMID,NULL);
        return 0;
    }
    //client.c
    #include "myipc.h"
    #include "utili.h"
    
    int main(int argc,char *argv[])
    {
        key_t msg_key= Ftok(argv[1],atoi(argv[2]));
        int msg_id= msgget(msg_key,0);
        Msg msg;
        while(1)
        {
            msgrcv(msg_id,&msg,MSG_BUFFER_LEN,CLIENT_RECV_FLAG,0);
            printf("Ser:%s
    ",msg.msg_text);
            printf("Cli:");
            scanf("%s",msg.msg_text);
            msg.msg_type= CLIENT_SEND_FLAG;
            msgsnd(msg_id,&msg,strlen(msg.msg_text)+ 1,0);
        }
        return 0;
    }
  • 相关阅读:
    Fundamentals of Garbage Collection
    CLR的八大特性
    Navigation and Pathfinding
    Work-Stealing in .NET 4.0
    Graphics.Blit
    整数的可除性
    关于强度
    重心坐标空间
    性能测试中TPS和并发用户数
    LoadRunner 12.02 安装以及汉化教程
  • 原文地址:https://www.cnblogs.com/XNQC1314/p/9094556.html
Copyright © 2020-2023  润新知