进程通信之消息队列07

 一、消息队列简介

1.什么是消息队列

        消息队列就是⼀个消息的链表,存放在内核中并由消息队列标识符表示。提供了⼀ 种由⼀个进程向另⼀个进程发送块数据的⽅法。 消息队列是存在于内核中,只有在内核重启(操作系统重启)或者显式的删除消息 队列时,该消息队列才会被真正的删除。 可以使⽤ipcs -q 查看系统消息队列信息,ipcrm -q msgid 移除⽤msgid标识的消息队列

2.消息队列的特点

        ①消息队列可以实现消息的随机查询。消息不⼀定要以先进先出的次序读取,编程时可以按消息的类型读取

        ②消息队列允许⼀个或多个进程向它写⼊或者读取消息。

        ③与⽆名管道、命名管道⼀样,从消息队列中读出消息,消息队列中对应的数据进程通信之消息队列都会被删除

        ④每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯⼀的。

        ⑤消息队列是消息的链表,存放在内存中,由内核维护。只有内核重启或⼈⼯删除消息队列时,该消息队列才会被删除。若不⼈⼯删除消息队列,消息队列会 ⼀直存在于系统中。

        ⑥消息队列的每⼀条消息都有⾃⼰的消息类型,消息类型⽤整数来表示,⽽且必须⼤于0。

 3.消息队列与管道⽐较

        管道:不能读取特定数据,只要管道中有数据,就会全部读取,通信效率低,不适合判频繁交换数据

        消息队列:可以读取相对应的数据,适合频繁的数据交换。

         缺点:

                1.与管道⼀样,每个数据块有有个最⼤度的限制,

                2.系统中所有队列所包含的全部数据块的总数也有个上限。

二、消息队列结构

        操作消息队列时,需要⽤到⼀些数据结构,熟悉这些熟悉结构是操作消息队列的关 键。向消息队列发送消息时,需要⼀定的数据结构,Linux系统定义了⼀个模板数据结构:

#include <sys/msg.h>
struct msgbuf
{
long mtype;
char mtext[1];
};

         其中 mtype代表消息的类型 mtext指定消息内容,虽然mtext指定为字符类型,并不代表消息类型只能是 char类型,消息类型可以是任意类型,可以根据⽤户需要去定义,⽐如:

//⾃定义消息类型1
struct Msg {
 long type;
 char name[20];
 int age;
} msg;
//⾃定义消息类型2
typedef struct {
 char name[20];
 int age;
}Person;
typedef struct {
 long type;
 Person person;
}Msg;

        消息队列中的消息⼤⼩是受到限制的,由的宏MSGMAX和 MSGMNB来限制⼀条消息的最⼤⻓度和⼀个队列的最⼤⻓度。 消息⼤⼩三⼤限制:

cat /proc/sys/kernel/msgmax最⼤消息⻓度限制 
cat /proc/sys/kernel/msgmnb消息队列总的字节数 
cat /proc/sys/kernel/msgmni消息条⽬数

Linux内核为每⼀个消息队列都维护了⼀个msgid_ds结构体,来保存消息队列的状态消息。

 struct msqid_ds {
 struct ipc_perm msg_perm; /* Ownership and permissions */
 time_t msg_stime; /* Time of last msgsnd(2) */
 time_t msg_rtime; /* Time of last msgrcv(2) */
 time_t msg_ctime; /* Time of last change */
 unsigned long __msg_cbytes; /* Current number of bytes in qu
eue (nonstandard) */
 msgqnum_t msg_qnum; /* Current number of messages i
n queue */
 msglen_t msg_qbytes; /* Maximum number of bytes allow
ed in queue */
 pid_t msg_lspid; /* PID of last msgsnd(2) */  //可以获得最后一次得到的进程pid
 pid_t msg_lrpid; /* PID of last msgrcv(2) */
 };

