[Linux]——IPC进程通信之消息队列

System V 消息队列

消息队列是两个进程之间传递二进制数据一种简单有效的方式。每个数据块都有一个特定的类型,接收方可以根据类型来有效的接收数据,而不一定像管道和命名管道那样必须以先进先出的方式接收数据。

数据块的类型其实是自己定义的,这里的类型并不是指的int、long,而是一个正整数。笔者将这里的类型理解为一个标识,我们可以用下图来为大家解释。

[Linux]——IPC进程通信之消息队列_第1张图片
消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道都有一个共同的不足,就是每个消息最大长度是有上限的,而且呢消息队列的生命周期是伴随内核。

内核中维护的消息队列其实是一个链表,那么让我们一起看看内核中的消息队列是如何被管理的。

消息队列内核管理

struct msqid_ds
{
    struct ipc_perm msg_perm;//消息队列操作权限信息
    struct msg *msg_first; //消息队列上的第一条信息指针
    struct msg *msg_last;//消息队列上的最后一条信息指针
    __kernel_time_t msg_stime;//最后一次调用msgsnd的时间
    __kernel_time_t msg_rtime;//最后一次调用msgrcv的时间
    __kernel_time_t msg_ctime;//最后一次被修改的时间
    unsigned long  msg_lcbytes;
    unsigned long  msg_lqbytes;
    unsigned short msg_cbytes;
    unsigned short msg_qnum;//消息队列中已经有的消息数
    unsigned short msg_qbytes;//消息队列允许的最大字节数
    __kernel_ipc_pid_t msg_lspid;//最后一次调用msgsnd的进程pid
    __kernel_ipc_pid_t msg_lrpid;//最后一次调用msgrcv的进程pid
};

在这里它的结构已经解释它的链式队列,msg_first和msg_last就是它的头指针和尾指针

消息队列系统调用

msgget函数:创建一个新的消息队列,或者获取一个已经存在的消息队列

int msgget(key_t key,int msgflg);

参数介绍:

  • key:用来标识系统中唯一的一个消息队列,使用ftok函数获取
  • msgflg:通常使用IPC_CREAT和IPC_EXCL等选项,他们直接使用"|"连接,也可以|上信号量的权限(八进制)

函数调用成功后就会在系统中为我们创建一个msqid_ds信号集合,并且初始化其中的某些部分。函数返回值返回一个此消息队列的标识符,失败时返回-1。其实我们发现System V版本的进程间通信机制系统调用都大同小异,记住一个的用法,其他的也就不难理解。

msgsnd函数:向消息队列发送数据

int msgsnd(int msqid,const void* msg_ptr,size_t msg_sz,int msgflg);

参数介绍:

  • msqid:填msgget函数的返回值
  • msg_ptr:指向一个准备被发送的消息,这个消息应该被定义为如下结构
struct msgbuf
{
	long mtype; //大于0	
	char mtext[512];// 消息的大小
};

这里的mtype就是不同数据的标识,需要填充一个大于0的数据,mtext中存放你要发送的消息

  • msg_sz:mtext消息的大小
  • msgflg:控制msgsnd的行为,填0表示阻塞发送,IPC_NOWAIT表示非阻塞发送。阻塞发送时如果消息队列被删除会收到EIDRM错误返回,或者被信号打断收到EINTR

msgrcv函数:从消息队列接收数据

ssize_t msgrcv(int msqid,void *msg_ptr,size_t msg_sz,long mtype,int msgflg);

参数介绍:

这里参数除了mtype其他参数全部同上,我们这里着重介绍一下mtype这个参数

  • mtype:表示你希望从消息队列中拿出哪种类型的数据。这个参数分别有大于零等于零小于零三种情况。当mtpye大于0时表示希望接收类型与mtpye相同的数据。等于零时读取消息队列中的第一个信息。小于零时读取消息队列中第一个类型比mtpye绝对值小的消息。
  • msgflg:设置为IPC_NOWAIT时表示如果消息队列中没有消息立马返回。设置IPC_EXCEPT并且mtype大于零则接收非mtype类型的数据

msgmctl函数:该函数用来直接控制消息队列信息

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

使用方式和之前信号量共享内存基本相同,我们这里只把经常使用的三个cmd列出:

命令 描述
IPC_STAT 该命令用来获取消息队列对应的msqid_ds数据结构,并将其保存到buf指定的地址空间
IPC_SET 该命令用来设置消息队列的属性,要设置的属性存储在buf中
IPC_RMID 从内核中删除msqid标识的消息队列

简单的消息队列使用实例

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define TYPE 1 //我们要接收的消息定义为类型1

//struct msgbuf //系统中已经帮我们定义了
//{
//    long mtype;
//    char mtext[512];
//};

int main()
{
    int k = ftok("/tmp", 0x6666);
    int msgid = msgget(k, IPC_CREAT|0644);
    pid_t id = fork();
    if(id < 0){
        cout << "fork error !" <<endl;
    }
    if(id == 0){
        struct msgbuf _mb;
        _mb.mtype = TYPE;
        string msg = "hello msgqueue!";
        strcpy(_mb.mtext, msg.c_str());
        if(msgsnd(msgid, (void*)&_mb, sizeof(_mb.mtext), 0) < 0){
            cout << "msgsnd error !!" <<endl;
        }
    }
    else{
        struct msgbuf _mb;
        if (msgrcv(msgid, &_mb, sizeof(_mb.mtext), TYPE, 0)< 0){
            cout << "msgrcv error !!" <<endl;
        }
        char buf[100];
        strcpy(buf,_mb.mtext);
        cout << "father recv:" << buf <<endl;
    }
    waitpid(id,NULL,0);
    msgctl(msgid, IPC_RMID, NULL);
    return 0;
}

补充关于System V IPC的Shell命令

使用ipcs查看系统中的消息队列、共享内存、信号量
[Linux]——IPC进程通信之消息队列_第2张图片
如果想单独的查询某一个那么可以使用选项,-m表示共享内存、-q表示消息队列、-s表示信号量。当我们需要手动的删除某个通信机制时使用ipcrm [选项] [shmid]。

你可能感兴趣的:(Linux)