用内存映射实现posix消息队列

本文介绍了如何利用内存映射技术实现POSIX消息队列,详细阐述了mq_send和mq_receive操作,并强调当队列状态变化(如非空到空,再到有新消息入队)时,mq_notify的通知机制。文中还提及了消息队列的具体数据结构,并给出了UNPv2上的实现示例。
摘要由CSDN通过智能技术生成
POSIX消息队列与System V消息队列的主要区别:
1.对POSIX队列的读总数返回最高优先级到最早消息,对SV队列到读则可以返回任意指定优先级的消息
2.当往一个空队列放置一个消息时,POSIX允许产生一个信号或启动一个线程,System V不提供此机制


消息的属性:
1.一个无符号整数的优先级(POSIX)或一个长整数的类型(SV)
2.消息的数据部分长度(可以为0)
3.数据本身(如果长度大于0)


POSIX消息队列总结:
mq_open创建一个新队列或者打开一个已经存在的队列
mq_close关闭队列
mq_unlink删除队列名,删除队列
mq_send往队列放置消息
mq_receive从一个队列中读出消息
mq_setattr和mq_getattr查询和设置队列的属性
mq_notify允许注册一个信号或者线程,在有一个消息被放置到空队列时,发送信号或者激活线程
每个消息被赋予一个小整数优先级,mq_receive总是返回最高优先级的最早消息


限制:
/proc/sys/fs/mqueue/msg_max 10
/proc/sys/fs/mqueue/msgsize_max 8192
/proc/sys/fs/mqueue/queues_max 256


创建一个新的消息队列或者打开一个已经存在的消息队列
<mqueue.h> 注意:编译加-lrt
<fcntl.h>
<sys/stat.h>
mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode,  struct mq_attr *attr);
成功返回描述字,失败返回-1并设置errno
name: 必须为/开头!!!
oflag: O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_EXCL, O_NONBLOCK


关闭消息队列,但不能删除它
mqd_t mq_close(mqd_t mqdes);
成功返回0,失败返回-1


删除消息队列,不一定马上删除消息队列,但队列名会立即删除,(真正的析构发生在引用计数为0的时候)
mqd_t mq_unlink(const char *name);
成功返回0,失败返回-1
当某个进程还没有关闭此消息队列时,调用mq_unlink时,不会马上删除队列,当最后一个进程关闭队列时,该队列被删除
int flags;
mqd_t mqd;
flags = O_RDWR | O_CREAT | O_EXCL;
mqd = mq_open("/tmp.111", flags, 0644, NULL);
if (mqd == (mqd_t)-1) {
perror("mq_open");
return 1;
}


消息队列的属性
mq_getattr mq_setattr
mqd_t mq_getattr(mqd_t mqdes, struct mq_attr *attr);
mqd_t mq_setattr(mqd_t mqdes, struct mq_attr *newattr, struct mq_attr *oldattr);
成功返回0,失败返回-1

收发消息

mq_send mq_receive


mq_receive返回队列中最高优先级的最早消息,而且该优先级能随该消息的内容及其长度一起返回


ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
成功返回消息的长度,消息的实际长度,不包括消息头;失败返回-1
msg_len指示msg_ptr的长度,必须大于等于mq_msgsize
如果msg_prio不为NULL,函数返回消息的优先级
如果队列为空,调用将阻塞,如果队列设置0_NONBLOCK,调用立即返回EAGAIN



队列限制
long int open_max = sysconf(_SC_MQ_OPEN_MAX);  // -1
long int prio_max = sysconf(_SC_MQ_PRIO_MAX);  // 32768


消息通告
当往空队列放置了一个消息时,通知进程
通告方式有2种:
1. 产生一个信号
2. 创建一个线程执行一个指定的函数
mqd_t mq_notify(mqd_t mqdes, const struct sigevent *notification);
成功返回0;失败返回-1
给队列建立或者删除异步事件通知
1.如果notification非空,那么当前进程希望在有一个消息到达而且队列先前为空时得到通知,该进程被注册为接收该队列的通知
2.如果notification为空,而且当前进程目前被注册为接收该队列的通知,那么现有注册将被撤销
3.任意时刻只有一个进程可以被注册为接收队列的通知
4.当有一个消息到达一个空队列,而且已经有一个进程被注册为接收该队列的通知时,只有在没有任何线程阻塞在该队列的mq_receive调用的前提下,通知才会发送。即在mq_receive调用中的阻塞比任何通知的注册都优先
5.当该通知已经发送给它的注册进程时,其注册即被撤销。该进程必须再次调用mq_notify以重新注册

