Linux下使用C语言实现进程间的通讯-消息队列

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、Linux下查看消息队列
    • 1.1 查看消息队列的设置信息
    • 1.2 调整消息队列的设置信息
    • 1.3 查看消息队列:
  • 二、消息队列
    • 2.1 基本结构
      • 2.1.1 数据结构
      • 2.1.2 key值与ID值
    • 2.2 函数详解
      • 2.2.1 msgget
        • 2.2.1.1 函数原型
      • 2.2.2 msgctl
        • 2.2.2.1 函数原型
        • 2.2.2.2 cmd字段除了公共的四个操作以外,还有自己独立的操作。
      • 2.2.3 msgsnd和msgrcv
          • 2.3.1 函数原型
      • 2.2.4 msgp(自己定义的信号)
  • 总结


前言

参考网上资源:https://www.bookstack.cn/read/linuxapi/docs-msgsnd-msgrcv.md


一、Linux下查看消息队列

消息队列提供了一种从一个进程向另外一个进程发送一个数据块的方法。
消息队列每个消息的最大长度有限制,每个消息队列总字节数有限制,系统的消息队列有限制;

1.1 查看消息队列的设置信息

查看每个消息的最大长度:
cat /proc/sys/kernel/msgmax
查看每个消息队列总的字节数:
cat /proc/sys/kernel/msgmnb
查看消息队列的总数上限:
cat /proc/sys/kernel/msgmni

1.2 调整消息队列的设置信息

通过root用户修改/etc/sysctl.conf中的配置可调整上述参数的大小(如果本身/etc/sysctl.conf中没有相应的配置信息,则可以新增):
kernel.msgmni=65536
kernel.msgmax=81920
kernel.msgmnb=4194304
修改后执行下面的命令生效:
/sbin/sysctl -p

1.3 查看消息队列:

ipcs -q :查看消息队列
ipcs -pq :查看消息队列对应的用户、发送和接收进程pid
ipcs -q -i msgid :查看该msgid的消息队列的详细情况
ipcrm -Q key:删除消息队列

二、消息队列

2.1 基本结构

2.1.1 数据结构

消息队列数据结构
消息的链式队列。
重要的数据结构

msqid_ds 代码如下(示例):

//位置/usr/include/linux/msg.h
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;    /* Reuse junk fields for 32 bit */
    unsigned long  msg_lqbytes;    /* ditto */
    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;    /* 最近receive的pid */
};

2.1.2 key值与ID值

每一个IPC机制都有一个ID,只有ID值相同才能传递数据。它的类型是key_t(int),但是设置任意数字为key值有违软件设计的思想。所以Linux提供了函数ftok来创建key值,以文件为参数,提高了与文件的关联度。

2.2 函数详解

2.2.1 msgget

创建消息队列。如果把消息队列看做一个文件的话,那么该函数就相当于open。

2.2.1.1 函数原型

#include 
#include 
#include 
int msgget(key_t key, int msgflg);

参数
a) 第一个参数是key值。
b) 第二个参数的地位用来确定消息队列的访问权限。可以附加参数:

比如:msgid=msgget(key,0666|IPC_CREAT)

IPC_CREAT 如果key不存在,则创建(类似open函数的O_CREAT)
IPC_EXCL 如果key存在,则返回失败(类似open函数的O_EXCL)
IPC_NOWAIT 如果需要等待,则直接返回错误

返回值
成功执行时,返回消息队列标识符。失败返回-1,errno被设为以下的某个值

  • EACCES:指定的消息队列已存在,但调用进程没有权限访问它,而且不拥有CAP_IPC_OWNER权能
  • EEXIST:key指定的消息队列已存在,而msgflg中同时指定IPC_CREAT和IPC_EXCL标志
  • ENOENT:key指定的消息队列不存在同时msgflg中不指定IPC_CREAT标志
  • ENOMEM:需要建立消息队列,但内存不足
  • ENOSPC:需要建立消息队列,但已达到系统的限制

2.2.2 msgctl

该函数用来对消息队列的基本属性进行控制、修改。

2.2.2.1 函数原型

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

