目录
? ? ? ?消息队列是一种进程间通信的机制,允许不同进程在系统中传递数据。它们通常由内核维护,并提供了一种异步的通信方式,允许进程通过在队列中发送和接收消息进行通信。
消息队列是消息的链表,存放在内存中,由内核维护
1、消息队列中的消息是有类型的。
2、消息队列中的消息是有格式的。
3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
4、消息队列允许一个或多个进程向它写入或者读取消息。
5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除。 6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一的。
7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在 于系统中。
在 ubuntu 12.04 中消息队列限制值如下:
每个消息内容最多为 8K 字节。
每个消息队列容量最多为 16K 字节 系统中消息队列个数最多为 1609 个。
系统中消息个数最多为 16384 个。
System V 提供的 IPC 通信机制需要一个 key 值,通过 key 值就可在系统内获得一个唯一的消息队列标识符。 key 值可以是人为指定的,也可以通过 ftok 函数获得。
ftok函数
ftok()函数是在 Linux/Unix 操作系统中用于创建一个唯一的 key 的函数。
消息队列、信号量等进程间通信方式都需要一个唯一的标识符来标识自己。`ftok()` 函数可以将一个普通文件的路径名和一个整数 ID 转换为一个 key(即 IPC 标识符),返回值为一个 long 类型的 key 值。
ftok()的函数原型为:
#include <sys/types.h>?
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
?功能:通过文件名和目标值共同创造一个键值并返回值。获得项目相关的唯一的 IPC 键值。
参数:
?pathname:任意一个文件名(文件名或者目录名)路径名
?proj_id:目标值,范围一般是0~127,项目 ID,非 0 整数(只有低 8 位有效)。
返回值:?成功:返回 key 的键值。?失败:‐1
?如果使用ftok函数获取键值,得到的键值是由ftok的第一个?参数对应文件的信息和第二个参数一起决定的。
其中,参数 `pathname` 是指文件的路径名,`proj_id` 是一个用户自定义的整数 ID。
`ftok()` 函数会根据给定的文件路径名和整数 ID 生成一个唯一的 key。在多个进程中使用同一个 key 可以让它们访问同一个 IPC 对象,例如同一个消息队列或同一个信号量集合。
需要注意的是,如果文件不存在或者无法访问,或者多个进程使用不同的文件路径名和/或整数 ID 调用 `ftok()` 生成相同的 key,则可能导致 IPC 系统中的冲突。因此,在使用 `ftok()` 函数时需要选择一个合适的文件路径名和整数 ID,并确保所有进程都使用相同的参数来调用该函数。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main(int argc, char const *argv[])
{
//使用ftok函数获取键值
//只要保证ftok的第一个参数对应的文件和第二个参数值相同,则不管程序运行多少遍或者多少个进程或者键值
//键值一定都是唯一的
key_t mykey;
if((mykey = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("key = %#x\n", mykey);
return 0;
}
man 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
使用shell命令操作消息队列:
查看消息队列 :?ipcs ‐q ?
删除消息队列 :?ipcrm ‐q msqid?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(int argc, char const *argv[])
{
//通过ftok函数获取ipc键值
key_t mykey;
if((mykey = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
printf("mykey = %#x\n", mykey);
//通过msgget函数创建一个消息队列
int msqid;
if((msqid = msgget(mykey, IPC_CREAT | 0666)) == -1)
{
perror("fail to msgget");
exit(1);
}
printf("msqid = %d\n", msqid);
system("ipcs -q");
return 0;
}
man msgsnd
? ?????#include <sys/types.h>
? ? ? ?#include <sys/ipc.h>
? ? ? ?#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); .
功能:向指定的消息队列发送数据(写操作)
?参数:
?msqid:消息队列的id
?msgp:要写入的数据,需要自己定义结构体
struct struct_name{
?long mtype; //消息的编号,必须大于0
?char mtext[128]; //消息正文,可以定义多个成员
?}
msgsz:消息正文的大小,不包括消息的编号长度
msgflg:标志位
?0 阻塞
?IPC_NOWAIT 非阻塞
返回值:
?成功:0
失败:‐1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define N 128
typedef struct{
long msg_type; //消息类型,必须在结构体的第一个位置并且类型必须是long
char msg_text[N]; //消息正文,也可以有多个成员并且类型也可以是任意
}MSG;
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))//此处表示消息正文的大小
int main(int argc, char const *argv[])
{
//使用ftok函数获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
//使用msgget函数创建一个消息队列
int msgid;
if((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
perror("fail to msgget");
exit(1);
}
system("ipcs -q");
//使用msgsnd函数向消息队列中发送数据(写操作)
MSG msg1 = {1, "hello world"};
MSG msg2 = {2, "nihao beijing"};
MSG msg3 = {2, "hello kitty"};
MSG msg4 = {3, "welcome to china"};
if(msgsnd(msgid, &msg1, MSGTEXT_SIZE, 0) == -1)
{
perror("fail to msgsnd");
exit(1);
}
if(msgsnd(msgid, &msg2, MSGTEXT_SIZE, 0) == -1)
{
perror("fail to msgsnd");
exit(1);
}
if(msgsnd(msgid, &msg3, MSGTEXT_SIZE, 0) == -1)
{
perror("fail to msgsnd");
exit(1);
}
if(msgsnd(msgid, &msg4, MSGTEXT_SIZE, 0) == -1)
{
perror("fail to msgsnd");
exit(1);
}
system("ipcs -q");
return 0;
}
? ?????#include <sys/types.h>
? ? ? ?#include <sys/ipc.h>
? ? ? ?#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, 5 long msgtyp, int msgflg);
?功能:从消息队列中接收数据(读操作),接收的数据会从消息队列中删除
?参数:
?msqid:消息队列id
?msgp:保存接收到的数据的结构体
?struct struct_name{
?long mtype; //消息的编号,必须大于0
?char mtext[128]; //消息正文,可以定义多个成员
?}
msgsz:消息正文的大小
?msgtyp:设置要接收哪个消息
?msgtyp = 0 按照写入消息队列的顺序依次读取
?msgtyp > 0 只读取消息队列中消息编号为当前参数的第一个消息
msgtyp < 0 只读取消息队列中小于等于当前参数的绝对中内最小的第一个消息
?msgflg:标志位
?0 阻塞
?IPC_NOWAIT 非阻塞
?返回值:
?成功:接收到的消息正文的长度
?失败:‐1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define N 128
typedef struct{
long msg_type;
char msg_text[N];
}MSG;
#define MSGTEXT_SIZE (sizeof(MSG) - sizeof(long))
int main(int argc, char const *argv[])
{
//使用ftok函数获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
//使用msgget函数创建一个消息队列
int msgid;
if((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
perror("fail to msgget");
exit(1);
}
system("ipcs -q");
//通过msgrcv函数接收消息队列中的信息(读操作)
//注意:如果没有第四个参数指定的消息时,msgrcv函数会阻塞等待
MSG msg;
//如果第四个参数为0,则按照先进先出的方式读取数据
//if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 0, 0) == -1)
//如果第四个参数为>0,则获取当前值得消息类型的数据
//if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 2, 0) == -1)
//如果第四个参数为<0,则获取当前值得绝对值内消息类型最小的数据
if(msgrcv(msgid, &msg, MSGTEXT_SIZE, -3, 0) == -1)
{
perror("fail to msgrcv");
exit(1);
}
printf("recv_msg = %s\n", msg.msg_text);
system("ipcs -q");
return 0;
}
man msgctl
? ? ?#include <sys/types.h>
? ? ? ?#include <sys/ipc.h>
? ? ? ?#include <sys/msg.h>
? ? ? ?int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:设置或者获取消息队列的信息
?参数:
msqid:指定的消息队列的id
?cmd:具体操作的指令
?IPC_SET :设置属性
?IPC_STAT: 获取属性
?IPC_RMID: 删除消息队列
msqid_ds:设置或者获取消息队列的属性
?返回值:
?成功:0 ?失败:‐1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int main(int argc, char const *argv[])
{
//使用ftok函数获取键值
key_t key;
if((key = ftok(".", 100)) == -1)
{
perror("fail to ftok");
exit(1);
}
//使用msgget函数创建一个消息队列
int msgid;
if((msgid = msgget(key, IPC_CREAT | 0777)) == -1)
{
perror("fail to msgget");
exit(1);
}
printf("msgid = %d\n", msgid);
system("ipcs -q");
//通过msgctl函数删除消息队列
if(msgctl(msgid, IPC_RMID, NULL) == -1)
{
perror("fail to msgctl");
exit(1);
}
system("ipcs -q");
return 0;
}