6.当调用mq_notify但是队列不为空时,通知不会发送;当队列变为空,并且有一个消息入队时,才发送通知


下面是使用内存映射实现的消息队列,消息队列的数据结构如下:


以下是UNPv2上的实现

/*
		_______________
		|	mq_attr		|
		|_______________|
		|	mqh_head	|
		|	mqh_free	|
		|	mgh_nwait	|
		|	mgh_pid		|
		|_______________	|
		|	mqh_enevt	|
		|_______________	|
		|	mqh_lock	|
		|______________	|
		|	mqh_hdr		|
		|	数据		|
		|————————|
		|	mqh_hdr		|
		|	数据		|
		|________________|
		|	..........		|
		|_______________|

*/
#include<signal.h>
#include	<stdarg.h>
#include<sys/mman.h>
#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>

//取得文件stat结构的最大尝试次数,
#define		MAX_TRIES	10	


//不同系统对mode_t类型有不同的定义,这里统一
#ifdef __bsdi__
#define va_mode_t int
#else 
#define va_mode_t mode_t
#endif


typedef struct mq_info *mqd_t;

//定义消息的头部
struct msg_hdr{
	//指示下一个消息的索引下标
	long msg_next;
	//消息的俄实际长度
	ssize_t msg_len;
	//消息的优先级
	unsigned int msg_prio;
};
//定义消息队列属性
struct mq_attr {
	//消息队列的属性(只实现了O_NONBLOCK)
	long mq_flags;
	//消息队列上的消息的最大容量
	long mq_maxmsg;
	//每个消息的大小
	long mq_msgsize;
	//队列上的当前消息数
	long mq_curmsgs;
};

//定义消息队列的头部
struct mq_hdr{
	//消息队列属性
	struct mq_attr mqh_attr;
	//第一个消息的下标索引
	long mqh_head;
	//队列中的第一个空闲消息的下标索引
	long mqh_free;
	//阻塞在mq_receive()的进程数
	long mqh_nwait;
	//注册当前进程的pid
	pid_t mqh_pid;
	//注册到队列上的事件
	struct sigevent mqh_event;
	//消息队列的互斥锁
	pthread_mutex_t mqh_lock;
	//消息队列的条件锁
	pthread_cond_t mqh_wait;
};

//定义POSIX消息队列的描述符
struct mq_info{
	//消息队列的头部指针,()即指向消息队列的首地址
	struct mq_hdr *mqi_hdr;
	//魔数
	long mqi_magic;
	//当前进程的打开标志
	int mqi_flags;
};

#define MQI_MAGIC 0x23653125
//内存long对齐,使分配的字节数是long大小的整数倍,方便内存操作
#define MSGSIZE(i) ((((i)+sizeof(long)-1)/sizeof(long))%sizeof(long))

//定义默认属性
struct mq_attr	defattr = { 0, 128, 1024, 0 };

