zoukankan      html  css  js  c++  java
  • 消息队列

    回顾:
    进程间通信方式:
    信号,管道
    消息队列,共享内存,信号量
    sokcet

    信号:
    本质就是软中断
    signal(信号,函数指针);
    void func(int);
    kill(pid,signo);
    raise(signo);
    alarm(seconds);
    pause();

    kill -9 PID

    --------------------------------
    管道:
    1.基本概念:
    管道本质上还是以文件作为通信的媒介,该文件比较特殊,叫管道文件
    管道分为两类:
    无名管道(pipe)和有名管道(fifo)
    有名管道:由程序员手动创建,实现任意两个进程之间的通信
    无名管道:调用系统函数创建,实现父子进程间的通信。

    无名管道的创建与关闭:
    管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1],其中fd[0]固定用于读管道,fd[1]固定写管道,这样构成一个半双工的通道。

    管道关闭时,只需将这两个文件描述符关闭即可,使用close函数。

    管道创建函数:
    int pipe(int pipefd[2]);
    功能:用于内核中创建无名管道
    pipefd:整形数组,用来存放管道的读写文件描述符
    pipefd[0]:用来存放读端的文件描述符
    pipefd[1]:用来存放写端的文件描述符
    返回:
    成功返回0
    失败返回-1,errno被设置

    管道读写说明:
    用pipe()这个函数创建的管道两端处于同一进程之中,因此,在实际中没有太大意义。
    通常的做法:先创建一个管道,再通过fork()函数创建一个子进程,子进程会继承父进程所创建的管道,为了实现父子进程间的通信,需要把无关的读端或写端的文件描述符关闭。


    管道的特点:
    pipe创建的管道是阻塞方式的。
    去读一个管道,如果管道里没有数据,则阻塞,直到有数据可读
    往管道中写数据,如果管道不可写,则阻塞,直到文件可写。
    数据写到管道的写端,内核会把这些数据缓存,直到有进程来读。
    数据一旦被读走,就没有了。


    ---------------------
    有名管道
    为了克服无名管道的缺点,提出了有名管道。
    该管道用于不同进程之间的通信,它提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统
    在建立了管道之后,两个进程就可以把它当作普通文件一样进行读写操作,不支持lseek定位操作
    要注意的是,有名管道的名字存在于文件系统中,内容放在内存中。
    mkfifo f1.pipe

    1.有名管道的创建
    int mkfifo(const char *pathname, mode_t mode);
    pathname:是一个普通的路径名,也就是创建后FIFO文件的名字
    mode:与打开普通文件open()函数中的mode参数相同
    1.0666,0777,0644...
    2.S_IRUSR S_IWGRP S_IXOTH...
    返回值:成功返回0
    失败返回-1,errno被设置

    注:如果mkfifo的第一个参数是个已经存在的路径名时,会返回EEXIST错误
    所以,一般最典型的写法是调用代码首先检查是否返回该错误,如果确实返回该错误,那么只需要调用打开FIFO的函数就可以了。

    2.有名管道的打开规则:
    有名管道比无名管道多了一个打开操作:open()


    --------------------------------------
    共享内存:
    共享内存是以一块内存作为IPC交互的媒介,这块内存由内核维护和管理,允许其它进程映射,共享内存效率是最高的。

    1.共享内存的操作流程:
    1.创建/获取它共享内存
    2.映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问。
    3.正常使用
    4.解除映射
    5.如果确保不再使用,删除共享内存对像

    2.相关的API函数
    1.shmget函数
    #include <sys/ipc.h>
    #include <sys/shm.h>
    int shmget(key_t key, size_t size, int shmflg);
    功能:主要用于创建/获取共享内存段
    第一个参数:key,标识共享内存的键值(就像文件的标识是文件名)
    第二个参数:要建立的共享内存的长度
    第三个参数:有效的标志有IPC_CREAT和IPC_EXCL,与open()函数的O_CREAT与O_EXCL相当。
    IPC_CREAT:如果共享内存不存在,则创建一个
    IPC_EXCL:与IPC_CREAT搭配使用,如果存在,创建失败

    返回值:成功返回一个shmid(类似于打开/创建一个文件获得文件描述符一样)
    失败返回-1,errno被设置
    注:
    当新创建共享内存时,一般情况下第三个参数是:0666 | IPC_CREAT | IPC_EXCL

    2.ftok()函数
    key_t ftok(const char *pathname, int proj_id);
    功能:根据文件路径和项目编号生成key值
    第一个参数:字符串形式的文件路径,要求文件必须存在,且可访问。
    第二个参数:整型的项目编号,要求必须是非0,低8位被使用,一般写一个字符代替
    返回值:成功返回key_t类型的key值
    失败返回-1,errno被设置


    3.shmat函数
    void *shmat(int shmid, const void *shmaddr, int shmflg);
    功能:用于映射共享内存到进程的地址空间
    第一个参数:shmid,共享内存的ID,shmget()函数的返回值
    第二个参数:shmaddr,将共享内存映射到指定的地址,给NULL/0由系统指定
    第三个参数:shmflg,默认给0即可,表示共享内存可读可写
    返回值:成功返回共享内存的映射地址
    失败返回-1,errno被设置


    4.shmdt函数
    int shmdt(const void *shmaddr);
    功能:用于取消共享内存与用户进程的映射
    参数:shmaddr:就是shmat的返回值

    5.shmctl函数
    int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    功能:用于对指定的共享内存执行指定的操作
    第一个参数:shmid共享内存的ID,即shmget()返回值
    第二个参数:cmd,操作命令
    IPC_RMID ---删除共享内存,此时第三个参数给NULL即可
    第三个参数:buf结构体指针
    返回值:成功返回0
    失败返回-1,errno被设置


    3.相关命令
    ipcs -m


    -------------------------
    消息队列
    1.基本概念:
    消息队列就是在系统内核中保存的一个用来保存消息的队列,这个队列不是简单的先进先出,还可以控制消息更为灵活。

    2.基本通信流程
    1.获取key值,使用ftok()函数
    2.创建/获取消息队列,使用msgget()函数
    3.发送/接收消息,使用msgsnd()函数和msgrcv()函数
    4.如果不再使用,删除消息队列,使用msgctl()函数



    3.相关API函数
    1.msgget()函数
    int msgget(key_t key, int msgflg);
    功能:主要用于创建/获取消息队列
    第一个参数:key,ftok()的返回值
    第二个参数:msgflg消息队列的创建标志
    IPC_CREAT 创建
    IPC_EXCL 与IPC_CREAT搭配使用,若消息队列存在,则创建失败
    返回值:成功返回消息队列的ID,失败返回-1,errno被设置
    注:
    当创建新的消息队列时,需要指定权限,如0644

    2.msgsnd()函数
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    功能:主要用于向指定的消息队列发送指定的消息
    第一个参数:msqid消息队列的ID,msgget的返回值
    第二个参数:msgp消息的首地址
    消息的一般形式如下:
    struct msgbuf {
    long mtype; /* 消息类型, 必须大于0 */
    char mtext[1]; /* 消息内容,可以使用其它的数据类型 */
    };
    第三个参数:msgsz消息的大小,是指定的消息结构体中的内容的大小,不包括消息类型
    第四个参数:发送的标志,一般给0即可
    返回值:成功返回0,失败返回-1

    3.msgrcv()函数
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
    功能:主要用于从消息队列中接受消息存到指定的位置
    第一个参数:msqid消息队列的ID,msgget的返回值
    第二个参数:消息的首地址(表示存到哪里去)
    第三个参数:消息的大小
    第四个参数:消息的类型
    0 ---表示接受消息队列中的第一个消息
    >0 ---表示接受消息队列中的第一个类型为msgtyp的消息
    <0 ---表示接受消息队列中的第一个小于等于msgtyp绝对值的消息,其中最小的类型优先读取。

    第五个参数:接受的标志,一般给0即可
    返回值:成功返回实际接受的数据大小,失败返回-1,errno被设置

    4.msgctl()函数
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
    第一个参数:msqid消息队列的ID,msgget的返回值
    第二个参数:cmd操作命令
    IPC_RMID 删除消息队列,此时第三个参数给NULL即可。
    第三个参数:结构指针

    返回值:成功返回0,失败返回-1,errno被设置

    4.相关命令
    ipcs -q


    作业:
    1.写一个代码尝试求管道的大小
    2.往共享内存中写数据除了memcpy其它的方式
    3.写一个程序从消息队列中收消息

    /*************************************************************************
    > File Name: msg.c
    > Author: csgec
    > Mail: longer.zhou@gmail.com
    > Created Time: Wed 10 Aug 2016 04:58:33 PM CST
    ************************************************************************/

    #include<stdio.h>
    #include<sys/ipc.h>
    #include<signal.h>
    #include<stdlib.h>
    #include<string.h>

    int msgid;
    void hander(int num)
    {
    if(num == SIGINT)
    {
    printf("消息队列删除中... ");
    sleep(3);
    if(msgctl(msgid,IPC_RMID,0) < 0)
    {
    perror("msgctl");
    }
    printf("消息队列删除成功! ");
    exit(0);
    }
    }
    struct msgbuf
    {
    long mtype;
    char mtext[256];

    };
    int main()
    {
    key_t key;
    key = ftok(".",100);
    if(key < 0)
    {
    perror("ftok");
    exit(-1);
    }
    msgid = msgget(key,0666 | IPC_CREAT | IPC_EXCL);
    if(msgid < 0)
    {
    perror("msgget");
    exit(-1);
    }
    struct msgbuf msg1;
    struct msgbuf msg2;
    msg1.mtype = 1;
    strcpy(msg1.mtext,"hello msg1...");

    msg2.mtype = 2;
    strcpy(msg2.mtext,"hello msg2...");
    msgsnd(msgid,&msg1,sizeof(msg1.mtext),0);
    msgsnd(msgid,&msg2,sizeof(msg2.mtext),0);
    printf("使用ctrl + c退出并删除消息队列! ");
    signal(SIGINT,hander);
    while(1)
    {

    }

    }

  • 相关阅读:
    PHP 类的继承问题
    爬虫第一章
    如何给CBV添加装饰器
    结巴分词 gensim系数矩阵相似度 pypinyin
    elasticsearch 第二章 elasticsearch的详细用法及参数
    运维自动化 第五章 playbook 模块补充
    运维自动化 第四章 模块
    运维自动化 第三章 ansible
    正则补充
    运维自动化 第二章 openpyxl的用法,读写excel内容
  • 原文地址:https://www.cnblogs.com/liudehao/p/5758358.html
Copyright © 2011-2022 走看看