linux:进程间通信之消息队列

1.消息队列是什么?

消息队列是一个IPC对象,是操作系统提供的一个链表,消息队列提供了一个从一个进程向另一个进程发送一块数据块的方法,每个数据块都被认为是有一个类型,接受者进程接收的数据块可以有不同类型
ps:此处的类型并不是int、char类型

2.消息队列的特点?

1.可用于双向通信;
2.生命周期随内核;
3.可用于随意内核;
  因为消息队列的生命周期随内核,所以消息队列必须显式删除
4.面向数据块;
5.自带同步互斥;

3.消息队列的优点和缺点?

优点:

消息队列是在两个不相关的进程间传递数据的一种简单、高效的方式,它独立于发送进程和接收进程

缺点:

每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNL)

这里写图片描述
ps:消息队列独立与进程,因此需要唯一标识消息队列,以便两个不相关的进程能够通过事先约定好的key进行通信

4.消息队列的相关函数

(1)msgget()

1.头文件:#include<sys/msg.h>
2.功能:创建和访问消息队列

3.int msgget(key_t key,int msgflg);
参数说明:
     key:是某个消息队列的名字(由ftok()而来,函数ftok()的返回值就是key
     msgflg:由9个权限标志构成
4.返回值
     成功返回一个非负整数,即该消息队列的标识码(msqid)
     失败:返回-1

ftok()

 1.key_t ftok(const char *pathname, int proj_id);
 参数说明:
   (1)filename:是指定的文件名,这个文件必须是存在的而且可以访问的。
                 ps: 一般设为" .  "2)proj_id:是子序号,它是一个8bit的整数。即范围是0~255。
                ps:一般设为0x6666
2.返回值:
        成功:则会返回key_t键值,
        失败:返回-1

(2)msgctl()

1.功能:消息队列的控制函数
  头文件:#include<sys/msg.h>

2.int msgctl(int msqid, int cmd, struct msqid_ds* buf);
参数说明:
 (1)msqid:是segget函数的返回值,是消息队列的标识码
 (2)cmd:是对此消息队列将要采取的动作
 (3)buf:是一个指针,指向消息队列的结构,如果想要对消息队列底层结构
     做什么修改的话,可使用此参数,如果不做修改的话,此参数可以设为NULL
            ps:此参数一般都设为NULL
3.返回值:
         成功:返回0
         失败:返回-1

cmd有三个可取值:

1.IPC_RMID: 删除消息队列(此选项用的较多);
2.IPC_SET: 在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值;
3.IPC_STAT:把msqid_ds数据结构中的数据设置为消息队列的当前关联值;

(3)msgsnd()

1.功能:把一条消息添加到消息队列中
  头文件:#include<sys/msg.h>

2.int msgsnd(int msqid,const void* msgp,size_t msgsz,int msgflg);
   参数说明:
   (1)msqid:是msgget函数的返回值,是该消息队列的标识码
   (2)msgp:是一个指针,指向准备发送的消息
   (3)msgsz:是msgp指向的消息的长度,即就是准备发送消息的长度
   (4)msgflg:控制着当前消息队列满或者到达系统上限时将要发生的事情
          msgflg=IPC_NOWAIT:表示队列满不等待,并返回EAGAIN错误
                            即就是:放满,不阻塞,直接返回
          msgflg=0: 表示此消息队列放满的话,就会阻塞 (一般设为03.返回值:
          成功:返回0
          失败:返回-1

(4)msgrcv()

1.功能:是从一个消息队列接收消息
2.头文件:#include<sys/msg.h>

3.ssize_t msgrcv(int msqid,void* magp,size_t msgsz,long msgtype,int msgflg)
参数说明:
(1)msqid:是msgget函数的返回值,是此消息队列的标识码
(2)msgp:是一个指针,指向准备接收的消息
(3)msgsz:是msgp指向消息的长度,这个长度不包含保存消息类型的long int4)msgtype:可以实现接收优先级的简单形式
(5)msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
ps:此参数一般设为0