三、消息队列接口函数

 Linux提供了⼀系列消息队列的函数接⼝来实现进程间的通信。

1.功能:创建新的消息队列或获取已有的消息队列。

   格式: int msgget(key_t key, int msgflg);

         key: 关 键 字 值 ⽤ 来 标 识 消 息 队 列 对 象 ( 通 常 是 由 ftok() 返 回 的 , 也 可 以 指 定 为 IPC_PRIVATE),函数将它与已有的消息队列对象的关键字进⾏⽐较来判断消息 队列对象是否已经创建。 其实IPC_PRIVATE表示的key为0,所以这个key和IPC对象。

        msgflg: IPC_CREAT如果内核中没有此队列,则创建它。如果key所命名的消息队列存在 时,IPC_CREAT标志会被忽略,⽽只返回⼀个标识符。 IPC_EXCL当和IPC_CREAT⼀起使⽤时,如果队列已经存在,则失败。 除了以上的两个标志以外,在msgflg 标志中还可以有存取权限控制符。这种控制 符的 意义和⽂件系统中的权限控制符是类似的。 

        返回值: 成功返回⼀个以key命名的消息队列的标识符(⾮零整数),失败时返 回-1.

2.功能:发送消息,将消息添加到消息队列尾部。

   格式: int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

         参数: msqid:是消息队列的引⽤标识符;

         msgp:是指向消息内容所在的缓冲区;

         msgsz:要发送消息的实际数据⻓度, 但不包括消息类型的⻓度(4个字节)

        msgflg:该值为 0:如果消息队列空间不够,msgsnd 会阻塞。 如果为IPC_NOWAIT则直接返回。

        返回值:0 表示成功,-1 失败并设置 errno。

3.功能:读取消息,从消息队列中取⾛消息

   格式: size_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

         参数 msqid:ipc 内核对象 id

        参数 msgp:⽤来接收消息数据地址

        参数 msgsz:消息正⽂部分的⼤⼩(不包含消息类型)

        参数 msgtyp:指定获取哪种类型的消息

                msgtyp = 0:获取消息队列中的第⼀条消息

                msgtyp > 0:获取类型为 msgtyp 的第⼀条消息,除⾮指定了 msgflg 为 MSG_EXCEPT,这表示获取除了 msgtyp 类型以外的第⼀条消息。

                msgtyp < 0:获取类型 ≤|msgtyp|≤|msgtyp| 的第⼀条消息。

                msgflg:0表示忽略,表示进程将被堵塞,直到进程从队列中得到消息为⽌

                IPC_NOWAIT:如果指定类型的消息不存在就⽴即返回,同时设置errno 为ENOMSG 

                错误MSG_EXCEPT:仅⽤于msgtyp>0的情况。表示获取类型不为msgtyp的消 息

                 错误MSG_NOERROR:如果消息数据正⽂内容⼤于msgsz,就将消息数据截断为msgsz

        返回: 成功返回实际接收到的字节数,否则返回-1,错误码存放在errno中。

4.功能:销毁消息队列

   格式:int msgctl(int msqid, int cmd, struct msqid_ds *buf);

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

        cmd: 是将要采取的动作,(有三个可取值) IPC_STAT: 取此队列的msqid_ds结构,并将其存放在buf指向的结构中。

        IPC_SET: 按由buf指向的结构中的值,设置与此队列相关的结构中的下列四个字段:                 msg_perm.uid、 msg_perm.gid、 msg_perm;mode msg_qbytes。

        此命令只能由下列两种进程执⾏:⼀种是其有效⽤户ID等于msg_perm.cuid或msg_perm.uid;另⼀种是 具有超级⽤户特权的进程。只有超级⽤户才能增加msg_qbytes的值。

IPC_RMID : 从系统中删除该消息队列以及仍在该队列上的所有数据。这种删除⽴即⽣效。

四、消息队列使用 

1.消息队列创建过程:

1)创建发送消息端

