6.消息队列之IPC对象
1、信号、无名管道和有名管道是最原始的进程通信方式,除此外还有三种通信,称之为IPC对象;
2、IPC对象分为:消息队列、共享内存、信号灯集
3、IPC对象也是在内核空间中开辟区域,每一种ipc对象创建好之后都会将其设置为全局。并且会给其分配一个编号,只要找到唯一的这个编号就可以通信了,所以不相关的进程可以通过ipc对象通信;
4、ipc对象创建好之后,会在当前系统中可见,只要不删除或者不关闭系统,就会一直存在;
6.01消息队列概述
消息队列是消息的链表,存放在内存中,由内核维护
消息队列的特点:
1、消息队列中的消息是有类型的
2、消息队列中的消息是有格式的
3、消息队列可以实现消息随机查询,消息不一定要以先进先出的次序,编程时可以按照消息的类型读取
4、消息队列允许一个或者多个进程向它写入或者读取消息
5、与无名管道、命名管道一样,从消息队列中读出的消息数据会被删除
6、每个消息队列都有消息队列标识,消息队列的标识符在整个系统中是唯一的
7、只有内核重启或者 人工删除消息队列时,该消息队列才会被删除,否则一直存在系统中;
****************在每个系统中消息队列是有限制的**********
每个消息内容最多为8K、
每个消息队列容量最多为16K
系统中消息队列个数最多为1609个
系统中消息个数最多为16384个
System V提供的IPC通信机制需要一个key值,通过key值就可以在系统内获得一个唯一的消息队列的标识符。key值可以通过人为指定,也可以通过ftok函数获得;
ftok函数
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id)
功能:获得项目相关的唯一的ipc键值
参数:pathname(任意一个文件名、路径名或者目录名)因为对应的inode号是唯一的
proj_id:项目id,非0整数,只有8位有效
返回值:成功返回Key,失败-1
ftok实例:如果两个参数一致是不是每次key值都一样呢?
#include<stdio.h>
#include<stdlib.h>
#include<unstd.h>
int main(int argc, char **argv)
{
key_t mykey;
if((ftok(".", 100)) == -1)
{
perror();
exit(1);
}
printf("%#X\n", mykey);
return 0;
}
6.03创建消息队列----msgget
命令ipcs或者ipcs -q可以查看
#include<sys/msg.h>
int msgget(key_t key, int msgflag)
功能:创建一个新的或者打开一个已经存在的消息队列,不同的进程调用此函数,只要用相同的key值就能得到用一个消息队列的标识符;
参数:key:ipc键值
msgflag:标识函数的行为以及消息队列的权限
msgflag的取值:一般设置为:IPC_CREATE | IPC_EXECL | 0777
IPC_CREAT 创建消息队列
IPC_EXECL 检查消息队列是否存在
成功返回消息队列标识符,失败返回-1
实例创建消息队列:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflag);
//执行后ipcs -q可以查看
//ipcrm -a 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 **argv)
{
int msqid=0;
//通过ftok函数获取key值
if((mykey = ftok(".", 100) == -1))
{
exit(-1);
}
printf("%#x\n", mykey);
//通过msgget函数创建一个消息队列
if( (msqid = msgget(mgkey, IPC_CEEAT | 0666) )== -1)
{
exit(-1);
}
system("ipcs -q")
return 0;
//执行后ipcs -q可以查看
//ipcrm -a msqid 就可以删除
}
6.04消息队列----发送消息
#include<sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflag);
功能:将新的消息添加到消息队列
参数:msgid 消息队列的标识符
msgq 待发送消息的结构体地址
msgsz 消息正文的字节数
msgflag 函数的控制属性
0:msgsnd调用阻塞直到条件满足为止
IPC_NOWAIT:若消息没有立即发送,则调用该消息的进程立即返回
返回:成功0 失败-1
消息队列的格式:
typedef struct _msg
{
long mtype; //消息的类型
char mtext[100];//消息正文
。。。。//消息的正文可以有多个成员
}MSG;
消息类型必须是长整型的,而且必须要是结构体类型的第一个成员,类型下面是消息正文,正文可以有多个成员
#defined N 128
#difined MSGTEXT_SIZE sizeof(MSG)-sizeof(long)
typedef struct
{
long msg_type;//消息类型必须在结构体的第一个
char nsg_text[N];//正文
}MSG;
int main(int argc. char **argv)
{
int msqid=0;
//通过ftok函数获取key值
if((mykey = ftok(".", 100) == -1))
{
exit(-1);
}
printf("%#x\n", mykey);
//通过msgget函数创建一个消息队列
if( (msqid = msgget(mgkey, IPC_CEEAT | 0666) )== -1)
{
exit(-1);
}
system("ipcs -q")
MSG msg1={1, "hello world1"};
MSG msg2={4, "hello world2"};
MSG msg3={3, "hello world3"};
MSG msg4={2, "hello world4"};
if(msgsnd(msgid, &msg1, MSGTEXT_SIZE, 0) == -1)
{
}
if(msgsnd(msgid, &msg2, MSGTEXT_SIZE, 0) == -1)
{
}
if(msgsnd(msgid, &msg3, MSGTEXT_SIZE, 0) == -1)
{
}
if(msgsnd(msgid, &msg4, MSGTEXT_SIZE, 0) == -1)
{
}
system("ipcs -q");
return 0;
//执行后ipcs -q可以查看
//ipcrm -a msqid 就可以删除
}
每执行一次,往里面写一次!
6.05消息队列----接收消息
#include <sys/msg.h>
ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflag);
功能:从标识符为msgqid的消息队列中接收一个消息,一旦接收消息成功,则消息在消息队列中被删除
参数:msgqid:消息队列的标识符,代表要从哪里获取消息
msgp 存放消息结构的地址
msgsz: 消息正文的字节数
msgtyp:消息的类型,可以有一下几种类型:
msgtyp=0; 返回队列种的第一个消息
msgtyp>0;返回队列种的消息类型为msgtyp的消息
msgtyp<0;返回队列种消息类型小于或者等于msgtyp绝对值的消息,如果这种消息由若干个,则取类型最小的消息
注意:1、若消息队列种由多种类型的消息,msgrcv获取消息的时候按照消息类型获取,不是先进先出;2、在获取某类型消息的时候,若丢列种由多条此类型的消息,则获取最先添加的消息;即先进先出的原则;
msgflag:函数的控制属性
0:msgrcv调用阻塞直到接收消息成功为止
MSG_NOERROR:若返回的消息字节数比nbytes字节数多,则消息就会截短到nbytes字节,且不通知消息发送进程
IPC_NOWAIT:调用进程会立即返回,若没有收到消息则立即返回-1
注意:msgrcv是会阻塞的,如果接收指定的消息类型不纯在的时候就会发送阻塞!!!!!!
实例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
defined N 128
typedef struct {
long msg_type;
char msg_text[N];
}MSG;
#defined MSGTEXT_SIZE sizeof(MSG) - sizeof(long)
int main(int argc. char **argv)
{
int msqid=0;
//通过ftok函数获取key值
if((mykey = ftok(".", 100) == -1))
{
exit(-1);
}
printf("%#x\n", mykey);
//通过msgget函数创建一个消息队列
if( (msqid = msgget(mgkey, IPC_CEEAT | 0666) )== -1)
{
exit(-1);
}
system("ipcs -q")
//通过msgrcv接收消息队列种的消息
if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 0, 0) == -1)
{ }
if(msgrcv(msgid, &msg, MSGTEXT_SIZE, 2, 0) == -1)
{ }
printf("recv_msg = %s\n", msg.msg_text);
system("ipcs -q");
return 0;
//执行后ipcs -q可以查看
//ipcrm -a msqid 就可以删除
}
6.05消息队列控制
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:对消息队列进行各种控制,如修改消息队列属性,或者删除消息队列
参数:msqid 消息队列的标识符
cmd 函数功能的控制
buf: msqid_ds数据类型的地址,用来存放或者更改消息队列的属性
cmd:函数功能的控制
IPC_RMID 删除由msqid指示的消息队列,将它从系统中删除并破环相关的数据结构
IPC_STAT 将msqid相关的数据结构中的各个元素的当前值存放到由buf指向的结构中
IPC_SET 将msqid相关的数据结构中的元素设置为由buf指向的结构中的对应值
成功返回0
int main(int argc. char **argv)
{
int msqid=0;
//通过ftok函数获取key值
if((mykey = ftok(".", 100) == -1))
{
exit(-1);
}
//通过msgget函数创建一个消息队列
if( (msqid = msgget(mgkey, IPC_CEEAT | 0666) )== -1)
{
exit(-1);
}
system("ipcs -q");
if(msgctl(msgid, IPC_RMID, null) == -1)
{}
system("ipcs -q");
return 0;
}