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

消息队列

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

    (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中打开:

消息队列函数:

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两进程进行通信:

首先创建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

#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 0x666  
 
#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);  
        size_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);  
        size_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;  
}  

ipcs & ipcrm指令:

    ipcs 显示IPC资源 

    ipcrm手动删除IPC资源 

当我们要查看当前系统中的消息队列就可以加上 -q 选项:

当我们需要手动 删除消息队列时候:

    ipcrm -q +msqid

    ipcrm -Q +key

通信结果如图所示:

猜你喜欢

转载自blog.csdn.net/yaotengjian/article/details/79941948