参数
a) msqid为消息队列标识符。
b) cmd为执行的控制命令(在ipc.h中定义):

  • #define IPC_RMID 0
  • #define IPC_SET 1
  • #define IPC_STAT 2
  • #define IPC_INFO 3

c) buf

2.2.2.2 cmd字段除了公共的四个操作以外,还有自己独立的操作。

IPC_RMID
删除消息队列。从系统中删除给消息队列以及仍在该队列上的所有数据,这种删除立即生效。
仍在使用这一消息队列的其他进程在它们下一次试图对此队列进行操作时,将出错,并返回EIDRM
此命令只能由如下两种进程执行:

  • 其有效用户ID等于msg_perm.cuid或msg_perm.guid的进程。
  • 另一种是具有超级用户特权的进程。

IPC_SET
设置消息队列的属性。按照buf指向的结构中的值,来设置此队列的msqid_id结构。
该命令的执行特权与上一个相同。
IPC_STAT
读取消息队列的属性。取得此队列的msqid_ds结构,并存放在buf*中。
IPC_INFO
读取消息队列基本情况。

这是一对函数,用于消息队列的发送和接收

2.2.3 msgsnd和msgrcv

msgsnd函数:用于将新的消息添加到消息队列的尾端。
msgrcv函数:用于从消息队列中读取msqid指定的消息

2.3.1 函数原型
#include 
#include 
#include 
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);

参数
(1) msgsnd
a) msqid:消息队列标识符(由msgget生成)
b) msgp:指向用户自定义的缓冲区(msgp)
c) msgsz:接收信息的大小。范围在0~系统对消息队列的限制值
d) msgflg:指定在达到系统为消息队列限定的界限时应采取的操作。

  • IPC_NOWAIT 如果需要等待,则不发送消息并且调用进程立即返回,errno为EAGAIN
  • 如果设置为0,则调用进程挂起执行,直到达到系统所规定的最大值为止,并发送消息

(2) msgrcv
a) msqid:消息队列标识符
b) msgp:指向用户自定义的缓冲区(msgp)
c) msgsz:如果收到的消息大于msgsz,并且msgflg&MSG_NOERROR为真,则将该消息截至msgsz字节,并且不发送截断提示
d) msgtyp:用于指定请求的消息类型:

  • msgtyp=0:收到的第一条消息,任意类型
  • msgtyp>0:收到的第一条msgtyp类型的消息。
  • msgtyp<0:收到的第一条最低类型(小于或等于msgtyp的绝对值)的消息。

e) msgflg:用于指定所需类型的消息不再队列上时的将要采取的操作:

  • 如果设置了IPC_NOWAIT,若需要等待,则调用进程立即返回,同时返回-1,并设置errno为ENOMSG
  • 如果未设置IPC_NOWAIT,则调用进程挂起执行,直至出现以下任何一种情况发生:
    某一所需类型的消息被放置到队列中。
    msqid从系统只能怪删除,当该情况发生时,返回-1,并将errno设为EIDRM。
    调用进程收到一个要捕获的信号,在这种情况下,未收到消息,并且调用进程按signal(SIGTRAP)中指定的方式恢复执行。

返回值
毋庸置疑,成功0,失败-1。
其他要注意的是,消息队列数据结构(msqid_ds类型)成员的变化:
(1) msgsnd
成功时:

  • msg_qnum 以1为增量递增
  • msg_lspid 设置为调用进程的pid
  • msg_stime 设置为当前时间。

(2) msgrcv
成功时:

  • msg_qnum 以1为增量递减
  • msg_lrpid 设置为调用进程的pid
  • msg_rtime 设置为当前时间。

2.2.4 msgp(自己定义的信号)

该类型需要自己在编程时定义,用于存储消息的内容。
下面给出一个范例,注意,里面的名称随意。

消息类型必须要有,且不为0

struct msgbuf{
    long mtype;   //消息类型 
    char mtext[1];//数组大小编程时自己指定
};

总结

  • 用于linux下进程间的通讯;
  • 了解在linux下使用命令创建、查看、删除消息队列,并修改其属性;
  • 使用c代码实现消息队列的创建等。

你可能感兴趣的:(LinuxC++编程,linux,c语言,java)