6.消息队列
6.1 IPC对象
除了最原始的进程间通信方式信号、无名管道和有名管道外,还有三种进程间通信方式,这三种方式称之为IPC对象: 消息队列、共享内存、信号灯集。
IPC对象也是在内核空间开辟区域,每一种IPC对象创建好之后都会将其设置为全局,并且会给其分配一个编号,只要找到唯一的这个编号就可以进行通信,所以不相关的进程可以通过IPC对象通信。
IPC对象创建好之后,会在当前系统中可见,只要不删除或者不关闭系统,就会一直存在。
查看系统中已经创建的IPC
ipcs # 查看当前系统中所有创建的IPC对象
ipcs -q # 查看消息队列
ipcs -m # 查看创建的共享内存
ipcs -s # 查看信号量
ipcrm # 删除ipc对象
如
ipcrm -q msqid # 删除标号为msqid的消息队列
6.2 消息队列概述
- 消息队列中的消息是有类型的
- 消息队列中的消息是有格式的
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取
- 消息队列允许一个或多个进程向它写入或者读取消息
- 与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。
- 每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
- 只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。
SystemV提供的 IPC 通信机制需要一个key 值,通过key植就可在系统内获得一个唯一的消息队列标识符
key 值可以人为指定,也可以通过 ftok
函数获得。
6.2.1 ftok函数
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj id);
功能:获得项目相关的唯一的 IPC 键值;
参数:
路径名pathname:
prej_id:项目 ID,非0整数(只有低 8 位有效),范围 0 ~ 127;
返回值:
成功:返回 key 值;
失败:返回 -1
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
key_t mykey;
if ((mykey = ftok(".", 100))== -1)
{
perror("fail to ftok");
exit(1);
}
printf("mykey = %#x\n",mykey);
return 0;
}
输出结果
mykey = 0x640516aa
6.3 消息队列的操作
6.3.1 创建消息队列 - msgget() 函数
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int msgget(key_t key, int msgflg);
功能:创建一个消息队列,得到消息队列的id;
参数:
key:键值,唯一的键值确定唯一的消息队列方法
1:任意指定一个数
2:使用ftok函数获取键值;
msgflg: 消息队列的访问权限,一般设置为 IPC_CREAT|IPC_EXCL|0777 或者 IPC_CREAT 0777
返回值:
成功:消息队列的id;
失败:返回 -1;
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
key_t mykey;
if ((mykey = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("mykey = %#x\n", mykey);
int msqid;
if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
{
perror("fail to msgget");
exit(1);
}
printf("msqid = %d\n", msqid);
return 0;
}
输出结果
mykey = 0x640516aa
msqid = 0
6.3.2 发送信息 - msgsnd() 函数
#include<sys/msg. h>
int msgsnd(int msgid, const void *msgp,size_t msgsz, int msgflg);
功能:
将新消息添加到消息队列;
参数:
msgid:消息队列的标识符;
msgp:待发送消息结构体的地址,需要写入的数据,自己定义结构体;
struct struct_name{
long mtype; // 消息编号,必须大于0
char mtext[128]; // 消息正文,可以定义多个成员
}
msgsz: 消息正文的字节数,不包括消息编号的长度;
msgflg: 函数的控制属性
0: msgsnd,调用阻塞直到条件满足为止。
IPC_NOWAIT: 非阻塞,若消息没有立即发送则调用该函数的进程会立即返回。
返回值:
成功: 0;
失败: 返回-1。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
// 第一个位置必须是long,代表消息类型
long msg_type;
char msg_text[128]; // 消息正文,可以多个成员并且类型也可以随意
} MSG;
// 定义消息正文的大小
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))
int main(int argc, char const *argv[])
{
key_t mykey;
if ((mykey = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("mykey = %#x\n", mykey);
int msqid;
if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
{
perror("fail to msgget");
exit(1);
}
printf("msqid = %d\n", msqid);
system("ipcs -q");
// 使用msgsnd向消息队列中写入数据
MSG msg1 = {1, "hello world"};
MSG msg2 = {1, "i love c"};
MSG msg3 = {1, "i love hello kitty"};
if ((msgsnd(msqid, &msg1, MSGTEXT_SIZE, 0)) == -1)
{
perror("fail to send message");
}
system("ipcs -q");
if ((msgsnd(msqid, &msg2, MSGTEXT_SIZE, 0)) == -1)
{
perror("fail to send message");
}
if ((msgsnd(msqid, &msg3, MSGTEXT_SIZE, 0)) == -1)
{
perror("fail to send message");
}
system("ipcs -q");
return 0;
}
输出结果
mykey = 0x640516aa
msqid = 2
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x640516aa 2 spider 666 0 0
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x640516aa 2 spider 666 128 1
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x640516aa 2 spider 666 384 3
注意:消息队列结束后,消息仍然存在于消息队列中,而如果是管道,就会消失。
6.3.3 接收数据 - msgrev() 函数
#include <sys/msg.h>
ssize_t msgrcv(int msgid, void *msgp,size_t msgsz, long msgtyp, int msgflg)
功能: 从标识符为msgid的消息队列中接收一个消息。一旦接收消息成功,则消息在消息队列中被删除;
参数:
msgid: 消息队列的标识符,代表要从哪个消息列中获取消息。
msgp: 存放消息结构体的地址。
msgsz: 消息正文的字节数。
msgtyp: 消息的类型、可以有以下几种类型
msgtyp = 0: 返回队列中的第一个消息
msgtyp > 0: 返回队列中消息类型为msgtyp的消息
msgtyp < 0: 返回队列中消息类型值小于或等于 msgtyp绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。
注意:
若消息队列中有多种类型的消息,msscv,获取消息的时候按消息类型获取,不是先进先出的。
在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消息,即先进先出原则。
msgflg:函数的控制属性
0: msgrev 调用阻塞直到接收消息成功为止。
MSG_NOERROR:若返回的消息字节数比 nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程。
IPC NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1.
返回值:
成功: 返回读取消息的长度;
失败: 返回-1
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
// 第一个位置必须是long,代表消息类型
long msg_type;
char msg_text[128]; // 消息正文,可以多个成员并且类型也可以随意
} MSG;
// 定义消息正文的大小
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))
int main(int argc, char const *argv[])
{
key_t mykey;
if ((mykey = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("mykey = %#x\n", mykey);
int msqid;
if ((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
{
perror("fail to msgget");
exit(1);
}
printf("msqid = %d\n", msqid);
system("ipcs -q");
// // 使用msgsnd向消息队列中写入数据
// MSG msg1 = {1, "hello world"};
// MSG msg2 = {1, "i love c"};
// MSG msg3 = {1, "i love hello kitty"};
// if ((msgsnd(msqid, &msg1, MSGTEXT_SIZE, 0)) == -1)
// {
// perror("fail to send message");
// }
// system("ipcs -q");
// if ((msgsnd(msqid, &msg2, MSGTEXT_SIZE, 0)) == -1)
// {
// perror("fail to send message");
// }
// if ((msgsnd(msqid, &msg3, MSGTEXT_SIZE, 0)) == -1)
// {
// perror("fail to send message");
// }
MSG msg;
// 如果第四个数据位0,则按照先进先出的方式读取数据
// 如果第四个数据大于0,则获取当前值的消息类型相同的数据
// 如果第四个参数小于0,则获取当前值的绝对值内消息类型最小的数据
if (msgrcv(msqid, &msg, MSGTEXT_SIZE, 1, 0) == -1)
{
perror("fail to msgrcv");
exit(1);
}
printf("recv_msg = %s\n", msg.msg_text);
system("ipcs -q");
return 0;
}
输出结果
mykey = 0x640516aa
msqid = 2
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x640516aa 2 spider 666 384 3
recv_msg = hello world
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x640516aa 2 spider 666 256 2
6.3.4 消息队列的控制 - msgctl() 函数
#include<sys/msg. h>
int msgetl(int msqid int cmd, struct msqid_ds *buf)
功能: 对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列;
参数:
msqid: 消息队列的标识符
cmd: 函数功能的控制
IPC_RMID: 删除由 msqid 指示的消息队列,将它从系统中删除并破坏相关数据结构;
IPC_STAT: 将 msqid 相关的数据结构中各个元素的当前值存入到由buf指向的结构中;
IPC_SET: 将 msqid 相关的数据结构中的元素设置为由 buf指向的结构中的对应值。
buf: msqid_ds,数据类型的地址,用来存放或更改消息队列的属性
返回值:
成功:返回0
失败:返回-1
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
key_t key;
if ((key = ftok(".", 20)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("key = %#x\n", key);
int msgid;
if ((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
perror("fail to msgget");
exit(1);
}
printf("msgid = %d", msgid);
system("ipcs -q");
// 通过msgctl函数删除消息队列
if (msgctl(msgid, IPC_RMID, NULL) == -1)
{
perror("fail to msgctl");
exit(1);
}
system("ipcs -q");
return 0;
}
输出结果
key = 0x140516aa
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x640516aa 2 spider 666 256 2
0x140516aa 3 spider 777 0 0
--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息
0x640516aa 2 spider 666 256 2
标签:02,IPC,27,队列,msqid,2024,int,消息,include
From: https://www.cnblogs.com/hasaki-yasuo/p/18036428