这里写图片描述

5.示例

实现服务器与客户端之间的通信
msg.h

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

#define PATHNAME "."
#define PROJ_ID 0x6666

#define SERVER_TYPE 1
#define CLIENT_TYPE 2

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

int createMsgQueue();//创建消息队列;
int getMsgQueue();//打开消息队列
int sendMsg(int msgid,int who,char *msg);//发消息
int recvMsg(int msgid,int recvType,char out[]);//收消息
int destroyMsgQueue(int msgid);//销毁消息队列}

msg.c

#include "msg.h"
static int commMsgQueue(int flag)
{
    //得到key
    key_t key=ftok(PATHNAME,PROJ_ID);
    if(key<0)
    {
        perror("ftok");
        exit(1);
    }
    //msgget()创建消息队列
    int msgid=msgget(key,flag);
    if(msgid<0)
    { 
        perror("msgget");
        exit(1);
    }
    return msgid;
}
int createMsgQueue()
{
    return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQueue()
{
    return commMsgQueue(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");
        exit(1);
    }
    return 0;
}
int recvMsg(int msgid,int recvType,char out[])
{
    struct msgbuf buf;
    if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0)<0)
    {
        perror("msgrcv");
        exit(1);
    }
    strcpy(out,buf.mtext);
    return 0;
}
int destroyMsgQueue(int msgid)
{
    if(msgctl(msgid,IPC_RMID,NULL)<0)
    {
        perror("msgctl");
        exit(1);
    }
    return 0;
}

server.c

#include "msg.h"
int main()
{
    //创建消息队列
    int msgid=createMsgQueue();
    char buf[1024]={0};
    while(1)
    {
        //从客户端接收消息
        recvMsg(msgid,CLIENT_TYPE,buf);
        //输出收到的消息
        printf("client say:>  %s\n",buf);
        //给客户端发消息
        printf("please say to client:>   ");
        fflush(stdout);
        ssize_t r=read(0,buf,sizeof(buf));
        if(r>0)
        {
            buf[r-1]=0;
            sendMsg(msgid,SERVER_TYPE,buf);
            printf("server send done,wait recv...\n");
        }
    }
    //销毁消息队列
    destroyMsgQueue(msgid);
    return 0;
}

client.c

#include "msg.h"
int main()
{
    //打开消息队列
    int msgid=getMsgQueue();
    char buf[1024]={0};
    while(1)
    {
        //给服务器发送消息
        printf("please say to  server:>  ");
        fflush(stdout);
        ssize_t r=read(0,buf,sizeof(buf));
        if(r>0)
        {
            buf[r-1]=0;
            sendMsg(msgid,CLIENT_TYPE,buf);
            printf("client send done,wait recv...\n");
        } 
        //从服务器接收消息
        recvMsg(msgid,SERVER_TYPE,buf);
        printf("server say:>    %s\n",buf);
    }
    return 0;
}

运行结果:

编译命令:gcc server.c msg.c -o server

这里写图片描述

编译命令:gcc client.c msg.c -o client

这里写图片描述

现在异常终止./server,再次运行./server,会出现以下的结果:
这里写图片描述
使用如下的命令,解决上面的情况:

ipcs -q //显示所有消息队列
ipcrm -q +ID(此ID是消息队列的msqid)//手动删除ID对应的消息队列

这里写图片描述
再次运行./server的话,程序就可以正常运行了

6.ipcs&ipcrm命令

1.ipcs

作用:显示所有的IPC资源
ipcs -q//显示所有的消息队列
ipcs -m//显示所有的共享内存

2.ipcrm

作用:手动删除IPC资源
ipcrm -q +msqid//删除消息队列名为msqid的消息队列
ipcrm -m +shmid//删除共享内存名为shmid的共享内存

猜你喜欢

转载自blog.csdn.net/dangzhangjing97/article/details/79998433