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

一、消息队列是什么

消息队列提供了从一个进程向另外一个进程发送一块数据的方法,每个数据块都有相应的类型,接收者进程接收的数据块可以由不同的类型值。

消息队列和管道的不同之处,是消息队列是基于消息的,而管道是基于字节流的。

消息队列和命名管道一样有缺陷:

  • 每个消息的最大长度是由上线的。
  • 每个消息队列的总字节数是有上限的。
  • 系统上消息队列的总数也是有上限的。

这个上限值我们可以分别在proc/sys/kcrncl/msgmax、proc/sys/kcrncl/msgmnb、proc/sys/kcrncl/msgmni里面找到相应的内容。

消息队列不靠引用计数,是个设计上的缺陷,如果有别人要删除这个消息队列,此时另外一个正在写的直接被删除了。

二、IPC数据结构

每个进程间通信中都有如下数据结构,这个数据结构在/usr/include/linux/ipc.h中

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 */
gid_t          cgid;        /* Effective GID of creator */
unsigned short mode;        /* Permissions */ 
unsigned short __seq;       /* Sequence number */ 
}; 

首先IPC 对象的关键字,uid 和gid。然后是IPC 对象的创建者的 uid 和gid。接下来的是IPC 对象的存取权限。最后一个成员也许有点难于理解,不过不要担心,这是系统保存的IPC 对象的使用频率信息,我们完全可以不去理会它。

三、消息队列结构

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

我们可以看到消息队列的结构msgid_ds中第一个结构就是ipc_perm,后面都是它的私有成员。

消息队列是用链表实现的,如下图:

这里写图片描述

四、有关消息队列函数

(1)msgget函数

我们在Linux下man msgget查看有关msgget函数的信息。

msgget函数有创建和打开的意思。

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

int msgget(key_t key, int msgflg);
  • 返回值
    返回值是个id相当于文件描述符,失败返回-1。
  • 参数
    1. key:某个消息队列的名称。相当于文件名,产生的方式有三种
      方法一:直接写key,例如1234相当于写死
      方法二:IPC_PROVATE生成一个私有的消息队列
      方法三:用ftok()函数生成。
    2. msgflg:九个权限标志组成

ftok()函数

在key的第三种生成方式中指出了用ftok函数进行生成,下面讲解下ftok函数

int ftok(char* path ,int pro_id)
  • 参数
    path:第一个参数是一个路径(是系统必须存在的路径)。pro_id:第二个参数是一个id(低八位必须非0).
    唯一产生一个id.
  • 返回值
    这个函数的返回值是一个key,这个key分为前八位中间八位和后16位、前8位是id的低八位,中间8位是st的低八位,是次设备号,后16位是inode的低16位。

flg相当于相应的权限,可设置的参数有IPC_CREAT等,权限可以或。

例:

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

int main()
{
    //此处采用了ftok函数生成key
    int fd = msgget(ftok(".",'a'),IPC_CREAT|0644);
    if(fd == -1 )
        perror("msgget"),exit(1);
    printf("ok\n");
}

运行结果如图:

这里写图片描述

次设备号 ls -l /dev/sda2

(2)msgctl函数

msgctl是消息队列的控制函数

  • 函数原型
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • 返回值
    操作成功返回0,失败返回-1。
  • 参数
    msqid:由msgget函数返回的消息队列标识符
    cmd:是将要采取的动作,参数如下
    • IPC_RMID:删除消息队列
    • IPC_SET:在由权限的情况下,把消息队列的关联值设置为msqid_ds的数据结构中给的值。
    • IPC_STAT:把msqid_ds结构中的数据设置为消息队列当前值

(3)msgsnd函数

向消息队列中发送数据
msgsnd

  • 函数原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  • 返回值:
    成功返回0,失败返回-1。
  • 参数:
    • msqid:由msgget函数返回的消息队列标识码。
    • msgp:第二个参数指向准备发送的消息地址,必须是一种特殊的格式,是一个结构体,第一个参数是消息的类型,必须是大于0,第二个参数是真正的消息数据。
    • msgsz:第三个参数是通道的大小,是不包含channel字段的
    • msgflg:最后一个参数一般填写0,一般常用的是IPC_NOWAIT,如果加了,消息队列写满了会返回错误,errno=EAGAIN

备注:如下是第二个参数的结构体。

struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[1];    /* message data */
};

(4)msgrcv函数

向消息队列中读取数据
msgrcv

  • 函数原型
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
  • 参数
    • msqid:由msgget函数返回的消息队列标识码。
    • msgtype:从指定的通道收取数据。如果大于0则指定的通道收取数据,如果等于0,则返回消息队列中的第一条,如果小于0,会读取小于等于这个数的绝对值,并且先读最小的。
    • msgflg:flag一般写0,还可以写IPC_NOWAIT

五、删除创建的消息队列的方法

(1)手动删除

  • 命令:
    ipcrm
  • 注意:
    如果根据KEY来删除,则在命令后面要跟上大Q,如果根据id来删除命令后面是用小q
[root@localhost day10]# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0xffffffff 0          root       644        0            0           
0x61020113 32769      root       644        0            0           

[root@localhost day10]# ls -id
131347 .
[root@localhost day10]# ls -l /dev/sda2 
brw-rw----. 1 root disk 8, 2 1229 23:31 /dev/sda2
[root@localhost day10]# ipcrm -Q 0xffffffff
[root@localhost day10]# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x61020113 32769      root       644        0            0      

(2)函数删除

根据函数来删除的方法是选用msgctl()函数

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

如果是要删除,第二个参数填IPC_RMID,第三个参数填0,因为无意义

例子:

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

int main()
{
    int id = msgget(ftok(".",'a'),0);
    if(id == -1 )
        perror("msgget"),exit(1);

    msgctl(id,IPC_RMID,0);
}

运行结果如下:

[root@localhost day10]# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x61020113 32769      root       644        0            0           

[root@localhost day10]# ./a.out 
[root@localhost day10]# ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值