进程间通信IPC 主题一 之 消息队列

来源:互联网 发布:墨子号成功发射知乎 编辑:程序博客网 时间:2024/05/24 15:41

这是我们第一接触IPC主题系列先来看看
IPC简介:

XSI IPC 包括消息队列,信号量以及共享内存,他们都依托标识符和键来实现的,这就是像是管道靠文件描述符来实现一样。今天我们只了解消息队列。

IPC主题一:消息队列


一:什么是消息队列

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道有一样的不足,就是每个消息队列的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。

我们用代码验证下消息队列的最大容量:

这里写图片描述


二:IPC对象数据结构

内核为每一个IPC对象维护了一个数据结构(/user/include/linux/ipc.h)

struct kern_ipc_perm{  //内核中记录消息队列的全局数据结构msg_ids能够访问到该结构;  key_t  key;    //该键值则唯一对应一个消息队列  uid_t  uid;  gid_t  gid;  uid_t  cuid;  gid_t  cgid;  mode_t  mode;  unsigned long seq;  } 

消息队列,信号量和共享内存都有这样一个共同的数据结构;


三:消息队列结构

这里写图片描述

可以看到第一个条目就是IPC结构体,即使共有的,后面的都是消息队列的私有成员。

消息队列的特点:
(1) 存放的是有类型的数据块,并不像管道的那样针对字节流。
(2)生命周期随内核。
(3)所存在消息可以随机访问,不一定是先进先出。
(4)他是用链表实现的,所以可以实现一个或多个进程同时对读取或写入。


消息队列的创建

使用到的函数:

函数: key_t ftok(const char *filename, int proj_id);通过文件名和项目号获得System V IPC键值(用于创建消息队列、共享内存所用)proj_id:项目号,不为0即可返回:成功则返回键值,失败则返回-1----------函数: int msgget(key_t key, int msgflg);key:键值,当为IPC_PRIVATE时新建一块共享内存;msgflg:标志。IPC_CREAT:内存不存在则新建,否则打开;IPC_EXCL:只有在内存不存在时才创建,否则出错。返回:成功则返回标识符,出错返回-1----------函数: int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);向消息队列发送消息msgid:消息队列标识码,通过msgget获取msgp:指向一段暂时存储消息内容的指针  形态如下:struct msgstru{long mytype; //大于0char mtext[用户指定大小];}msgsz:消息内容的大小msgflg:处理方式;如为IPC_NOWAIT时表示空间不足时不会阻塞返回:成功则返回0,失败返回-1 ;当msgflg为0 时,msgsnd() 和 msgrcv() 都采用阻塞式等待。----------函数: int msgrcv(int msgid, void *msgp, size_t msgsz, long msgtype, int msgflg);从消息队列读取消息msgid:通过msgget获取msgp:指向一段暂时存储消息内容的指针  形态如下:struct msgstru{long mytype; //大于0char mtext[用户指定大小];}msgsz:消息内容的大小msgtype:指定接收的消息类型;若为0则队列第一条消息将被读取,而不管类型;若大于0则队列中同类型的消息将被读取,如在flg中设了MSG_RXCEPT位将读取指定类型的其他消息;若小于0读取绝对值小于type的消息。msgflg:处理方式;返回:成功返回收到消息长度,错误返回-1----------//设置消息队列属性函数: int msgctl(int msgid, int cmd, struct msgid_ds *buf);msgid:通过msgget获取cmd:控制命令,如下:IPC_STAT:获取消息队列状态(获取对应的msgid_ds),并保存在buf中指定的空间。IPC_SET:改变消息队列状态(设置属性存在buf中)IPC_RMID:删除(msgid标识的)消息队列buf:结构体指针,用于存放消息队列状态返回:成功返回与cmd相关的正数,错误返回-1

指令
ipcs -q 查看存在的消息队列

ipcrm -q +msgid 删除该消息队列

接下来我们用代码实现:

common.h

  1 #ifndef _COMMON_H_  2 #define _COMMON_H_  3 #include <sys/types.h>  4 #include <sys/ipc.h>  5 #include <sys/wait.h>  6 #include <sys/msg.h>  7 #include <stdio.h>  8 #include <sys/stat.h>  9 #include <string.h> 10 #include <stdlib.h> 11 #define PATHNAME "." 12 #define PROJ_ID 0X6666 13 #define SERVER_TYPE 1 14 #define CLIENT_TYPE 2 15 #define SIZE 128 16 struct msgbuf 17 { 18     long mtype; 19     char mtext[SIZE]; 20 }; 21  22 int creatMsgQueue(); 23 int getMsgQueue(); 24 int sendMsg(int msgid, int type, const char* _info); 25 int recvMsg(int msgid, int type, char *out); 26 int destoryMsgQueue(int msgid); 27  28 #endif

common.c

  1  #include "common.h"  2  3  int commMsgQueue(int flags)  4 {  5     key_t _k = ftok(PATHNAME, PROJ_ID);  6     if(_k < 0)  7     {  8         perror("ftok");  9         return 1; 10     } 11     int msg_id = msgget(_k, flags); 12     if(msg_id < 0) 13     { 14         perror("msg"); 15         return 2; 16     } 17     return msg_id; 18 } 19  20 int creatMsgQueue() 21 { 22     return commMsgQueue(IPC_CREAT | IPC_EXCL | 0666); 23 } 24  25 int getMsgQueue() 26 { 27     return commMsgQueue(IPC_CREAT); 28 } 29  30 int destoryMsgQueue(int msgid) 31 { 32     if(msgctl(msgid, IPC_RMID, NULL) < 0) 33     { 34         perror("msgctl"); 35         return 1; 36     } 37     return 0; 38 } 39  40 int sendMsg(int msgid, int type, const char* _info) 41 {  1 #include "common.h"  2  int commMsgQueue(int flags)  3 {  4     key_t _k = ftok(PATHNAME, PROJ_ID);  5     if(_k < 0)   6     {  7         perror("ftok");  8         return 1;  9     }    10     int msg_id = msgget(_k, flags); 11     if(msg_id < 0) 12     { 13         perror("msg"); 14         return 2; 15     }    16     return msg_id; 17 }    18  19 int creatMsgQueue() 20 { 21     return commMsgQueue(IPC_CREAT | IPC_EXCL | 0666); 22 } 23  24 int getMsgQueue() 25 { 26     return commMsgQueue(IPC_CREAT); 27 } 28  29 int destoryMsgQueue(int msgid) 30 { 31     if(msgctl(msgid, IPC_RMID, NULL) < 0) 32     { 33         perror("msgctl"); 34         return 1; 35     } 36     return 0; 37 }

server.c

  1 #include "common.h"  2   3 int main()  4 {  5     char buf[SIZE];  6     int msgid = creatMsgQueue();  7     while(1)  8     {  9         if(recvMsg(msgid, CLIENT_TYPE, buf) < 0) 10         { 11             break; 12         } 13         printf("client say: %s\n", buf); 14         printf("please enter:"); 15  16         fflush(stdout); 17         ssize_t s = read(0, buf, sizeof(buf) - 1); 18         if(s > 0) 19         { 20             buf[s - 1] = 0; 21            if(sendMsg(msgid, SERVER_TYPE, buf) < 0) 22            { 23                break; 24            } 25         } 26     } 27     destoryMsgQueue(msgid); 28     return 0; 29 }

client.c

  1 #include "common.h"  2 int main()  3 {  4     char buf[SIZE];  5     int msgid = getMsgQueue();  6     while(1)  7     {  8        printf("please enter$: ");  9        fflush(stdout); 10  11        ssize_t s = read(0, buf, sizeof(buf) - 1); 12        if(s > 0) 13        { 14            buf[s - 1] = 0; 15           if( sendMsg(msgid, CLIENT_TYPE, buf) < 0) 16           { 17               break; 18           } 19        } 20       if(recvMsg(msgid, SERVER_TYPE, buf) < 0) 21       { 22           break; 23       } 24        printf("server say: %s\n", buf); 25     } 26     return 0; 27 }

makefile:
这里写图片描述
下边是运行结果:
这里写图片描述

这样我们就通过消息队列实现了任意两个进程之间的双向通信!

原创粉丝点击