消息队列是systemV风格中一种进程间通信的方式,它提供了一种从一个进程向另一个进程发送一个数据块的方法。他在Linux下是以双向链表(list_head)的形式实现,可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。
可以在内核中的/include/linux/msg.h路径下找到它的定义:
struct msg_queue {
struct kern_ipc_perm q_perm;/* systemV的IPC对象描述结构 */
time_t q_stime; /* 最后一次发送消息时间 */
time_t q_rtime; /* 最后一次接收消息时间 */
time_t q_ctime; /* 最后一次修改消息时间 */
unsigned long q_cbytes; /*当前队列上的字节数 */
unsigned long q_qnum; /* 队列中的消息数 */
unsigned long q_qbytes; /* 队列上的最大字节数 */
pid_t q_lspid; /* 最后一次发送消息的进程的PID */
pid_t q_lrpid; /* 最后一次接受消息的进程的PID */
/*三个双向链表*/
struct list_head q_messages;/* 存储消息的双向链表 */
struct list_head q_receivers;/* 存储消息接收者的双向链表 */
struct list_head q_senders;/* 存储消息发送者的双向链表*/
};
同时在用户空间中我的ubuntu也给出了用户调用的消息队列的描述信息结构体:
int msgget(key_t key, int msgflg);
参数:
key:由ftok()函数生成的消息队列文件的文件号。
msflg:msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。
IPC_CREAT值,若没有该队列,则创建一个并返回新标识符;若已存在,则返回原标识符。
IPC_EXCL值,若没有该队列,则返回-1;若已存在,则返回0。
msgrcv从队列中取用消息:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgsnd将数据放到消息队列中:
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数:
msqid:消息队列的标识码;
msgp:是一个指向消息缓冲区的指针,它用来存储发送和接收的消息,以一个长整型开头的结构体,接收函数以此确定消息的类型。该结构为用户自己定义例如:
struct my_message{
long int message_type;
/* The data you wish to transfer*/
};
msgsz:消息所占的长度,特指消息的长度,开头的长整型不计算在内;
msgtyp:从消息队列中读取消息的形态,如果值为零,则表示读取消息队列全部内容;
msgflg:⽤来指明核⼼程序在队列没有数据的情况下所应采取的⾏动。如果msgflg和常数IPC_NOWAIT合⽤,则在msgsnd()执⾏时若是消息队列已满,则msgsnd()将不会阻塞,⽽会⽴即返回-1,如果执⾏的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。
3.设置消息队列的属性
int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
参数:
对消息队列msgqid进行cmd操作,cmd操作类型有以下三种,
IPC_STAT:该命令用来获取msqid_ds的数据结构,把它存放到buf指定的地址空间;
IPC_SET:设置消息队列的属性,并存放在buf内;
IPC_RMID:从内核中删除msgqid标识的消息队列。
comm.h
------
#ifndef __COMM__
#define __COMM__
#include
#include
#include
#include
#include
#include
#include
#define __MSG_SIZE__ 1024
#define FILEPATH "/tmp/.msg"
#define ID 0
extern const int g_ser_send_type;//server
extern const int g_cli_send_type;//client
typedef struct _msginfo{
long mtype;
char mtext[__MSG_SIZE__];
}msginfo;
void print_log(char *);
#endif
comm.c
------
#include "comm.h"
const int g_ser_send_type=1;//server
const int g_cli_send_type=2;//client
void print_log(char *msg)
{
printf("%s[%d] : %s\n", __FUNCTION__,__LINE__,msg);
}
msg_server.h
------------
#ifndef _MSG_SERVER_
#define _MSG_SERVER_
#include"comm.h"
int msg_server_start();
int msg_server_end(int);
#endif
msg_server.c
------------
#include "msg_server.h"
int _msg_id = -1;
int msg_server_start()
{
key_t _key = ftok(FILEPATH,ID);//创建键值
if( _key < 0 ){
print_log("get key id error");
return 1;
}
msginfo _ser_info;
_msg_id = msgget(_key, IPC_CREAT); //获取信号队列ID
if( _msg_id < 0 ){
print_log("msg_server get key id failed\n");
return 1;
}
while(1){
// sleep(10);
if( msgrcv(_msg_id, &_ser_info, sizeof(_ser_info), g_cli_send_type, 0) == -1 ){
print_log("msg rcv error");
return 1;
}
printf("client :> %s\n",_ser_info.mtext);
printf("server :>");
memset(_ser_info.mtext, '\0', sizeof(_ser_info.mtext));
fgets(_ser_info.mtext, __MSG_SIZE__, stdin);
if(strncasecmp(_ser_info.mtext, "quit", 4) == 0){
printf("server bye!\n");
break;
}
_ser_info.mtype = g_ser_send_type;
if( msgsnd(_msg_id, &_ser_info, __MSG_SIZE__, 0) == -1 ){
printf("server send msg error\n");
exit(0);
} }
return 0;
}
int msg_server_end(int id)
{
if(msgctl(id, IPC_RMID, NULL) == -1){
printf("delete msg kernel info error\n");
return 1;
}
return 0;
}
static void delete_msg(void)
{
if( _msg_id != -1 ){
msg_server_end(_msg_id);
}
printf("delete msg queue end\n");
}
int main(int argc, char *argv[])
{
atexit(delete_msg);
if(msg_server_start() == 0){
print_log("msg_server start success\n");
}else{
print_log("msg_server start failed\n");
}
return 0;
}
msg_client.h
------------
#ifndef _MSG_CLIENT_
#define _MSG_CLIENT_
#include"comm.h"
int msg_client_start();
int msg_client_end(int);
#endif
msg_client.c
------------
#include "msg_client.h"
int _msg_id = -1;
int msg_client_start()
{
key_t _key = ftok(FILEPATH,ID);//创建键值
if( _key < 0 ){
print_log("client get key id error");
return 1;
}
msginfo _cli_info;
_msg_id = msgget(_key, 0); //获取信号队列ID
if( _msg_id < 0 ){
print_log("msg_server get key id failed\n");
return 1;
}
while(1){
printf("client :>");
fgets(_cli_info.mtext, sizeof(_cli_info.mtext),stdin);
if(strncasecmp(_cli_info.mtext, "quit", 4) == 0){
printf("client bye!\n");
break;
}
_cli_info.mtype = g_cli_send_type;
if( msgsnd(_msg_id, &_cli_info,sizeof(_cli_info), 0) == -1 ){
printf("client send msg error\n");
exit(0);
}
memset(_cli_info.mtext, '\0', sizeof(_cli_info.mtext));
if( msgrcv(_msg_id, &_cli_info, __MSG_SIZE__, g_ser_send_type, 0) == -1 ){
print_log("client recive msg error");
return 1;
}
printf("server :>%s\n",_cli_info.mtext); }
return 0;
}
int msg_client_end(int id)
{
if(msgctl(id, IPC_RMID, NULL) == -1){
return 1;
}
return 0;
}
static void delete_msg(void)
{
if( _msg_id != -1 ){
msg_client_end(_msg_id);
}
printf("delete msg queue end\n");
}
int main(int argc, char *argv[])
{
atexit(delete_msg);
if(msg_client_start() == 0){
print_log("msg_server start success\n");
}else{
print_log("msg_server start failed\n");
}
return 0;