• 进程通信之消息队列


    1.什么是消息队列
      消息队列以队列形式(消息链表)的数据结构存放在系统内核中的,它可用于客户进程与服务进程之间的双向数据流。但是它跟信号量、共享内存一样,都只在系统范围内通信,且其在系统里没有名字,其内容不会自动删除,没有文件描述符。这些缺点导致现代进程通信基本不用它了。尽管如此,但是今天我还是需要了解它。

    2.消息的数据类型

      首先,在头文件<sys/msg.h>中,定义了我们可用传给消息队列的数据类型:

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

    mtype是指该消息的类型,区分出不同的消息,我们就可以辨别出消息的来源,比如我们把客户进程的mtype设置为1,服务进程的mtype设置为2,这样我们就可用实现客户进程和服务进程的双向通信了。注意,msgbuf只是一个模板,我们可用根据这个模板,自己定义其它的数据结构传给消息队列,如:

    struct msgbuf_custom
    {
    long mtype;
    char name[256];
    int age;
    }

    我们自己定义了msgbuf_custom,它可用向消息队列传送字符串的name,整形的age。

    3.相关函数

      (1). key_t ftok(char * fname, int id)

        ftok()的作用是产生一个独一无二的ID,把这个ID提供给IPC对象来作为的key,以便其与其它IPC对象通信。fname就是你指定的文件名(已经存在的文件名),一般使用当前目录,如:

    key_t key = ftok(".", 1); //将fname设为当前目录,id是子序号

    其返回值一般是取fname的索引节点号,然后加上id。

      (2). int msgget(key_t key,int msgflag)
        msgget用于打开或者产生一个消息队列,key即上面函数产生的key,msgget具体行为跟msgflag的取值有关:
        IPC_CREAT:不存在则创建之,存在则打开之.
        IPC_EXCL:该值要与IPC_CREAT(通过|)连用才有意义。与IPC_CREAT连用表示,不存在则创建之,存在则报错返回-1,错误码是EEXIST。下面是一个封装的打开消息队列的函数:

    int openMsg(key_t key)
    {
    int mid;
    if( ( mid = msgget( key , IPC_CREAT|0660) )==-1 )
    {
    perror(
    "open error:");
    exit(
    1);
    }
    return mid;
    }

    注意我们给其加上了权限0660.

      (3). int msgsnd(int mid,struct msgbuf *buf,int msgsize,int msgflag);

        msgsnd()用于向消息队列发送一条消息,mid即上面函数msgget()返回的结果,buf即我们要发送的消息,msgsize即消息的大小,msgflag跟具体取值有关:
          0,则忽略。
          IPC_NOWAIT,如果消息队列没有满,则buf就不写入了,返回,不造成阻塞。
       下面我们封装一个发送消息队列的函数:

    int writeMsg(int mid ,struct msgbuf *mbuf)
    {
    int rs;
    int len =sizeof(struct msgbuf)-sizeof(long);
    if( rs =msgsnd(mid,mbuf,len,0)==-1 )
    {
    perror(
    "write error:");
    exit(
    1);
    }
    return rs;
    }

      (4). int msgrcv(int mid,struct msgbuf *buf,int msgsize,long mtype,int msgflag)
        msgrcv()用于取出mtype类型的消息到buf中,其中msgflag的取值可以是:
          0,忽略。
          IPC_NOWAIT:如果消息队列为空,则返回ENOMSG,不造成进程阻塞。
          MSG_NOERROR:如果取得的消息大于msgsize,则只返回msgsize大小的消息。如果设置该值,将会导致消息留在队列中。
        下面我们封装一个发送消息队列的函数:

    int readMsg(int mid,long mtype,struct msgbuf *mbuf)
    {
    int rs;
    int len =sizeof(struct msgbuf)-sizeof(long);

    if((rs=msgrcv(mid,mbuf,len,mtype,0))==-1)
    {
    perror(
    "read is error");
    exit(
    1);
    }
    return rs;
    }

    利用msgflag的特性,我们还可用检测指定类型(mtype)的的消息是否存在:

    int msg_exist(int mid,long mtype)
    {
    int rs;
    if( (rs=msgrcv(mid,NULL,0,mtype,IPC_NOWAIT))==-1 )
    {
    if(errno==E2BIG)
    {
    return1;
    }
    else{
    perror(
    "exist is error");
    exit(
    0);
    }
    }
    return0;
    }

      (5). int msgctl(int mid,int cmd ,struct msqid_ds *buf)
        cmd可用取以下值:
        IPC_STAT:取出系统保存的消息队列的msqid_ds数据到buf中.
        IPC_SET :根据buf重新设置系统保存的消息队列的msqid_ds数据.
        IPC_EMID:将该队列从系统删除。
        下面是一个封装用来删除消息队列的函数:

    void delMsg(int mid)
    {
    msgctl(mid,IPC_RMID,
    0);
    }

    下面是一个封装用来重新设置消息队列的权限的函数:

    void modifyMsg(int mid,char*mode)
    {
    struct msqid_ds mds;

    msgctl(mid,IPC_STAT,
    &mds);
    sscanf(mode,
    "%ho",&mds.msg_perm.mode);
    msgctl(mid,IPC_SET,
    &mds);
    }


    3.一个实例
      附件提供一个实例,下载后请使用:gcc -o msgtool msgtool.c func.c编译。
      用法:./msgtool w 1 "codebean"    //将数据“codebean”写作为1的类型入消息队列
         ./msgtool r 1            //取出消息队列类型为1的消息
                     ./msgtool d            //删除该消息队列            

  • 相关阅读:
    python生成随机整数
    pycharm怎么修改python路径
    Linux 在 TOP 命令中切换内存的显示单位
    MySQL之limit使用
    Fiddler设置抓取FireFox火狐的包
    火狐FireFox看视频不能全屏显示的问题
    【.Net】exe加密/加壳工具.Net Reactor
    【WPF】使用控件MediaElement播放视频
    【WPF】在MenuItem中下划线“_”显示不正常
    【.Net】Thread.Start()与ThreadPool.QueueUserWorkItem()的区别
  • 原文地址:https://www.cnblogs.com/codebean/p/2082791.html
Copyright © 2020-2023  润新知