进程间通信之消息队列

           在简单了解了进程间通信之后,我们知道进程间通信的方式不止有管道,还有其他的system V IPC资源。

其中就有消息队列。

      消息队列是什么?

      

      消息队列:提供一个进程像另一个进程发送有类型数据块的方法。消息队列的声明周期岁内核,其意思就是

就算进程结束,由操作系统所创建的队列也不会结束,它需要我们手动删除。

      当然消息队列也有他的缺点:消息队列与命名管道有一样的不足,就是每个消息的最大长度是有上限的

(MSGMAX),每个消息队列的总的字节数(MSGMNB)、系统上消息队列的总数也都有上限(MSGMNI)

     由操作系统所提供的消息队列也不会只有一条,那我们是如何来保证两个进程通过同一个消息队列来进行通信的呢?

这就要提出一个词Key,Key是消息队列的标号,当两个进程看到标号相同的消息队列时,才能够进行通信。

一、接下来我们就看看消息队列的有关结构和函数。

   1、IPC对象数据结构:(可以敲命令 cat /usr/include/linux/ipc.h进行查看)

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

     

    结构体中的第一个变量Key就代表的是消息队列的标号,那么Key是怎么得出的呢?

    这里我们就需要提一个函数:

     Key_t ftok(const char *pathname,int proj_id);

    第一个参数为获取路径,第二个参数为工程号。

   2、 消息队列结构:(可以敲命令 cat /usr/include/linux/msg.h进行查看)

     

   3、消息队列在内核中的表示(以图表的形式说明)

    

     4、 消息队列相关函数

     (1)创建和访问一个消息队列

      int msgget(key_t key, int msgflg);

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

     参数:Key即为消息队列的标识符。由fork函数得出;

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

   我们比较常用的就是IPC_CREAT、IPC_EXCL (IPC_CREAT单独使用时,不存在就创建,存在就返回。和IPC_EXCL同时使用时,

消息队列不存在就创建,存在就出错返回)

      (2)消息队列的控制函数

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

      返回值:成功返回0;失败返回 - 1

     (3)发送消息到队列中的函数

       返回值:成功返回0;失败返回 - 1

      (4)从消息队列中取消息的函数

       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

   5、制约

     (1)消息结构在两方面受到制约:  

          首先,它必须小于系统规定的上限值;

          其次,它必须以一个long int长整型开始,接收者函数将利用这个长整数确定消息的类型。

     (2)消息结构参考形式输入下:   

struct msgstru
{     
    long mtype; //大于0 
    char mtext[1];//用户可以自己指定大小
};  

二、消息队列的实现

   comm.c 

#include "comm.h"

static int commMsg(int flags)
{
	key_t _key = ftok(PATHNAME, PROJ_ID);
	if (_key<0)
	{
		perror("ftok");
		return -1;
	 }
	int msgid = msgget(_key, flags);
	if (msgid < 0)
	{
		perror("msgget");
	}
	return msgid;

}

int creatMsg()
{
    return commMsg(IPC_CREAT|IPC_EXCL|0666);
}
int getMsg()
{
    return commMsg(IPC_CREAT);
}
int destoryMsg(int msgid )
{
    if(msgctl(msgid,IPC_RMID,NULL)<0)
    {
        perror("msgctl");
        return -1;
    }
    return 0;
}
int sendMsg(int msgid,int who,char *msg)
{
    struct msgbuf buf;//用户自己定义
    buf.mtype=who;
    strcpy(buf.mtext,msg);

    if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)
    {
        perror("msgsnd");
        return -1;
    }
    return 0;
}
int recvMsg(int msgid,int recvType,char out[])
{
    struct msgbuf buf;
    int p=msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0);
    if(p<0)
    {
        perror("msgrcv");
        return -1;
    }
    strncpy(out,buf.mtext,p);
    return 0;
}

   comm.h

#ifndef _COMM_H_
#define _COMM_H_

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <string.h>
#define PATHNAME "."
#define PROJ_ID 0x6656

#define SERVER_TYPE 1
#define CLIENT_TYPE 2

struct msgbuf
{
    long mtype;
    char mtext[1024];
};

int creatMsg();//创建消息队列
int getMsg();//获得消息队列
int destoryMsg(int msgid );//销毁消息队列
int sendMsg(int msgid,int who,char* msg);//发送消息
int recvMsg(int msgid,int recvType,char out[]);//接收消息
#endif

   server.c

#include "comm.h"

int main()
{
    int msgid=creatMsg();//server创建消息队列
    char buf[1024];
    while(1)
    {
        buf[0]=0;
        recvMsg(msgid,CLIENT_TYPE,buf);//读取消息队列中的消息
        printf("client say :%s\n",buf);

        printf("Please Enter:");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf));
        if(s>0)
        {
            buf[s-1]=0;
            sendMsg(msgid,SERVER_TYPE,buf);//发送消息
            printf("waiting \n ");
         }
	}
    destoryMsg(msgid);
    return 0;
    
}

   client.c

#include "comm.h"

int main()
{
    int msgid=getMsg();
    char buf[1024];
    while(1)
    {
        buf[0]=0;
        printf("Please Enter:");
        fflush(stdout);
        ssize_t s = read(0,buf,sizeof(buf));
       
        if(s>0)
        {
            buf[s-1]=0;
            sendMsg(msgid,CLIENT_TYPE,buf);
            printf("waiting \n");
        }
        recvMsg(msgid,SERVER_TYPE,buf);
        printf("server say:%s\n",buf);
    }
    return 0;
}

   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

   在消息队列中有一对特别重要的命令,

        ipcs 显示IPC资源     我们要查看当前系统中的消息队列就可以加上 -q 选项
        ipcrm手动删除IPC资源 
            

    我们可以看到,在没运行server之前,系统中没有消息队列的存在。接下来我们就运行server

 

    可以看到,系统中以及存在一个编号为131072 的消息队列,然后运行client我们就可以进行通信了。

 

   在通信结束之后,我们还需要使用上面我们所讲到的命令ipcrm -q 加队列ID进行删除消息队列,

因为我们知道,消息队列由操作系统创建,生命周期随内核,不会自动销毁。 

   

   好了,通过消息队列进行进程间通信就完成了。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值