//打开或创建一个消息队列
mqd_t mq_open(const char *pathname, int oflag, ...)
{
	int		i, fd, nonblock, created, save_errno;
	long	msgsize, filesize, index;
	va_list	ap;
	mode_t	mode;
	int8_t	*mptr;
	struct stat	statbuff;
	struct mq_hdr	*mqhdr;
	struct msg_hdr	*msghdr;
	struct mq_attr	*attr;
	struct mq_info	*mqinfo;
	pthread_mutexattr_t	mattr;
	pthread_condattr_t	cattr;

	created = 0;
	//加入非阻塞标志
	nonblock = oflag & O_NONBLOCK;
	oflag &= ~O_NONBLOCK;
	//字节指针
	mptr = (int8_t *) MAP_FAILED;
	mqinfo = NULL;
again:
	if (oflag & O_CREAT) {//指定了创建标志
		va_start(ap, oflag);		
		//取消的模式中的用户执行权限标志S_IXUSR,为了避免竞争条件,规定指定S_IXUSR的进程负责初始化消息队列
		mode = va_arg(ap, va_mode_t) & ~S_IXUSR;
		attr = va_arg(ap, struct mq_attr *);
		va_end(ap);

		//在创建的时候指定S_IXUSR标志
		fd = open(pathname, oflag | O_EXCL | O_RDWR, mode | S_IXUSR);
		if (fd < 0) {
			//需要打开的文件已存在,并且没有指定排他性标志(O_EXCL),则可以直接以O_RDWR方式打开
			if (errno == EEXIST && (oflag & O_EXCL) == 0)
				goto exists;		
			else
				return((mqd_t) -1);
		}
		//标志文件已创建
		created = 1;
		//没有指定属性,则使用默认属性
		if (attr == NULL)
			attr = &defattr;
		else {
			//将查指定的消息队列属性
			if (attr->mq_maxmsg <= 0 || attr->mq_msgsize <= 0) {
				errno = EINVAL;
				goto err;
			}
		}
		//内存对齐
		msgsize = MSGSIZE(attr->mq_msgsize);
		//计算内存对齐以后需要创建文件的实际大小
		filesize = sizeof(struct mq_hdr) + (attr->mq_maxmsg *
				   (sizeof(struct msg_hdr) + msgsize));
		//定位文件,即是指定文件大小
		if (lseek(fd, filesize - 1, SEEK_SET) == -1)
			goto err;
		if (write(fd, "", 1) == -1)
			goto err;
		//以读、写、共享方式作内存映射
		mptr = mmap(NULL, filesize, PROT_READ | PROT_WRITE,
					MAP_SHARED, fd, 0);
		if (mptr == MAP_FAILED)
			goto err;

		//为消息队列分配一个struct mq_info结构,并初始化
		if ( (mqinfo = malloc(sizeof(struct mq_info))) == NULL)
			goto err;

		mqinfo->mqi_hdr = mqhdr = (struct mq_hdr *) mptr;
		mqinfo->mqi_magic = MQI_MAGIC;
		mqinfo->mqi_flags = nonblock;

		//初始化消息队列的属性
		mqhdr->mqh_attr.mq_flags = 0;
		mqhdr->mqh_attr.mq_maxmsg = attr->mq_maxmsg;
		//还是使用为对齐前,用户指定的大小来初始化
		mqhdr->mqh_attr.mq_msgsize = attr->mq_msgsize;
		mqhdr->mqh_attr.mq_curmsgs = 0;
		//无进程阻塞在当前队列
		mqhdr->mqh_nwait = 0;
		mqhdr->mqh_pid = 0;
		//当前队列无消息
		mqhdr->mqh_head = 0;
		//得到第一个消息的地址,(因为struct mq_hdr结构后面就是消息结构)
		index = sizeof(struct mq_hdr);
		mqhdr->mqh_free = index;
		//将消息链起来
		for (i = 0; i < attr->mq_maxmsg - 1; i++) {
			msghdr = (struct msg_hdr *) &mptr[index];
			index += sizeof(struct msg_hdr) + msgsize;
			msghdr->msg_next = index;
		}

		msghdr = (struct msg_hdr *) &mptr[index];
		//最后一个消息队列的next下标为0,即他就是最后一个
		msghdr->msg_next = 0;		

		//初始化互斥锁属性
		if ( (i = pthread_mutexattr_init(&mattr)) != 0)
			goto pthreaderr;
		//设置互斥锁为共享,之后不同进程之间就能使用这个锁了
		pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
		i = pthread_mutex_init(&mqhdr->mqh_lock, &mattr);

		if (i != 0)
			goto pthreaderr;
		//和互斥锁的操作一样
		if ( (i = pthread_condattr_init(&cattr)) != 0)
			goto pthreaderr;
		pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
		i = pthread_cond_init(&mqhdr->mqh_wait, &cattr);
		
		if (i != 0)
			goto pthreaderr;

		//此时初始化完成,恢复用户指定的打开文件模式
		if (fchmod(fd, mode) == -1)
			goto err;
		//关闭文件,返回消息队列的描述符
		close(fd);
		return((mqd_t) mqinfo);
	}
//对错误处理
//文件已存在
exists:
	if ( (fd = open(pathname, O_RDWR)) < 0) {
		//发生了竞争条件,如一个进程open文件后在lseek是阻塞,另一个进程open时发现文件已存在,到这里再次open,并且阻塞,第一个进程开始执行lseek出错,关闭文件并删除了文件,因此第二个进程执行时就会发生错误,如果本次用户执行的mq_open指定了O_CREAT标志,则回到前面再次执行
		if (errno == ENOENT && (oflag & O_CREAT))
			goto again;
		goto err;
	}

	//这也有一个竞争条件,如果上面和下面执行之间有其他进程执行出错(如mmap出错)就会执行错误处理,删除文件,这里使用stat调用取得文件的属性,如果文件不存在会返回文件不存在的错误(ENOENT),可以判断这个错误,返回到前面重新执行
	for (i = 0; i < MAX_TRIES; i++) {
		if (stat(pathname, &statbuff) == -1) {
			if (errno == ENOENT && (oflag & O_CREAT)) {
				close(fd);
				goto again;
			}
			goto err;
		}
		//如果没有指定文件的S_IXUSR标志,则说明他不负责初始化,消息队列已经初始化好了
		if ((statbuff.st_mode & S_IXUSR) == 0)
			break;//
		//否则等待一秒,看是否初始化成功
		sleep(1);
	}
	if (i == MAX_TRIES) {
		errno = ETIMEDOUT;
		goto err;
	}
	//将文件映射到自己进程空间
	filesize = statbuff.st_size;
	mptr = mmap(NULL, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (mptr == MAP_FAILED)
		goto err;
	close(fd);
	//分配struct mq_info初始化并返回
	if ( (mqinfo = malloc(sizeof(struct mq_info))) == NULL)
		goto err;

	mqinfo->mqi_hdr = (struct mq_hdr *) mptr;
	mqinfo->mqi_magic = MQI_MAGIC;
	mqinfo->mqi_flags = nonblock;
	return((mqd_t) mqinfo);

pthreaderr:
	errno = i;
err:
	save_errno = errno;
	if (created)
		unlink(pathname);
	if (mptr != MAP_FAILED)
		munmap(mptr, filesize);
	if (mqinfo != NULL)
		free(mqinfo);
	close(fd);
	errno = save_errno;
	return((mqd_t) -1);
}


//注册或删除消息队列的一个调用进程
int mq_notify(mqd_t mqd, const struct sigevent *notification)
{
	int		n;
	pid_t	pid;
	struct mq_hdr	*mqhdr;
	struct mq_info	*mqinfo;

	mqinfo = mqd;
	//检测麽数是否发生变化,如果发生变化,则这个消息队列已经损坏
	if (mqinfo->mqi_magic != MQI_MAGIC) {
		errno = EBADF;
		return(-1);
	}
	//取得消息队列的头
	mqhdr = mqinfo->mqi_hdr;
	//获得消息队列的互斥锁
	if ( (n = pthread_mutex_lock(&mqhdr->mqh_lock)) != 0) {
		errno = n;
		return(-1);
	}

	pid = getpid();
	
	if (notification == NULL) {
		if (mqhdr->mqh_pid == pid) {//如果struct sigevent结构为空,且当前进程就是之前的注册进程,则删除注册
			mqhdr->mqh_pid = 0;	
		} 						
	} 
	else {//如果struct sigevent结构不为空
		if (mqhdr->mqh_pid != 0) {//如果 当前队列已有注册进程,则发送信号0检测该进程是否存在,如果不存在返回ESRCH错误,信号0是一个特殊的信号,之用来检测进程是否存在,并不向进程实际发送信号
			if (kill(mqhdr->mqh_pid, 0) != -1 || errno != ESRCH) {
				errno = EBUSY;
				goto err;
			}
		}
		//将消息队列注册到当前进程
		mqhdr->mqh_pid = pid;
		mqhdr->mqh_event = *notification;
	}
	//释放互斥锁
	pthread_mutex_unlock(&mqhdr->mqh_lock);
	return(0);

err:
	pthread_mutex_unlock(&mqhdr->mqh_lock);
	return(-1);
}

//关闭消息队列的描述符,本进程不再使用,这个操作并不会从系统中删除消息队列
int mq_close(mqd_t mqd)
{
	long	msgsize, filesize;
	struct mq_hdr	*mqhdr;
	struct mq_attr	*attr;
	struct mq_info	*mqinfo;

	mqinfo = mqd;
	if (mqinfo->mqi_magic != MQI_MAGIC) {
		errno = EBADF;
		return(-1);
	}
	mqhdr = mqinfo->mqi_hdr;
	attr = &mqhdr->mqh_attr;
	//撤销当前进程的注册
	if (mq_notify(mqd, NULL) != 0)	
		return(-1);
	//根据消息队列的属性,再次计算内存对齐的大小和整个消息队列的大小
	msgsize = MSGSIZE(attr->mq_msgsize);
	filesize = sizeof(struct mq_hdr) + (attr->mq_maxmsg *
			   (sizeof(struct msg_hdr) + msgsize));
	//撤销当前进程对消息队列的内存映射
	if (munmap(mqinfo->mqi_hdr, filesize) == -1)
		return(-1);
	//重置魔数,以防再次使用
	mqinfo->mqi_magic = 0;		
	free(mqinfo);
	return(0);
}

//删除消息队列,注意每个消息队列都维护了一个引用计数,就像文件一样,当引用计数不为0时并不发生真正的析构,即内核中的消息队列并不真正删除
int mq_unlink(const char *pathname)
{
	if (unlink(pathname) == -1)
		return(-1);
	return(0);
}

//设置消息队列的属性
int mq_setattr(mqd_t mqd, const struct mq_attr *mqstat,
			 struct mq_attr *omqstat)
{
	int		n;
	struct mq_hdr	*mqhdr;
	struct mq_attr	*attr;
	struct mq_info	*mqinfo;

	mqinfo = mqd;
	if (mqinfo->mqi_magic != MQI_MAGIC) {
		errno = EBADF;
		return(-1);
	}
	mqhdr = mqinfo->mqi_hdr;
	attr = &mqhdr->mqh_attr;
	if ( (n = pthread_mutex_lock(&mqhdr->mqh_lock)) != 0) {
		errno = n;
		return(-1);
	}
	//保存当前的消息队列作为返回值
	if (omqstat != NULL) {
		omqstat->mq_flags = mqinfo->mqi_flags;	/* previous attributes */
		omqstat->mq_maxmsg = attr->mq_maxmsg;
		omqstat->mq_msgsize = attr->mq_msgsize;
		omqstat->mq_curmsgs = attr->mq_curmsgs;	/* and current status */
	}
	//只设置一个属性O_NONBLOCK
	if (mqstat->mq_flags & O_NONBLOCK)
		mqinfo->mqi_flags |= O_NONBLOCK;
	else
		mqinfo->mqi_flags &= ~O_NONBLOCK;

	pthread_mutex_unlock(&mqhdr->mqh_lock);
	return(0);
}
//获得消息队列的属性
int  mq_getattr(mqd_t mqd, struct mq_attr *mqstat)
{
	int		n;
	struct mq_hdr	*mqhdr;
	struct mq_attr	*attr;
	struct mq_info	*mqinfo;

	mqinfo = mqd;
	if (mqinfo->mqi_magic != MQI_MAGIC) {
		errno = EBADF;
		return(-1);
	}
	mqhdr = mqinfo->mqi_hdr;
	attr = &mqhdr->mqh_attr;
	if ( (n = pthread_mutex_lock(&mqhdr->mqh_lock)) != 0) {
		errno = n;
		return(-1);
	}

	mqstat->mq_flags = mqinfo->mqi_flags;	
	mqstat->mq_maxmsg = attr->mq_maxmsg;	
	mqstat->mq_msgsize = attr->mq_msgsize;
	mqstat->mq_curmsgs = attr->mq_curmsgs;

	pthread_mutex_unlock(&mqhdr->mqh_lock);
	return(0);
}
//向消息队列发送消息,len是消息长度,prio是消息优先级
int mq_send(mqd_t mqd, const char *ptr, size_t len, unsigned int prio)
{
	int		n;
	long	index, freeindex;
	int8_t	*mptr;
	struct sigevent	*sigev;
	struct mq_hdr	*mqhdr;
	struct mq_attr	*attr;
	struct msg_hdr	*msghdr, *nmsghdr, *pmsghdr;
	struct mq_info	*mqinfo;

	mqinfo = mqd;
	if (mqinfo->mqi_magic != MQI_MAGIC) {
		errno = EBADF;
		return(-1);
	}
	//消息队列head的指针
	mqhdr = mqinfo->mqi_hdr;	
	//字节指针
	mptr = (int8_t *) mqhdr;
	//消息队列的属性	
	attr = &mqhdr->mqh_attr;
	if ( (n = pthread_mutex_lock(&mqhdr->mqh_lock)) != 0) {
		errno = n;
		return(-1);
	}
	//消息长度不能超过属性中mq_msgsize字段指定的大小
	if (len > attr->mq_msgsize) {
		errno = EMSGSIZE;
		goto err;
	}
	if (attr->mq_curmsgs == 0) {//当消息队列由空边非空时才会向组测进程发送信号
		if (mqhdr->mqh_pid != 0 && mqhdr->mqh_nwait == 0) {
			sigev = &mqhdr->mqh_event;
			if (sigev->sigev_notify == SIGEV_SIGNAL) {//发送指定信号
				sigqueue(mqhdr->mqh_pid, sigev->sigev_signo,
						 sigev->sigev_value);
			}
			mqhdr->mqh_pid = 0;		//取消进程对消息队列的注册
		}
	} else if (attr->mq_curmsgs >= attr->mq_maxmsg) {
		//消息队列已满,并且设置非阻塞,则直接返回	
		if (mqinfo->mqi_flags & O_NONBLOCK) {
			errno = EAGAIN;
			goto err;
		}
		//阻塞,直到有进程从消息队列读走一条消息,使条件变量返回
		while (attr->mq_curmsgs >= attr->mq_maxmsg)
			pthread_cond_wait(&mqhdr->mqh_wait, &mqhdr->mqh_lock);
	}
	//取得可用消息的索引下标
	if ( (freeindex = mqhdr->mqh_free) == 0){
		printf("mq_send: curmsgs = %ld; free = 0", attr->mq_curmsgs);
		exit(0);
	}

	nmsghdr = (struct msg_hdr *) &mptr[freeindex];
	nmsghdr->msg_prio = prio;
	nmsghdr->msg_len = len;
	//nmsghdr + 1其实就是sizeof(struct msg_hdr)+1,指向的就是实际存储消息的首地址
	memcpy(nmsghdr + 1, ptr, len);	//内存拷贝	
	//修改链表的指针
	mqhdr->mqh_free = nmsghdr->msg_next;	


	//根据优先级将当前消息查到对应优先级的最后面,这样可以直接返回消息链表的第一个就是优先级最高且最早接受的消息
	index = mqhdr->mqh_head;
	pmsghdr = (struct msg_hdr *) &(mqhdr->mqh_head);
	while (index != 0) {
		msghdr = (struct msg_hdr *) &mptr[index];
		if (prio > msghdr->msg_prio) {
			nmsghdr->msg_next = index;
			pmsghdr->msg_next = freeindex;
			break;
		}
		index = msghdr->msg_next;
		pmsghdr = msghdr;
	}
	if (index == 0) {
		pmsghdr->msg_next = freeindex;
		nmsghdr->msg_next = 0;
	}
	//如果放消息前当前消息数为0,唤醒任何一个阻塞的接受进程
	if (attr->mq_curmsgs == 0)
		pthread_cond_signal(&mqhdr->mqh_wait);
	attr->mq_curmsgs++;

	pthread_mutex_unlock(&mqhdr->mqh_lock);
	return(0);

err:
	pthread_mutex_unlock(&mqhdr->mqh_lock);
	return(-1);
}

//从消息队列上接受一个消息
ssize_t mq_receive(mqd_t mqd, char *ptr, size_t maxlen, unsigned int *priop)
{
	int		n;
	long	index;
	int8_t	*mptr;
	ssize_t	len;
	struct mq_hdr	*mqhdr;
	struct mq_attr	*attr;
	struct msg_hdr	*msghdr;
	struct mq_info	*mqinfo;

	mqinfo = mqd;
	if (mqinfo->mqi_magic != MQI_MAGIC) {
		errno = EBADF;
		return(-1);
	}
	mqhdr = mqinfo->mqi_hdr;	
	mptr = (int8_t *) mqhdr;	
	attr = &mqhdr->mqh_attr;
	if ( (n = pthread_mutex_lock(&mqhdr->mqh_lock)) != 0) {
		errno = n;
		return(-1);
	}

	if (maxlen < attr->mq_msgsize) {
		errno = EMSGSIZE;
		goto err;
	}
	if (attr->mq_curmsgs == 0) {//队列空且非阻塞,直接返回		
		if (mqinfo->mqi_flags & O_NONBLOCK) {
			errno = EAGAIN;
			goto err;
		}
		//阻塞等待,并将mqh_nwait加一
		mqhdr->mqh_nwait++;
		while (attr->mq_curmsgs == 0)
			pthread_cond_wait(&mqhdr->mqh_wait, &mqhdr->mqh_lock);
		mqhdr->mqh_nwait--;
	}

	//取得第一个消息的索引下标
	if ( (index = mqhdr->mqh_head) == 0){
		printf("mq_receive: curmsgs = %ld; head = 0", attr->mq_curmsgs);
		exit(0);
	}

	msghdr = (struct msg_hdr *) &mptr[index];
	mqhdr->mqh_head = msghdr->msg_next;	/* new head of list */
	len = msghdr->msg_len;
	memcpy(ptr, msghdr + 1, len);		/* copy the message itself */
	//如果调用要求返回优先级,则赋值优先级
	if (priop != NULL)
		*priop = msghdr->msg_prio;
	//将发当前消息挂在可用消息链表的最前面
	msghdr->msg_next = mqhdr->mqh_free;
	mqhdr->mqh_free = index;

	//唤醒任何一个等待发送的进程
	if (attr->mq_curmsgs == attr->mq_maxmsg)
		pthread_cond_signal(&mqhdr->mqh_wait);
	attr->mq_curmsgs--;

	pthread_mutex_unlock(&mqhdr->mqh_lock);
	return(len);

err:
	pthread_mutex_unlock(&mqhdr->mqh_lock);
	return(-1);
}


首先,POSIX共享内存是一种允许多个进程共享同一块内存的机制。在使用POSIX共享内存时,需要先使用shm_open()函数创建一个共享内存对象,然后使用mmap()函数将该共享内存对象映射到进程的虚拟地址空间中。通过这种方式,多个进程就可以通过访问同一块内存来进行通信。 其次,利用C++ STL中的std::queue作为先进先出通知队列模板,可以方便地实现在共享内存中完成高可用的队列。具体步骤如下: 1. 在共享内存中分配一定大小的内存空间,用于存储队列元素以及队列的状态信息(如队列头指针、尾指针等)。 2. 将队列元素存储在共享内存中,保证多个进程可以访问同一份队列数据。 3. 使用POSIX信号量来保证多个进程对队列的并发访问安全。例如,可以使用一个二元信号量来表示队列是否为空,以及一个计数信号量来表示队列中元素的数量。 4. 在队列头部和尾部添加标记位,用于标记队列的状态(如队列是否已满、是否已经被销毁等)。 5. 在实现队列操作时,需要使用原子操作或者加锁机制来保证多个进程对队列的操作是原子性的。 通过以上步骤,可以实现一个高可用的、支持多进程共享访问的队列。当然,还需要考虑一些其他的问题,如内存分配的策略、队列的容量限制、队列的清空与销毁等。但总的来说,利用POSIX共享内存和C++ STL中的std::queue,可以快速地实现一个高效的共享队列。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值