消息队列
消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
特点
(1)消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级(链表存放的为结构体)。
(2)消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
(3)消息队列可以实现消息的随机查询(链表的特性),也可以按消息的类型读取。
(4)与无名管道、有名管道一样,从消息队列中读出消息,消息队列中数据会被删除。
常用api
int msgget(key_t key, int flag);
// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 添加消息:成功返回0,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
// 控制消息队列:成功返回0,失败返回-1
key_t ftok( const char * fname, int id )
//系统IPC键值的格式转换函数
msgget()
创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
参数:
key :消息队列的键值
多个进程可以通过它访问同一个消息队列,如果key为IPC_PRIVATE 意思就是创建一个只能被创建进程读写的消息队列;
如果不是IPC_PRIVATE .则我们可以指定一个值0x1234,还可以用ftok()函数来获得一个唯一的键值。
flag : 创建消息队列的创建方式或权限。
IPC_CREAT :(需要指定权限:IPC_CREAT|0600)
如果消息队列对象不存在,则创建消息队列,否则则进行打开操作;
IPC_EXCL:
和IPC_CREAT 一起使用(用”|”连接),如果消息对象不存在则创建,否则产生一个错误并返回。
msgsnd()
添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
//例
msgend(msgId,&sndBuf,strlen(sndBuf.mtext),0);
参数:
msqid:消息队列的ID
ptr:发送给队列的消息。写入的数据,指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */ //第一个字段必须是long类型
char mtext[1]; /* 消息文本 */
};
size:ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说size是不包括长整型消息类型成员变量的长度。
flag: 为0表示在消息队列满或者空时都等待,直到队列中出现满足要求的消息为止(阻塞方式),设置IPC_NOWAIT 表示非阻塞方式
msgrcv()
读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
//例
msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);
参数
msqid:消息队列的ID
ptr:写入的数据,指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */
char mtext[1]; /* 消息文本 */
};
type:消息类型
msgtyp=0 则返回队列的最早的一个消息。
msgtyp>0,则返回其类型为msgtyp的第一个消息。
msgtyp<0,则返回其类型小于或等于mtype(在msgbuf结构体中的mtype)参数的绝对值的最小的一个消息。
操作系统提供的消息队列,确保同一个类型的消息是先进先出的,整个队列的消息可能不是先进先出的。
size:数据的长度(同msgsnd)
flag:为0表示在消息队列满或者空时都等待,直到队列中出现满足要求的消息为止(阻塞方式),设置IPC_NOWAIT 表示非阻塞方式
示例:
//send
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */
char mtext[128]; /* 消息文本 */
};
int main()
{
// int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf sndBuf={888,"this is message from snd"};
int msgId=msgget(0x1234,IPC_CREAT|0777);
if(msgId==-1){
printf("get msg failed");
}
msgsnd(msgId,&sndBuf,strlen(sndBuf.mtext),0);
printf("send over\n");
//写入一个消息类型为888的数据
return 0;
}
//get
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
struct msgbuf readBuf;
int msgId=msgget(0x1234,IPC_CREAT|0777);
if(msgId==-1){
printf("get msg failed");
}
msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);
//把数据类型为888的数据读出来
printf("this is get :%s\n",readBuf.mtext);
printf("read over\n");
return 0;
}
先运行get
再运行send
ftok()
系统IPC键值的格式转换函数,系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。
函数原型
key_t ftok( const char * fname, int id );
参数
fname:就是你指定的文件名(已经存在的文件名),一般使用当前目录,如
key_t key;
key = ftok(".", 1);
“ . ” 表示当前文件 ls-a 可以查看
这样就是将fname设为当前目录。
id:是子序号。虽然是int类型,但是只使用8bits(1-255)。
如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。
查询文件索引节点号的方法是: ls -ia
msgctl()
内核中的队列过多时,可以用该函数来清除队列
函数原型
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数
msqid:消息队列的ID
cmd:函数要对消息队列进行的操作,它可以是:
IPC_STAT
取出系统保存的消息队列的msqid_ds 数据,并将其存入参数buf 指向的msqid_ds 结构
中。
IPC_SET
设定消息队列的msqid_ds 数据中的msg_perm 成员。设定的值由buf 指向的msqid_ds
结构给出
IPC_RMID
将队列从系统内核中删除(用来删除队列)
buf:队列内容(一般写NULL)
msgctl(msgid,IPC_RMID,NULL);//删除队列
关于ftok和msgctl的示例
//send
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf sndBuf={888,"this is message from send"};
struct msgbuf readBuf;
key_t key;
key=ftok(".",'a');//获取键值
printf("key=%x\n",key);
int msgId=msgget(key,IPC_CREAT|0777);//在内核中打开或建立键值为key的,权限为0777的消息队列
if(msgId==-1){
printf("get msg failed");
}
msgsnd(msgId,&sndBuf,strlen(sndBuf.mtext),0);//往队列id为msgId的队列写入sendbuf(类型为888)数据
printf("send over\n");
msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),988,0);//从队列中获取988类型的数据,如果队列中未出现988类型的数据,则程序阻塞在这里
printf("this is from get:%s\n",readBuf.mtext);
msgctl(msgId,IPC_RMID,NULL);//将队列从系统内核中删除
return 0;
}
//get
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf readBuf;
key_t key;
key=ftok(".",'a');//获取键值
printf("key=%x\n",key);
int msgId=msgget(key,IPC_CREAT|0777);//在内核中打开或建立键值为key的,权限为0777的消息队列
if(msgId==-1){
printf("get msg failed");
}
msgrcv(msgId,(void *)&readBuf,sizeof(readBuf.mtext),888,0);//从队列中获取888类型的数据,如果队列中未出现888类型的数据,则程序阻塞在这里
printf("this is get :%s \n",readBuf.mtext);
printf("read over\n");
struct msgbuf sendbuf={988,"thank you"};
msgsnd(msgId,&sendbuf,strlen(sendbuf.mtext),0);//往队列id为msgid的队列写入sendbuf(类型为988)数据
msgctl(msgId,IPC_RMID,NULL);//将队列从系统内核中删除
return 0;
}
IPCS:报告进程间通信状态
ipcs -q查看消息队列,-m查看内存,-s查看信号量 -a查看上述所有信息
IPCRM:删除
ipcrm -M shmkey 移除用shmkey创建的共享内存段 ipcrm -m shmid 移除用shmid标识的共享内存段
ipcrm -Q msgkey 移除用msqkey创建的消息队列 ipcrm -q msqid 移除用msqid标识的消息队列
ipcrm -S semkey 移除用semkey创建的信号 ipcrm -s semid 移除用semid标识的信号