0
点赞
收藏
分享

微信扫一扫

linux进程间通信:system V消息队列


文章目录

  • ​​基本介绍​​
  • ​​编程接口​​
  • ​​代码实例​​
  • ​​消息队列的发送和接收​​
  • ​​消息队列中的消息对象的属性控制​​

基本介绍

  1. 支持不同进程之间以消息(messages)的形式进行数据交换,消息能够拥有自己的标识,且内核使用链表方式进行消息管理。
  2. 进程之间的通信角色为:发送者和接受者
    发送者:
    a. 获取消息队列的ID(key或者msgid)
    b. 将数据放入一个带有标识的消息结构体,发送到消息队列
    接受者:
    a. 获取消息队列的ID
    b. 指定标识的消息从消息队列中读出,然后进一步后续处理
  3. 支持不同的进程标记不同的消息类型(1,2,3…),并由内核态维护对应消息类型的链表。
  4. 内核态的0号消息类型维护了一个链表,用来保存按照时间顺序加入的消息

编程接口

  1. 生成ipc对象的唯一标识​​key​​的接口
    a. 头文件 ​​<sys/ipc.h>​​ b. 函数声明 ​​key_t ftok(const char *path, int id);​​ c. 参数描述
    ​path​​需指定一个已经存在的可访问的文件
    ​id​​为用户可自由指定的id
  2. 创建或者打开一个消息队列,并获取system V 消息队列中消息的身份标识
    a. 头文件 ​​<sys/types.h> <sys/ipc.h> <sys/msg.h>​​ b. 函数声明 ​​int msgget (ket_t key, int msgflg)​​ c. 参数描述
    ​key​​ 为ipc对象的唯一标识,生成的消息身份标识与该参数相关
    ​msgflg​​当该函数没有搜索到系统中与key值对应的消息队列,则msgflg会指定IPC_CREAT,创建一个队列,并返回消息标识
    d. 返回值:成功返回消息身份标识,失败返回-1
  3. 发送消息到消息队列
    a. 头文件 ​​<sys/types.h> <sys/ipc.h> <sys/msg.h>​​ b. 函数声明 ​​int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);​​ c. 参数描述
    ​msqid​​ 消息标识,类似于文件描述符fd
    ​msgp​​ 消息内容指针
    ​msgsz​​ 消息大小
    ​msgflg​​ 当出现消息队列没有足够的可用空间时,可以通过设置msgflg为​​IPC_NOWAIT​​来让发送函数不产生阻塞,返回失败
    d. 返回值 失败返回-1,以及对应失败码;成功则返回0
  4. 从消息队列中接收消息
    a. 头文件 ​​<sys/types.h> <sys/ipc.h> <sys/msg.h>​​ b. 函数声明 ​​ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);​​ c. 参数描述
    ​msqid​​ 消息标识,类似于文件描述符fd
    ​msgp​​ 消息内容指针
    ​msgsz​​ 消息大小,同时也是当前接收的消息最大能够接收的大小
    ​msgflg​​ 当出现实际的消息内容大于设定的msgsz,可以通过​​MSG_NOERROR​​来将消息裁剪为​​msgsz​​大小进行获取,否则会返回失败。而消息仍然存在于消息队列。
    ​msgtyp​​ 如果是0,则会读取处于消息队列的第一个消息;大于0,则会读取对应type处于消息队列中的第一个消息;如果小于0,则会读取type绝对值或者小于绝对值的消息队列的第一个消息。
    d. 返回值 失败返回-1,成功返回对应消息的大小
  5. 控制消息队列的各个操作
    a. 头文件 ​​<sys/types.h> <sys/ipc.h> <sys/msg.h>​​ b. 函数声明 ​​int msgctl(int msqid, int cmd, struct msqid_ds *buf);​​ c. 参数描述
    ​msgqid​​ 消息标识
    ​cmd​​ 针对消息标识的操作,合法的操作如下:
  • ​IPC_STAT​​​ 获取msgqid的消息对象的信息,将各个属性从内核拷贝到一个临时的数据结构​​msgqid_ds​​类型的buf;调用者需要对消息队列有读权限
  • ​IPC_SET​​​ 自己可以通过临时的​​msgqid_ds​​​来设置内核中消息的对应​​msgqid_ds​​的属性
  • ​IPC_RMID​​ 立即移除消息队列;当前调用者需要拥有 消息队列的所有者权限,或者高于所有者的权限(root)
  • ​IPC_INFO​​ 返回消息队列的参数限制

其他标识可以通过​​man msgctl​​来查看

代码实例

消息队列的发送和接收

发送端msg_snd.c

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

#define MSG_TYPE1 1
#define MSG_TYPE2 2

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

