1、消息队列的特点
(1)消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.
(2)消息队列允许一个或多个进程向它写入与读取消息.
(3)管道和命名管道都是通信数据都是先进先出的原则。
(4)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。
目前主要有两种类型的消息队列:POSIX消息队列以及System V消息队列,System V消息队列目前被大量使用。System V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。
二、消息队列的创建
#include
#include
#include
int msgget(key_t, key, int msgflg);
参数key为消息队列的键值,通常是一个长整型,可以设置为任何整数值。该参数可以用户直接指定,也可以调用ftok函数来生成,如果直接设为IPC_RPIVATE,表示总是创建新的消息队列。
参数msgflg用来建立消息队列并设定存取权限,例如IPC_CREAT|0666,它表示创建一个当前用户,用户组以及其他用户有读写权限的消息队列。如果加上IPC_EXCL,则表示只有在指定的消息队列不存在时,才会创建新的消息队列。函数执行成功后,返回消息队列的标识符,否则返回-1.
ftok函数的一般形式如下:
#include
#include
key_t ftok(const char *pathname,int proj_id);
参数pathname用来指定进程有存取权限的一个路径。
参数proj_id用来指定某个特定字符。函数执行成功后,返回一个消息队列的键值,否则返回-1.
//使用msgget函数来创建一个消息队列
#include
#include
#include
#include
#include
#include
int main()
{
int qid;
key_t key;
key=ftok("/home/", 'a'); //生成消息队列的键值
if (key<0)
{
perror("ftok error");
exit(1);
}
qid =msgget(key,IPC_CREAT|0666); //创建一个消息队列
if (qid<0)
{
perror("msgget error");
exit(1);
}
else
{
printf("Done!\n");
}
return 0;
}
通过ipcs -q 可以看到创建的消息队列的键值为 0X61020005,标识符为65536.
三、消息队列的控制
#include
#include
#include
int msgctl(int msgid, int command, struct msgid_ds *buf);
参数msqid为消息队列的标识符;
struct msqid_ds
{
struct ipc_perm msg_perm; //存取权限
struct msg *msg_first; //消息队列头指针
struct msg *msg_last; //消息队列尾指针
__kernel_time_t msg_stime; //最后一次插入消息队列消息的时间
__kernel_time_t msg_rtime; //最后一次接收消息即删除队列中一个消息的时间
__kernel_time_t msg_ctime; //最后一次修改的时间
struct wait_queue *wwait; //发送消息等待进程队列
struct wait_queue *rwait;
unsigned short msg_cbytes; //当前队列的字节数
unsigned short msg_qnum; //消息队列中的消息个数
unsigned short msg_qbytes; //队列的最大字节数
__kernel_ipc_pid_t msg_lspid; //最后一次消息发送进程的pid
__kernel_ipc_pid_t msg_lrpid; //最后一次消息发送进程的pid
};
//清除创建的消息队列
#include
#include
#include
#include
#include
#include
int main()
{
int qid;
int status;
printf("请输入需要删除的消息队列的标识符:");
scanf("%d",&qid);
status=msgctl(qid, IPC_RMID, NULL); //删除指定的消息队列
if(status<0)
{
perror("msgctl error");
exit(1);
}
printf("removed!\n");
return 0;
}
四、消息队列的读写
1、msgsnd函数
该函数用来把消息添加到消息队列中。
#include
#include
#include
int msgsend(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
参数msqid为消息队列的标识符,要写入的消息存储在参数msgp所指向的msgbuf结构中,消息的 大小由参数msgsz决定,参数msgflg用来设置消息队列没有足够空间时msgsnd函数执行的动作,例如是否等待。
msgbuf结构用来包含一个消息,其定义如下:
struct msgbuf
{
long mtype; //消息的类型
char mtext[];//消息的内容
};
当进程调用msgsnd函数写入一个消息时,系统会首先检查进程对该消息队列是否有写权限,接着查看消息的长度是否超过系统允许的范围,以及消息队列的剩余空间情况等。如果条件都符合,系统会为消息分配消息头和消息数据区,将消息从用户空间复制到消息数据区后,并链入消息队列的尾部。同时,在消息头中填写消息的类型、大小以及指向消息数据区的指针等。
如果msgsnd函数被阻塞,则在下面某个条件满足时解除阻塞。
(1)消息队列中有容纳要写入消息的空间。
(2)消息队列被删除。
(3)进程被信号中断。
msgrcv函数用来从消息队列中读取(接收)一个消息。
#include
#include
#include
int msgrcv(int msqid,struct msgbuf *msgp,int msgsz,long msgtyp,int msgflg);
参数msqid为
为消息队列的标识符,消息返回后将会存储在参数msgp指向的msgbuf结构中,该结构mtext成员的长度由参数msgsz决定,参数msgtyp为请求读取消息的类型,有如下3种情况:
(1)msgtyp=0:返回消息队列中的第一个消息。
(2)msgtyp>0:返回消息队列中该类型的第一个消息。
(3)msgtyp<0:在类型小于等于msgtyp绝对值的所有消息中,返回类型值最小的第一个消息。
当进程调用msgrcv函数读取一个消息时,如果返回消息小于等于用户请求,系统会将消息复制到用户空间,然后从消息队列中删除该消息,并唤醒阻塞的写入进程;如果消息大于用户请求,则返回错误信息。
如果msgrcv函数被阻塞,则在下面某个条件满足时解除阻塞:
(1)消息队列中有了满足条件的消息。
(2)消息队列被删除。
(3)进程被信号中断。
示例:
发送消息程序:
//实现两个进程间的消息传递,消息发送程序
#include
#include
#include
#include
#include
#include
#include
#define MSG_SIZE 128
struct msgbuf //定义消息结构
{
long mtype; //消息类型
char mtext[MSG_SIZE];//消息的内容
};
int main()
{
int qid;
key_t key;
int ret;
struct msgbuf buf; //消息缓冲区
key=ftok("/home", 'a'); //生成消息队列的键值
if (key<0)
{
perror("ftok error");
exit(1);
}
qid=msgget(key, IPC_CREAT|0666); //创建一个消息队列
if (qid<0)
{
perror("msgget error");
exit(1);
}
while (1)
{
printf("input the message:");
fgets(buf.mtext,MSG_SIZE,stdin); //从键盘输入消息的内容
if (strncmp(buf.mtext, "exit",4)==0) //如果键盘输入exit,退出循环
{
buf.mtype=getpid();
ret=msgsnd(qid, &buf, MSG_SIZE, 0);
break;
}
buf.mtype=getpid(); //消息的类型,这里设置为当前进程的标识符
ret=msgsnd(qid, &buf, MSG_SIZE, 0); //向消息队列中发送一个消息
if (ret<0)
{
perror("msgsnd error");
exit(1);
}
else
{
printf("send!\n");
}
}
return 0;
}
接收端:
//实现两个进程间的消息传递,编写消息接收程序
#include
#include
#include
#include
#include
#include
#include
#define MSG_SIZE 128
struct msgbuf //定义消息结构
{
long mtype; //消息类型
char mtext[MSG_SIZE];//消息的内容
};
int main()
{
int qid;
key_t key;
int ret;
struct msgbuf buf;
key=ftok("/home", 'a');
if (key<0)
{
printf("ftok error");
exit(1);
}
qid=msgget(key,IPC_EXCL|0666); //打开消息队列
if (qid<0)
{
perror("msgget error");
exit(1);
}
while (1)
{
memset(&buf, 0, sizeof(buf));
ret=msgrcv(qid, &buf, MSG_SIZE, 0, 0); //读取消息队列中的一个的消息
if (ret<0)
{
perror("msgrcv error");
exit(1);
}
else
{
if (strncmp(buf.mtext, "exit",4)==0)
{
break;
}
printf("received message:\n");
printf("type=%ld,length=%ld,text:%s\n",buf.mtype,strlen(buf.mtext)-1,buf.mtext); //输入消息
}
}
return 0;
}