进程间通信 消息队列

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;

}

创建两个子进程A与B,父进程分别给两个子进程发型消息,消息类型为 100 与 200%E2%80%8B%0D%0A•%0D%0A父进程从键盘循环接收数据,发送给子进程,输入 quit 则退出

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.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);
        pid_t cpid1,cpid2;
        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);
        cpid1 = fork();
        if(cpid1 > 0){
                cpid2 = fork();
        }
        if(cpid1 == 0){
                fprintf(stdout,"Child <%d> start!\n",getpid());
                while(1){
                        ret = msgrcv(msgid,(void *)&buf,MSG_SZ,100,0);
                        printf("Child1 <%d>, type 100, mtext %s\n",getpid(),buf.mtext);
                }
        }
        else if(cpid2 == 0){
                fprintf(stdout,"Child <%d> start!\n",getpid());
                while(1){
                        ret = msgrcv(msgid,(void *)&buf,MSG_SZ,200,0);
                        printf("Child2 <%d>, type 200, mtext %s\n",getpid(),buf.mtext);
                }
        }
        else if(cpid1 > 0 && cpid2 > 0){
                int threadnum;
                char msg[MSG_SZ];
                scanf("%d %s",&threadnum,msg);
                while(strcmp(msg,"quit") != 0){
                        buf.mtype = MSG_TYPE;
                        strcpy(buf.mtext,msg);
                        buf.mtype = threadnum;
                        //printf("%d %s",threadnum,msg);
                        ret = msgsnd(msgid,(const void *)&buf,strlen(buf.mtext)+1,0);
                        if(ret == -1){
                                perror("msgsnd():");
                                exit(EXIT_FAILURE);
                        }
                        printf("%d\n",ret);
                        scanf("%d %s",&threadnum,msg);
                }
                kill(SIGKILL,cpid1);
                kill(SIGKILL,cpid2);
        }
        return 0;

}

posted @ 2023-04-03 17:29  shubin  阅读(62)  评论(0编辑  收藏  举报