进程间通信 消息队列

发布时间 2023-04-03 17:32:19作者: shubin

System V IP

  • IPC : Inter-Process Communication (进程间通讯)
  • System V 是早期的unix 系统,曾经被称为 AT & T System ,是 unix 操作系统中比较重要的一个分支,现在Linux 系统一般都支持 System V IPC
  • System V IPC 对象共有三种
    • 消息队列
    • 共享内存
    • 信号量
  • System V IPC 是由内核维护的若千个对象,通过 ipcs 命名查询
  • 每个 IPC 对象都有一个唯一的 ID,可以通过 ftok() 函数生成,fork 函数具体说明如下:
    函数头文件
    #include <sys/types.h>
    #include <sys/ipc.h>
    函数原型
    key_t ftok(const char *pathname, int proj_id);
    函数参数
  • pathname : 文件路径名
  • proj_id : 8 it的id 整数

函数返回值

  • 成功: 返回合成的 key
  • 失败:-1,并设置 errno

注意:

  • key 由文件的 inode 节点号 与 proi 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

创建一个消息队列,并打印消息队列 ID

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.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);
        return 0;


}

通过 ipcs -q 命名查看后:

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

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

  • read.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>

#define PATHNAME "."
#define PRO_ID 10

#define MSG_TYPE 100
#define MSG_SZ 64
struct msgbuf {
        long mtype;
        char mtext[MSG_SZ];
};
int main(void){
        int ret,msgid;
        key_t key;
        ssize_t rbytes;
        struct msgbuf buf;

        key = ftok(PATHNAME,PRO_ID);
        if(key == -1){
                perror("[ERROR] ftok()");
                exit(EXIT_FAILURE);
        }
        msgid = msgget(key,IPC_CREAT | 0666);
        if(msgid == -1){
                perror("[ERROR] msgget");
                exit(EXIT_FAILURE);
        }
        fprintf(stdout,"%d\n",msgid);
        rbytes = msgrcv(msgid, (void *)&buf,MSG_SZ,MSG_TYPE,0);
        if(rbytes == -1){
                perror("[ERROR] msgrcv():");
                exit(EXIT_FAILURE);
        }
        fprintf(stdout,"mtype:%ld\n",buf.mtype);
        fprintf(stdout,"mtext:%s\n",buf.mtext);
        ret = msgctl(msgid,IPC_RMID,NULL);
        if(ret == -1){
                perror("msgctl():");
                exit(EXIT_FAILURE);
        }
        return 0;

}
  • write.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PATHNAME "."
#define PRO_ID 10

#define MSG_TYPE 100
#define MSG_SZ 64

struct msgbuf {
        long mtype;
        char mtext[MSG_SZ];
};
int main(void){
        key_t key;
        int msgid,ret;
        struct msgbuf buf;
        key = ftok(PATHNAME,PRO_ID);
        if(key == -1){
                perror("ftok()");
                exit(EXIT_FAILURE);
        }
        msgid = msgget(key,IPC_CREAT | 0644);
        if(ret == -1){
                perror("msgget():");
                exit(EXIT_FAILURE);
        }
        fprintf(stdout,"msgid:%d\n",msgid);
        buf.mtype = MSG_TYPE;
        strcpy(buf.mtext,"Hello msg");
        ret = msgsnd(msgid,(const void *)&buf,strlen(buf.mtext)+1,0);
        if(ret == -1){
                perror("msgsnd():");
                exit(EXIT_FAILURE);
        }
        return 0;

}