进程间通信(2) 消息队列

昨天我们看了进程间通信的一种方式-----管道,那么今天我们继续学习进程间通信的另一种方式----消息队列。

消息队列

消息队列是由内核维护的一种链式结构。链表中每一个记录又称作消息,消息具有特定的格式和优先级别。

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

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

    3、消息队列也有管道⼀样的不足,就是每个消息的最⼤长度是有上限的(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 */
};

消息队列结构  可以在/usr/include/linux/msg.h打开

struct msqid_ds {
    struct ipc_perm msg_perm;
    struct msg *msg_first; /* first message on queue,unused */
    struct msg *msg_last; /* last message in queue,unused */
    __kernel_time_t msg_stime; /* last msgsnd time */
    __kernel_time_t msg_rtime; /* last msgrcv time */
    __kernel_time_t msg_ctime; /* last change time */
    unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */
    unsigned long msg_lqbytes; /* ditto */
    unsigned short msg_cbytes; /* current number of bytes on queue */
    unsigned short msg_qnum; /* number of messages in queue */
    unsigned short msg_qbytes; /* max number of bytes on queue */
    __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
    __kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};

消息队列在内核中表示:


消息队列函数

msgget函数

功能:⽤用来创建和访问⼀一个消息队列
原型

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>

 int msgget(key_t key, int msgflg);
参数
    key: 某个消息队列的名字

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

    参数msgflg是一些标志位,常用的有IPC_CREAT、IPC_EXCL、IPC_NOWAIT三个取值

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

msgctl函数

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

原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
//成功返回0,失败返回-11

参数:

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

        IPC_STAT:该命令用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指定的地址空间。

        IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf中。 

        IPC_RMID:从内核中删除 msqid 标识的消息队列。 

    cmd:是将要采取的动作(有三个可取值)

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

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);
//成功返回0,失败返回-1
参数
    msgid: 由msgget函数返回的消息队列标识码
    msgp:是一个指针,指针指向准备发送的消息,
    msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
    msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情

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

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

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

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

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

2.消息结构参考形式如下:
struct msgbuf {
    long mtype;
    char mtext[1];
}

msgrcv函数

功能:是从⼀一个消息队列接收消息

原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数
    msgid: 由msgget函数返回的消息队列标识码
    msgp:是一个指针,指针指向准备接收的消息,
    msgsz:是msgp指向的消息⻓度,这个长度不含保存消息类型的那个long int长整型
    msgtype:它可以实现接收优先级的简单形式

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

    注意:

    如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1。如果执行的是msgrcv(),则在消息队列为空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列为满或为空的情形时,采取阻塞等待的处理模式

返回值: 成功返回实际放到接收缓冲区⾥里去的字符个数,失败返回-1


接下来我们应用这些函数来实现client/server两进程进行通信:直接看代码:

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

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

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资源
    ipcrm手动删除IPC资源
我们要查看当前系统中的消息队列就可以加上 -q 选项,如图: 


这个时候我们可以看到当前是没有消息队列的,现在来运行我们的代码,先运行server.c,再开启一个终端查看消息队列



这时我们会发现,存在了消息队列,但是由于我们没有进行通信,因此mesages为0.

现在我们来进行通信:


最后,我们需要手动来删除消息队列,利用我们刚刚提到的命令: 

    ipcrm -q +msqid

    ipcrm -Q +key


如上图所示,两种方法均可以删除消息队列。。。


总结:

消息队列的特性:

    1、生命周期随内核

    2、不是面向字节流,而是基于消息的


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值