2)创建信息接收端

//消息队列发送端
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/msg.h>
#include<string.h>

struct node {
	long type;
	char name[20];
}Node;

int main()
{

	//1.创建消息队列
	key_t key = ftok("/bin/ls",3);
	int msqid = msgget(key,IPC_CREAT|0600);
	if(msqid == -1)
	{
		printf("create queue failed\n");
		return -1;
	}

	//2.发送消息
	struct node mydata;
	mydata.type = 1;
	strcpy(mydata.name,"hello");
	int send = msgsnd(msqid,&mydata,6,0);
	if(send == -1)
	{
		printf("send failed\n");
		return -2;
	}

	return 0;
}

 接收端:

//消息队列接收端
//用来接收消息
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/msg.h>
#include<string.h>

struct node {
	long type;
	char name[20];
}Node;

int main()
{
	//strcpy(Node.name,"hello");
	//1.获取消息队列
	key_t key = ftok("/bin/ls",3);
	int msqid = msgget(key,0600);
	if(msqid == -1)
	{
		printf("create queue failed\n");
		return -1;
	}

	//2.获取消息
	struct node mydata;
	int recv = msgrcv(msqid,&mydata,6,1,0); //6代表消息正文的大小  1代表发送端定义的类型  0表示忽略,进程将被阻塞,直到进程从队列中获取消息为止
	if(recv == -1)
	{
		printf("recv failed\n");
		return -2;
	}
	printf("%s----\n",mydata.name);

	//3.销毁消息队列
	msgctl(msqid,IPC_RMID,NULL);

	return 0;
}

运行结果:

建立两个.c程序,先执行发送端,在执行接收端,接收端就会接收到发送端发送来的数据。

 


2.消息队列互相通信:

一端向另一端发送数据,另一端收到后,会向发送数据的那一端提示接收成功。

pro1.c: 

//利用消息队列互相发送消息
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>
 #include <stdio.h>
 #include<unistd.h>
 #include <string.h>

 struct mymsg
 {
   long msgtype;
   char text[50];

 };

int main()
{
 //1.创建消息队列
 key_t key=ftok("/bin/rm",1);
 
 int msgid= msgget(key,IPC_CREAT|0600);
 if(msgid==-1)
 {
   printf("msg create failed\n");
   return -1;

 }

 //2. 消息队列发送和接收
  struct mymsg data;
  data.msgtype=1;  //定义的类型为1
  strcpy(data.text,"hello");
  
  //发送
  int ret=msgsnd(msgid,&data,50,0);
  if(ret==-1)
   {
 
     printf("send failed\n");
     return -2;

   }
  
  memset(&data,0,sizeof(data));  //将结构体前sizeof(int)个字节进行初始化
  ret=msgrcv(msgid,&data,50,2,0);  //接受类型为2的消息队列发送的消息
  printf("send end :%s\n",data.text);

 //3.destroy

return 0;
}

pro2.c:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include<unistd.h>
#include <string.h>

 struct mymsg
 {
   long msgtype;
   char text[50];

 };


int main()
{
 //1.获取消息队列
 key_t key=ftok("/bin/rm",1);
 
 int msgid= msgget(key,0600);
 if(msgid==-1)
 {
   printf("msg create failed\n");
   return -1;
 }

 //2. 接收消息队列
  struct mymsg data;
 
  int ret=msgrcv(msgid,&data,50,1,0);  //从类型1接收数据
  if(ret==-1)
   {
     printf("recv failed\n");
     return -3;
   }

  printf("recv end=%s\n",data.text);
  sprintf(data.text,"%s %s",data.text,"ok");  //字符串拼接函数
  data.msgtype=2;  //这是设定的类型
  msgsnd(msgid,&data,50,0);  //发送消息

 //3.删除消息队列
  msgctl(msgid,IPC_RMID,NULL);

  return 0;
}

运行结果:

 

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值