int main()
{
//当多用户的时候通过指定文件以及设置id来获取唯一的key标识
//key_t key = ftok(".",100);
key_t key = 12345; //个人使用的时候可以直接指定key

//创建msg_qid的对象
int msg_qid = msgget(key, IPC_CREAT | 0666);
struct msgbuf msg;
memset(&msg, 0 , sizeof(msg));

//初始化消息类型以及消息内容
msg.mtype = MSG_TYPE2;
strncpy(msg.mtext, "hello world" , 80);

//发送消息到消息标识的msg_qid IPC 对象中
if( -1 == msgsnd(msg_qid,(void *)&msg,strlen(msg.mtext),0)) {
printf("send msg failed\n");
_exit(-1);
}

return 0;
}

编译运行:
​​​gcc msg_snd.c -o msg_snd​

运行前查看系统消息队列
​​​ipcs -q​

[root@node1 ~]# ipcs -q

------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 0 root 666 0 0

运行后,可以看到我们发送了消息队列的各个属性信息。关于key值,它为我们设置的12345的16进制数值

[root@node1 ~]# ./msg_snd 
[root@node1 ~]# ipcs -q

------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 0 root 666 0 0
0x00003039 65538 root 666 11 1

接收端msg_rcv.c

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

#define MSG_TYPE1 1
#define MSG_TYPE2 2

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

int main()
{
key_t key = 12345;
int msg_qid = msgget(key, IPC_CREAT | 0666);
struct msgbuf msg;
memset(&msg, 0 , sizeof(msg));

if (-1 == msgrcv(msg_qid,(void *)&msg,sizeof(msg.mtext),MSG_TYPE2,0)) {
printf("receive msg failed\n");
_exit(-1);
}
printf("%s\n",msg.mtext);

//当完成接收之后从消息队列中删除当前消息
//msgctl(msg_id,IPC_RMID,NULL);
return 0;
}

编译运行,可以看到已经接手到了我们之前发送的内容:

[root@node1 ~]# gcc msg_rcv.c -o msg_rcv
[root@node1 ~]# ./msg_rcv
hello world

查看消息队列情况,消息队列中的数据已经被接收,所以在used-bytes和messages中看不到消息内容了,但是没有删除该消息队列,所以消息标识仍然存在。我们可以在上述代码中加入​​msgctl​​:

[root@node1 ~]# ipcs -q

------ Message Queues --------
key msqid owner perms used-bytes messages
0x000004d2 0 root 666 0 0
0x00003039 65538 root 666 0 0

消息队列中的消息对象的属性控制

控制代码​​msg_ctl.c​​​​int msgctl(int msqid, int cmd, struct msqid_ds *buf);​​​ 其中主要控制的是消息队列一个数据结构,可以通过​​man msgctl​​查看 ​​msqid_ds​​结构体

struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (nonstandard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};

以下代码为主要控制参数的代码,通过msgctl的cmd参数进程控制

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

int main()
{
key_t key = 12345;
int msg_id = msgget(key,IPC_CREAT|0666);

struct msqid_ds info;

//第一次先从已存在的12345的消息中获取队列状态
if (-1 == msgctl(msg_id,IPC_STAT,&info)) {
printf("control msg failed\n");
_exit(-1);
}

//打印各个状态信息
printf("uid :%d,gid:%d,cuid:%d,cgid:%d\n",\
info.msg_perm.uid,info.msg_perm.gid,info.msg_perm.cuid,info.msg_perm.cgid);

printf("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu\n",\
info.msg_perm.mode & 0777,info.__msg_cbytes,info.msg_qnum,info.msg_qbytes);

//尝试设置消息队列允许的最大字节内容
info.msg_qbytes = 16000;

//通过cmd为IPS_SET的标记进行设置
if (-1 == msgctl(msg_id, IPC_SET, &info)) {
printf("ipc_set failed\n");
_exit(-1);
}

if (-1 == msgctl(msg_id, IPC_STAT, &info)) {
printf("ipc_stat failed\n");
_exit(-1);
}


printf("mode:%o3o,cbytes:%lu,qnum:%lu,qbytes:%lu\n",\
info.msg_perm.mode & 0777,info.__msg_cbytes,info.msg_qnum,info.msg_qbytes);
return 0;
}

输出如下,可以看到我们控制的队列允许的最大字节内容msg_qbytes已经设置进去:

[root@node1 ~]# ./msg_ctl 
uid :0,gid:0,cuid:0,cgid:0
mode:6663o,cbytes:11,qnum:1,qbytes:16000
mode:6663o,cbytes:11,qnum:1,qbytes:16000


举报

相关推荐

0 条评论