【Linux】进程间通信之消息队列篇

上篇我们讲了管道,那么今天我们着重来看看消息队列

消息队列是由消息的链表,存放在内核中并由消息队列标识符标识,消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限制等缺点

那么什么是消息队列呢?消息队列有哪些特点呢?

消息队列提供了一个由一个进程向另外一个进程发送一块数据的方法

每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值

消息队列也有管道一样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的中的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)

消息队列不同于管道它的的生命周期随内核

我们先来了解一下IPC进程间通信的对象数据结构/usr/include/linux/ipc.h

内核为每个IPC对象维护一个数据结构

struct ipc_perm {
key_t __key; /* Key supplied to xxxget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};

msgget函数:

功能:用来创建和访问一个新的或者已经存在的消息队列,此消息队列与key相对应

#include<sys/msg.h>//头文件
int msgget(key_t key,int msgflg);//原型

参数:key表示某个消息队列的名字,可以是人为指定的,也可以是通过ftok()函数获得

】ftok函数:获取key值

#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char* pathname,int id)//pathname:路径名  id:项目id,非0整数(只有低8位有效)
ftok的返回值,成功返回key值,失败返回-1;

msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的

其中,IPC_CREAT表示内核中没有则创建之,msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符

IPC_EXCL和IPC_CREAT一起使用时表示若队列存在,则失败;则msgget()要么创建一个新的消息队列,要么消息队列已经存在返回一个失败值-1;

IPC_EXCL单独使用没有意义

返回值:成功返回一个非负整数,即表示该消息队列的标识码;失败则返回-1

实例: 

mymsgget.c

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int main()
{
    key_t key;
    key = ftok("xiaomaomi", 1);//获取key值
    int msgqid = msgget(key, IPC_CREAT | 0644);//创建消息队列
    return 0;
}

我们通过ipcs/ipcrm命令查看一下我们消息队列是否创建完成


msgctl函数

功能:消息队列的控制函数

#include<sys/msg.h>
int magctl(int msqid,int cmd,struct msqid_ds *buf_)//msgctl系统调用对msqid标识的消息队列执行cmd操作
//msqid:由msgget函数返回的消息队列标识码
//cmd:是将要采取的动作(有三个取值)
//返回值:成功返回0,失败返回-1
我们来了解一下队列的msqid_ds结构(对于每一个队列都有一个msqid_ds来描述队列当前的状态)

】消息队列结构体定义如下/usr/include/linux/msg.h

struct msqid_ds {//Linux系统中的定义
struct ipc_perm msg_perm; /* Ownership and permissions
time_t msg_stime; /* Time of last msgsnd() */
time_t msg_rtime; /* Time of last msgrcv() */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes inqueue (non-standard) */
msgqnum_t msg_qnum; /* Current number of messagesin queue */
msglen_t msg_qbytes; /* Maximum number of bytesallowed in queue */
pid_t msg_lspid; /* PID of last msgsnd() */
pid_t msg_lrpid; /* PID of last msgrcv() */
};//不同的系统中此结构会有不同的新成员

cmd将要采取的三个动作,分别如下:

1.IPC_STAT:把msqid_ds结构中的数据设置为消息队列的当前关联值

2.IPC_SET:在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值

3.IPC_RMID:删除消息队列

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);

参数

msqid:由msgget函数返回的消息队列标识码

msgp:指向准备发送的消息的指针

msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型

msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情

msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误

msgflg=0表示调用阻塞直到条件满足为止

返回值:

成功返回0,失败返回-1

1.消息结构在两⽅方⾯面受到制约:

(1)它必须⼩小于系统规定的上限值;

(2)它必须以⼀一个long int⻓长整数开始,接收者函数将利⽤用这个⻓长整数确定消息的类型

2.消息结构参考形式如下:

struct msgbuf {
   long mtype;//消息类型
   char mtext[1];//消息正文
}
msgrcv函数

功能:从一个消息队列中接受消息

#include<sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

参数:

msqid:由msgget函数返回的消息队列标识码

msgp:一个指向准备接受的消息的指针

msgsz:msgp指向的消息长度

msgtype:他可以实现接收优先级的简单形式

msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事,其取值如下

0:msgrcv() 调用阻塞直到接收消息成功为止。

MSG_NOERROR: 若返回的消息字节数比 nbytes 字节数多,则消息就会截短到 nbytes 字节,且不通知消息发送进程。

IPC_NOWAIT: 调用进程会立即返回。若没有收到消息则立即返回 -1。

返回值

     成功:返回实际放到接收缓冲区的字符个数

     失败:返回-1

说明

