进程间通信-消息队列

System V IPC

  • IPC : Inter-Process Communication(进程间通讯)
  • System V 是早期的unix 系统,曾经被称为 AT &T System ,是 unix 操作系统中比较重要的一个分支,现在0Linux 系统一般都支持 System V IPC
  • System V IPC 对象共有三种
    • 消息队列
    • 共享内存
    • 信号量
  • System V IPC 是由内核维护的若干个对象,通过 ipcs 命名查询

每个 IPC 对象都有一个唯一的 ID,可以通过 ftok()函数生成,ftok 函数具体说明如下
函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
函数原型
key_t ftok(const char *pathname, int proj id);
函数参数

  • pathname: 文件路径名
  • proj_id : 8 bit 的 id 整数0
    函数返回值
  • 成功: 返回合成的 key
  • 失败 :-1,并设置 errno
    注意:
  • key 由 文件的 inode 节点号 与 proj_id 构成
  • inode 节点号 : 每个存在的文件操作系统都会有唯一的编号,通过 ls -i命令查看

消息队列简介

  • 消息队列就是一个消息的列表,进程可以在消息队列中添加消息和的读取消息
  • 消息队列具有一定的FIFO特性,具有无名管道与有名管道的各自的优势,可以支持任意两个进程的进程间通讯
  • 消息队列是属于 sytem v ipc 的一种,由内核维护与管理 可以通过 ipcs -q 查看

二、创建消息队列

创建消息队列调用 msgget 函数
函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型
int msgget(key_t key, int msgflg);
函数参数

  • key: 由 ftok 函数合成
  • msgflg : 消息队列标志
    • IPC_CREAT: 创建标志
    • IPC_EXCL : 如果消息队列存在,则报错,errno 设置为 EEXIST
    • 权限控制标志
      函数返回值
  • 成功 : 返回 消息队列 id
  • 失败: 返回-1,并设置 errno

对于已经创建的消息队列,如果 key 一样,则直接获取这个消息队列ID,对于新创建的消息队列,每次消息队列的ID 不一定相同,即使key一致

删除消息队列

删除消息队列需要调用 msgctl 函数,具体信息如下
函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型
int msgctl(int msqid, int cmd, struct msqid ds *buf)
函数参数

  • msqid : 消息队列 id
  • cmd : 命令字
    • IPC_STAT: 获取消息队列属性
    • IPC_SET : 设置消息队列属性
    • IPC_RMID : 删除消息队列,用此命今时,第三个参数为 NULL
  • buf : 消息队列属性结构体对象指针
    函数返回值
  • 成功:
    IPC_STAT,IPC_SET, and IPC_RMID 返回
  • 失败: 返回-1,并设置 errno

创建一个消息队列,并打印消息队列 ID,然后删除消息队列,使用 ipcs 命令验证

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

#define PATHNAME "."
#define PROID 10

int main(){
        key_t key;
        int msgid;

        key = ftok(PATHNAME,PROID);
        if(key == -1){
                perror("[ERROR] ftok:");
                exit(EXIT_FAILURE);
        }
        msgid = msgget(key,IPC_CREAT | 0666);
        if(msgid == -1){
                perror("[ERROR] msgget():");
                exit(EXIT_FAILURE);
        }
        printf("msg id: %d\n",msgid);
        sleep(10);
        int ret = msgctl(msgid,IPC_RMID,NULL);
        if(ret == -1){
                perror("[ERROR] msgctl()");
                exit(EXIT_FAILURE);
        }
        return 0;


}

发送消息

发送消息队列需要调用 msgsnd 函数
函数头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
函数原型
int msgsnd(int msqid, const void *msgp, size t msgsz, int msgflg);
函数参数:

  • msgid : 消息队列 ID
  • msgp : 消息结构体指针
  • msgsz : 消息内容的长度
  • msgflg : 消息队列标志,默认可以填 0
    • IPC NOWAIT:可以设置非阻塞
      函数返回值:
  • 成功 : 返回 0
  • 失败 : -1,并设置 errno
    消息结构定义形式如下
struct msgbuf {
  long mtype;/* message type, must be > 0 */
  char mtext[1]; /* message data */
}
  • mtype: 消息类型
  • mtext: 消息内容数组

接收消息

  • 接收消息调用 msgrcv 函数
    函数头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    函数原型
    ssize_t msgrcv(int msqid, void *msgp, size t msgsz, long msgtyp, int msgflg);
    函数参数
  • msqid : 消息队列 id
  • msgp : 消息结构指针
  • msgsz: 消息内容的最大长度
  • msgtyp : 消息类型
    • =0: 获取消息队列中的第一条消息
    • >0:返回消息队列中等于mtype 类型的第一条消息
  • msgflg : 消息队列标志,默认可以填
    IPC NOWAIT:可以设置非阻塞
    函数返回值:
  • 成功: 返回实际读取消息内容的字节数
  • 失败:-1并设置 errno

创建两个没有血缘关系的进程,使用 消息队列进行通讯

posted @ 2023-04-03 15:46  shubin  阅读(39)  评论(0编辑  收藏  举报