进程间通信之消息队列
来源:互联网 发布:淘宝网店赚钱吗 编辑:程序博客网 时间:2024/06/07 04:02
概述
进程间的通信,除过管道和命名管道之外,还有一种我认为是比上述两种方式更优秀的通信方式,那就是消息队列。
消息队列的基本概念
消息队列是一个存放在内核中的消息链表,每个消息队列是由消息读列标识符标识。它与之前两种进程间通信方式不同的就是,它是存在于内核之中的,只有在内核重启的时候或是显式的删除一个消息队列的时候,该消息队列才会真正的删除。
要操作消息队列,需要用到一些数据结构,熟悉并掌握这些数据结构对消息队列的理解是很重要的。
消息缓冲结构
向消息队列发送消息时,必须组成合理的数据结构。Linux定义了一个模板数据结构msgbuf。
#include<linux/msg.h>struct msgbuf{ long mtype; char mtext[1];};//mtype表示的是消息类型,给消息指定类型,可以使消息在一个队列之中重复的使用。mtext指消息的内容。
注意: mtext虽然定义为char类型,但并不代表消息内容只能是一个字符,它是任意类型,它可以由用户定义。如:
struct mymsgbuf{ long mtype; struct student stu;};
消息队列中的消息大小是受限制的,由linux/msg.h中的宏MSGMAX给出消息的最大长度。
msqid_ds内核数据结构
Linux之中,每个消息队列都维护一个结构体msqid_ds,此结构体保存着消息队列当前的状态信息,它被定义在linux/msg.h之中。
struct msqid_ds{ struct_ipc_perm msg_perm; struct_msg *msg_first; struct_msg *msg_last; __kernel_t time_t msg_stime; __kernel_t time_t msg_rtime; __kernel_t time_t msg_ctime; unsigned long msg_lcbytes; unsigned long msg_lqbytes; unsigned short msg_cbytes; unsigned short msg_qnum; unsigned short msg_qbytes; __kernel_ipc_pid_t msg_lspid; __kernel_ipc_pid_t msg_lrpid; };
各字段的含义如下:
msg_perm:是一个ipc_perm(定义在头文件linux/ipc.h)的结构,保存了消息队列的存取权限,以及队列的用户ID、组ID等信息
msg_first:指向队列中的第一条消息
msg_last:指向队列中的最后一条消息
msg_stime:向消息队列发送最后一条信息的时间
msg_rtime:从消息队列取最后一条信息的时间
msg_ctime:最后一次变更消息队列的时间
msg_cbytes:消息队列中所有消息占的字节数
msg_qnum:消息队列中消息的数目
msg_qbytes:消息队列的最大字节数
msg_lspid:向消息队列发送最后一条消息的进程ID
msg_lrpid:从消息队列读取最后一条信息的进程ID
ipc_perm内核数据结构
ipc_perm保存着消息队列一些重要的信息 ,包括消息队列所关联的键值,消息队列的用户ID,组ID等等,它定义在linux_ipc.h中。
struct ipc_perm{ __kernel_key_t key; __kernel_uid_t uid; __kernel_gid_t gid; __kernel_uid_t cuid; __kernel_gid_t cgid; __kernel_mode_t mode; unsigned_short seg; };
几个主要字段的含义如下:
key:创建消息队列用到的键值key
uid:消息队列的用户ID
gid:消息队列的组ID
cuid:创建消息队列的进程用户ID
cgid:创建消息队列的进程组ID
消息队列的创建和读写
创建消息队列
获取键值:
#include<sys/types.h>#include<sys/ipc.h>key_t ftok(const char *pathname, int proj_id);//ftok函数主要根据后面两个参数生成键值,执行成功返回键值,失败返回-1。
注意: 参数pathname必须是系统中一定存在并且进程有权访问的。参数proj_id的范围是1~255。
创建队列:
返回的键值提供给函数msgget函数,可以创建一个新的消息队列或访问一个已经存在的消息队列。它被定义在头文件sys/msg.h之中。
int msgget(key_t key, int msgflg);//key是返回的键值,msgflg是一个标志参数。调用成功返回标识符,失败返回-1。
msgflg的取值:
IPC_CREATE:如果内核之中不存在键值与key相等的消息队列,则创建一个,如果存在就返回该消息队列的描述符。
IPC_EXCL:和上述取值一起使用,对应键值的key值已经存在,出错,返回-1。单独使用没有意义。
消息队列的读写
写消息队列
#include<sys/msg.h>int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int magflg)//函数调用成功返回0,失败返回-1。
其中各个参数的含义如下:
- msqid:函数向msqid标志的消息队列发送一个消息
- msgp:msgp指向发送的消息
- msgsz:要发送消息的大小,不包括消息类型所占的4个字节
- magflg:操作标志位。可以设置为0或IPC_NOWAIT。如果设置为0,则当消息队列满的时候,msgsnd会进行阻塞,知道消息可以写进消息队列。如果操作符设置为IPC_NOWAIT则当消息队列已经满的时》候,msgsnd不会进行阻塞而直接返回
常见的错误码:
- EAGAIN:消息队列已满
- EIDRM:消息队列已经被删除
- EACCESS:无权访问消息队列
读消息队列
#include<sys/msg.h>int msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long int msgtyp, int msgflg)
各参数含义如下:
- msqid:消息队列描述符
- msgp:读取消息队列的消息存入msgp中
- msgsz:消息缓冲区的大小
- msgtyp:请求读取的消息类型
- msgflg:操作标志位
msgflg的取值:
- IPC_NOWAIT:没有满足条件的消息则立即返回
- IPC_EXCEPT:与msgtyp配合使用,返回队列中第一个类型不为msgtyp的消息
- IPC_NOERROR:如果满足条件的消息内容大于所请求的msgsz字节,则把该消息截断
常见的错误码:
- E2BIG:消息长度大于msgsz
- EIDRM:消息队列已经被删除
- EINVAL:说明msqid无效或msgsz小于0
获取和设置消息队列的属性
msgctl函数可以获取或设置消息队列的属性。
#include<sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf)
该函数对msqid标识的消息队列执行了cmd操作,系统定义了3中cmd操作。
cmd的三种取值:
- IPC_STAT:该命令用来获取消息队列对应的msqid_ds数据结构,并将其保存在buf所指向的地址空间中
- IPC_SET:该命令用来设置消息队列的属性,要设置的属性包括在buf之中,可设置的属性包括msg_perm.uid,msg_perm.gid,msg_perm.mode,msg_qbytes
- IPC_RMID:从内核之中删除msqid标识的消息队列
消息队列的应用实例
下面我将给出基于消息队列的进程间通信的程序,对上面的几个知识点进行总结。
Server:
#include<stdio.h>#include<fcntl.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<sys/stat.h>#define BUF_SIZE 256#define PROJ_ID 32#define PATH_NAME "/tmp"#define SERVER_MSG 1#define CLIENT_MSG 2struct mymsgbuf{ long msgtype; char ctrlstring[BUF_SIZE];}msgbuffer;int main(){ int qid; int msglen; key_t msgkey; if((msgkey = ftok(PATH_NAME, PROJ_ID)) == -1) //先获取键值 { perror("ftok error!\n"); exit(1); } if((qid = msgget(msgkey, IPC_CREAT | 0660)) == -1) //获取消息队列标识符 { perror("msgget error!\n"); exit(1); } while(1) { printf("server: "); fgets(msgbuffer.ctrlstring, BUF_SIZE, stdin); //从键盘获取服务端的输入 if(strncmp("exit", msgbuffer.ctrlstring, 4) == 0) { msgctl(qid, IPC_RMID, NULL); break; } msgbuffer.ctrlstring[strlen(msgbuffer.ctrlstring) -1] = '\0'; msgbuffer.msgtype = SERVER_MSG; if(msgsnd(qid, &msgbuffer, strlen(msgbuffer.ctrlstring) +1, 0) == -1) //发送消息 { perror("Server msgsnd error!\n"); exit(1); } if(msgrcv(qid, &msgbuffer, BUF_SIZE, CLIENT_MSG, 0) == -1) //接受客户端的消息 { perror("Client msgsnd error!\n"); exit(1); } printf("Client: %s\n", msgbuffer.ctrlstring); } return 0;}
Client:
#include<stdio.h>#include<fcntl.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/msg.h>#include<sys/stat.h>#define BUF_SIZE 256#define PROJ_ID 32#define PATH_NAME "/tmp"#define SERVER_MSG 1#define CLIENT_MSG 2struct mymsgbuf{ long msgtype; char ctrlstring[BUF_SIZE];}msgbuffer;int main(){ int qid; int msglen; key_t msgkey; if(msgkey = ftok(PATH_NAME, PROJ_ID) == -1) //获取键值 { perror("msgget error!\n"); exit(1); } if(qid = msgget(msgkey, IPC_CREAT | 0660) == -1) //获取消息队列标识符 { perror("msgget error!\n"); exit(1); } while(1) { if(msgrcv(qid, &msgbuffer, BUF_SIZE, SERVER_MSG, 0) == -1) { perror("Server msgrcv error!\n"); exit(1); } printf("server: %s\n", msgbuffer.ctrlstring); printf("Client: "); fgets(msgbuffer.ctrlstring, BUF_SIZE, stdin); if(strncmp("exit", msgbuffer.ctrlstring, 4) == 0) { break; } msgbuffer.ctrlstring[strlen(msgbuffer.ctrlstring) -1] = '\0'; msgbuffer.msgtype = CLIENT_MSG; if(msgsnd(qid, &msgbuffer, strlen(msgbuffer.ctrlstring) +1, 0) == -1) { perror("client msgsnd error!\n"); exit(1); } } return 0;}
运行结果:
- 进程间通信之消息队列通信
- linux进程间通信之消息队列
- 进程间通信之消息队列篇
- linux进程间通信之消息队列
- 进程间通信之消息队列
- linux进程间通信之消息队列
- Linux进程间通信之消息队列
- 进程间通信之消息队列
- 进程间通信之消息队列
- 进程间的通信之消息队列
- 进程间通信之消息队列
- linux 进程间通信之消息队列
- 进程间通信之消息队列
- linux进程间通信之消息队列
- linux进程间通信之消息队列
- 进程间通信之消息队列
- 进程间通信之消息队列
- 进程间通信之消息队列
- UnityGUI系统之锚点与中心点
- Hurst Parameter Estimators
- Java EE知识结构
- 浙大 PAT b1038
- 浙大 PAT b1039
- 进程间通信之消息队列
- Java_io体系之BufferedWriter、BufferedReader简介
- lua的coroutine
- 浙大 PAT b1040
- Struts2配置时报struts-default.xml:65:72异常的解决方法
- Android Studio :如何导入Eclipse项目与AndroidStudio项目?
- 浙大 PAT a1001
- 第二十四期 OpenWrt ipk helloworld 《路由器就是开发板》
- UnityGUI系统之容器、下拉列表与自动布局