msgtype=0返回队列第一条信息
msgtype>0返回队列第⼀条类型等于msgtype的消息
msgtype<0返回队列第⼀条类型小于等于msgtype绝对值的消息,并且是满⾜足条件的消息类型最小的消息
msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断

msgtype>0且msgflg=MSG_EXCEPT,接收类型不等于msgtype的第⼀条消息。

用消息队列相关函数实现client和server两个进程之间的通信

【代码在线】

Makefile

 .PHONY:all
  all:client server
 client:client.c comm.c
      gcc -o $@ $^                                                
  server:server.c comm.c
     gcc -o $@ $^
 .PHONY:clean
 clean:
     rm -f client server

comm.h

  1 #ifndef _COMM_H_
  2 #define _COMM_H_
  3 #include<stdio.h>
  4 #include<sys/types.h>
  5 #include<sys/msg.h>
  6 #include<string.h>
  7 #define PATHNAME "."
  8 #define PROJ_ID 0x6666
  9 #define SERVER_TYPE 1
 10 #define CLIENT_TYPE 2
 11 struct msgbuf{
 12     long mtype;
 13     char mtext[1024];
 14 };
 15 
 16 int createmsg();
 17 int getmsg();
 18 int destroymsg(int msgid);
 19 int sendmsg(int msgid,int who,char* msg);
 20 int receivemsg(int msgid,int recvType,char out[]);
 21 
 22 #endif // _COMM_H_:                          

comm.c

 1 #include"comm.h"                                                
  2 
  3 //success > 0 failed ==-1
  4 static int commmsg(int flag)
  5 {
  6     key_t key=ftok(PATHNAME,PROJ_ID);
  7     if(key<0){
  8         printf("ftok error\n");
  9         return -1;
 10     }
 11     //int msgid=msgget(key,IPC_CREAT|IPC_EXCL);
 12     int msgid=msgget(key,flag);
 13     if(msgid<0){
 14         printf("msgget error\n");
 15         return -1;
 16     }
 17     return msgid;
 18 }
 19 int createmsg()
 20 {
 21     return commmsg(IPC_CREAT|IPC_EXCL|0666);
 22 }
 23 int getmsg()
 24 {
 25     return commmsg(IPC_CREAT);
 26 }
 27 int destroymsg(int msgid)
 28 {
 29     if(msgctl(msgid,IPC_RMID,NULL)<0){
 30         printf("msgctl error\n");
 31         return -1;
 32     }
 33     return 0;
 34 }
 35 int sendmsg(int msgid,int who,char* msg)
 36 {
 37     struct msgbuf buf;
 38     buf.mtype=who;
 39     strcpy(buf.mtext,msg);
 40     if(msgsnd(msgid,(void *)&buf,sizeof(buf.mtext),0)<0){
 41         printf("msgsnd error\n");
 42         return -1;
 43     }
 44     return 0;
 45 }
 46 int receivemsg(int msgid,int recvType,char out[])
 47 {
 48     struct msgbuf buf;
49     if(msgrcv(msgid,(void *)&buf,sizeof(buf.mtext),recvType,0)<0    ){
 50         printf("msgrcv error\n");
 51         return -1;
 52     }
 53     strcpy(out,buf.mtext);
 54     return 0;
 55 }
 56           

server.c

  1 #include"comm.h"
  2 int main()
  3 {
  4     int msgid=createmsg();                                     
  5     char buf[1024];
  6     while(1)
  7     {
  8         buf[0]=0;
  9         receivemsg(msgid,CLIENT_TYPE,buf);
 10         printf("client#####%s\n",buf);
 11         printf("Please Enter #####");
 12         fflush(stdout);
 13         ssize_t s=read(0,buf,sizeof(buf));
 14         if(s>0){
 15             buf[s-1]=0;
 16             sendmsg(msgid,SERVER_TYPE,buf);
 17             printf("send done,wait receive ....\n");
 18         }
 19     }
 20     destroymsg(msgid);
 21     return 0;
 22 }

client.c

  1 #include"comm.h"
  2 int main()
  3 {
  4     int msgid = getmsg();
  5     char buf[1024];
  6     while(1){
  7         buf[0]=0;
  8         printf("please enter# ");
  9         fflush(stdout);
 10         ssize_t s=read(0,buf,sizeof(buf));
 11         if(s>0){
 12             buf[s-1]=0;
 13             sendmsg(msgid,CLIENT_TYPE,buf);
 14             printf("send done ,wait receive....\n");
 15         }
 16         receivemsg(msgid,SERVER_TYPE,buf);
 17         printf("server#####%s\n",buf);
 18     }
 19     return 0;                                                  
 20 }
实现结果                    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值