系列文章目录
文章一: Linux—进程通信(一)—进程通信概述
文章二: Linux—进程通信(二)—管道
文章三: Linux—进程通信(三)—信号通信
文章四: Linux—进程通信(四)—IPC通信之共享内存
文章五: Linux—进程通信(五)—IPC通信之消息队列
文章六: Linux—进程通信(六)—IPC通信之信号灯
文章目录
前言
本文主要讲解IPC通信对象中的消息队列
一、消息队列
1.什么是消息队列?
消息队列是IPC通信的一个对象,存在于内核空间。消息队列的特别在于它是一个链式队列,即数据结构中的链表队列。
它本质上还是一个队列,因此读取了消息队列的内容后,该内容会从消息队列中删除。
2.消息队列的内容
一个消息队列包含下面的内容:
1.data:数据
2.length:数据长度
3.type:数据的类型
4.指针:链表的结点
3.消息队列的函数和文件I/O对比
文件I/O | 消息队列 |
open | msgget |
read | msgrcv |
write | msgsnd |
close | msgctl |
二、消息队列的使用
1.消息队列函数
创建消息队列
函数原型:
int msgget(key_t key,int flag);
所需头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数功能:
在内核空间中创建消息队列
函数参数:
1.key:和消息队列的key值
2.flag:消息队列的权限
函数返回值:
1.成功返回消息队列的ID,即消息队列的“文件描述符”
2.失败后返回-1
例子1:创建消息队列
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int main()
{
int msgid;
msgid=shmget(IPC_PRIVATE,128,0777);
if(msgid<0)
{
printf("create message queue failure\n");
return -1;
}
printf("create message queue success shmid=%d\n",msgid);
system("ipcs -q");
return 0;
}
执行结果:
删除消息队列的函数
函数原型:
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
所需头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数功能:
将内核空间中的消息队列删除,释放空间
函数参数:
1.msqid:消息队列的ID
2.cmd:
(1)IPC_STAT:读取该消息队列的属性,并将其保存到buf指向的缓存区
(2)IPC_SET:设置消息队列的属性,这个值取自buf
(3)IPC_RMID:从系统中删除消息队列
函数返回值:
1.成功返回0
2.失败返回-1
例子2:应用msgctl函数
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
int main()
{
int msgid;
msgid=shmget(IPC_PRIVATE,128,0777);
if(msgid<0)
{
printf("create message queue failure\n");
return -1;
}
printf("create message queue success shmid=%d\n",msgid);
system("ipcs -q");
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
消息队列的发送函数
函数原型:
int msgsnd(int msqid,const void *msgp,size_t size,int flag);
所需头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数功能:
向消息队列中写入信息。
函数参数:
1.msqid:消息队列的ID
2.msgp:指向消息的指针,常用的消息结构如下:
struct msgbuf
{
long mtypes; //消息类型
char mtext[N] //消息正文
};
3.size:发送消息的正文的字节数
4.flag:
(1)IPC_NOWAIT:消息没有发送完成函数也会立即返回
(2)0:直到消息发送完成函数才返回,阻塞
函数返回值:
1.成功返回0
2.失败返回-1
消息队列的接收函数
函数原型:
int msgrcv(int msqid,void *msgp,size_t size,long msgtype,int flag);
所需头文件:
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
函数功能:
从消息队列中接收数据。
函数参数:
1.msqid:消息队列的ID
2.msgp:接收消息的指针,指向接收消息的缓存区。
常用的消息结构如下:
struct msgbuf
{
long mtypes; //消息类型
char mtext[N] //消息正文
};
3.size:要接收消息的字节数
4.msgtype:
(1)0:接收消息队列的第一个消息。
(2)大于0:接收消息队列中第一个类型值为msgtype的消息。
(3)小于0:接收消息队列中类型值不大于msgtype的绝对值且类型值又最小的消息。
5.flag:
(1)IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG。
(2)0:若无消息会一直阻塞
函数返回值:
1.成功返回接收到的消息的长度。
2.失败返回-1。
例子3:消息队列的读写函数实例
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
struct msgbuf sendbuf,recvbuf;
msgid=shmget(IPC_PRIVATE,128,0777);
if(msgid<0)
{
printf("create message queue failure\n");
return -1;
}
printf("create message queue success shmid=%d\n",msgid);
system("ipcs -q");
sandbuf.type=100;
printf("please input message:\n");
fgets(sendbuf.voltage,124,stdin);
msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
memset(recvbuf.voltage,0,124);
readret=msgrcv(msgid,(void *)&recvbuf,124,100,0);
printf("recv:%s\n",recvbuf.voltage);
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
执行结果:
在键盘中输入一段字符,接着会打印出一样的字符。
三、基于消息队列的无亲缘关系进程间通信
进程1用来发送数据
进程1的C语言代码:
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
int key;
struct msgbuf sendbuf,recvbuf;
key=ftok("./a.c","a");
if(key<0)
{
printf("create key failure\n");
return -2;
}
msgid=shmget(key,128,IPC_CREAT|0777);
if(msgid<0)
{
printf("create message queue failure\n");
return -1;
}
printf("create message queue success shmid=%d\n",msgid);
system("ipcs -q");
sandbuf.type=100;
while(1)
{
memset(sendbuf.voltage,0,124);
printf("please input message:\n");
fgets(sendbuf.voltage,124,stdin);
msgsnd(msgid,(void *)&sendbuf,strlen(sendbuf.voltage),0);
}
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
进程2用来接收数据
进程2的C语言代码:
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
struct msgbuf
{
long type;
char voltage[124];
char ID[4];
};
int main()
{
int msgid;
int readret;
int key;
struct msgbuf sendbuf,recvbuf;
key=ftok("./a.c","a");
if(key<0)
{
printf("create key failure\n");
return -2;
}
msgid=shmget(key,128,IPC_CREAT|0777);
if(msgid<0)
{
printf("create message queue failure\n");
return -1;
}
printf("create message queue success shmid=%d\n",msgid);
system("ipcs -q");
while(1)
{
memset(recvbuf.voltage,0,124);
readret=msgrcv(msgid,(void *)&recvbuf,124,100,0);
printf("recv:%s\n",recvbuf.voltage);
}
msgctl(msgid,IPC_RMID,NULL);
system("ipcs -q");
return 0;
}
执行结果:
进程1发送数据,进程2会立刻将接收到的该数据打印出来。如此循环往复。
总结
对于fgets有疑问的读者可以参考下面的文章:
Linux----标准IO文件操作函数总结