Raw-OS源码分析之消息系统-Queue

        分析的内核版本截止到2014-04-15,基于1.05正式版,blogs会及时跟进最新版本的内核开发进度,若源码注释出现”???”字样,则是未深究理解部分。

        Raw-OS官方网站:http://www.raw-os.org/

        Raw-OS托管地址:https://github.com/jorya/raw-os/

 

        接下去几篇都是关于Raw-OS的消息队列模块的解读

        话说对于raw_queue.c这个内核文件的代码,浏览下去你就会发觉其实实时系统消息通信其实也是很简单的,关键是有关c指针的操作理解透彻一点就可以很明白地读懂,这指针的概念就不会多说了,或者可以补补c指针的概念再去读这个模块。

 

一、queue原理

        消息队列的原理很简单,就相当于给消息建立个中转站,然后由生产者提供消息,由消费者消费消息。

 

 

 

        在queue里面,加入消息是队列,那么生产者提供消息的时候,就可以选择插入到queue的前面或者queue的后面,插入queue头部的消息可以理解为“紧急消息”;插入到queue尾部的消息可以理解为“一般消息”;

 

        那么什么是消息,按照Raw-OS中queue传递消息的过程,消息是一个void指针~那么指针是可以指向任何数据类型的,也就等于说消息是一个可以指向任何数据类型的void指针,那么queue就可以中存放任何数据类型的消息。

        至于queue内部的操作,基本上是有关指针的逻辑,所以,亲们~你懂的~

 

二、queue读写指针

        在queue内部,被消费的消息都是从queue的读指针开始取消息的,无论消息是发到queue的头部或者是尾部,queue读指针都是指向第一个没有被消费的消息,queue写指针指向最后一个没有被消费的消息。

        接下来就简单看看queue内部,读写指针和queue内消息的关系,例子是一个拥有5个大小单位的queue以及queue内消息的关系:

 

 

 

        当往queue发送消息缓存的时候,read指针和write指针就是如此来跑位的,至于有一个情况,如果queue满那读写指针是怎么排位的,这里,除了queue为空时(没有消息),read指针和write指针重合,指向同一个数据单元,当queue满的时候,在操作read指针和write指针前就会直接返回满标志。

 

三、queue传递消息原理

        之前讲过,消息是一个可以指向任何数据类型的void指针,也就是说,queue存放消息,就是存放指向用户数据的指针。

        就是说,如果msg是一个用户数据,那么发往queue的就是&msg,那么在queue作缓存的内存以数组形式出现,这个存放内容是指针的数组是指针数组,所以queue内部用一个用户定义大小的指针数组来缓存消息,继续联想,再要继续对数组操作时,很多时候就需要用到二级指针,因为操作的数组的内容是指针;同样,对于消费消息的操作,就是发送消息的逆操作,废话一堆,看一个发送消息到消费消息过程图应该可以很好理解~

 

 

 

四、任务阻塞在queue上

        哈哈,至于阻塞的概念我们已经很熟了,讲神马tick,神马mutex都涉及有这个概念,对于queue也是一样,当queue为空(没有消息)时,任务想获取消息时,就会被阻塞在queue的阻塞链表上~阻塞的代码又回到原来的task block了,所以也就没什么难度了~

 

 

 

        最后的最后,给出Raw-OS有关消息发送和接收的代码注释,至于创建queue,删除queue等等都是非常简单的~

 

        queue发送

RAW_U16 msg_post(RAW_QUEUE *p_q, RAW_VOID *p_void, RAW_U8 opt_send_method, RAW_U8 opt_wake_all)
{
	LIST *block_list_head;
	/* 定义CPU状态机字变量 */
 	RAW_SR_ALLOC();
 	/* 禁止CPU中断,保存CPU状态字 */
	RAW_CRITICAL_ENTER();
	/* 如果传入的queue控制的类型对象不是Raw-OS的队列对象,错误返回 */
	if (p_q->common_block_obj.object_type != RAW_QUEUE_OBJ_TYPE) {
		RAW_CRITICAL_EXIT();
		return RAW_ERROR_OBJECT_TYPE;
	}
	/* 获取queue的阻塞链表头 */
	block_list_head = &p_q->common_block_obj.block_list;

	/* 如果当前存放在queue的消息数量大于或等于queue存放消息的消息数组的大小,就说明queue已满,返回满标志 */
	if (p_q->msg_q.current_numbers >= p_q->msg_q.size) {
		RAW_CRITICAL_EXIT();
		TRACE_QUEUE_MSG_MAX(raw_task_active, p_q, p_void, opt_send_method);
		return RAW_MSG_MAX;
	}

	/* 如果执行到这里,说明queue未满,并且没有任务阻塞在queue的阻塞链表上 */
	if (is_list_empty(block_list_head)) {
		/* queue存放消息的数量++ */
		p_q->msg_q.current_numbers++;

		/* 貌似peak_numbers就是用来存放可能出现在queue内消息的最大数量的值 */
		if (p_q->msg_q.current_numbers > p_q->msg_q.peak_numbers) {
			p_q->msg_q.peak_numbers = p_q->msg_q.current_numbers;
		}

		/* 当消息发送到queue的末端时 */
		if (opt_send_method == SEND_TO_END)  {
			/* 将消息写入msg写指针指向的位置同时,queue控制块的消息写指针后移 */
			*p_q->msg_q.write++ = p_void;
			/* 如果写指针后移到queue存放消息数组的末端时,那么就重新指向开头 */
			if (p_q->msg_q.write == p_q->msg_q.queue_end) {
				/* 将queue消息写指针 */
				p_q->msg_q.write = p_q->msg_q.queue_start;
			}
		}

		/* 当消息发送到queue的前端时 */
		else {
			/* 先检查读指针是否在queue消息数组的起始地址,小心越界 */
			if (p_q->msg_q.read == p_q->msg_q.queue_start) {
	        	p_q->msg_q.read = p_q->msg_q.queue_end;
	    	}
	    	/* queue控制块的消息读指针前移 */
			p_q->msg_q.read--;
			/* 将消息写入读指针前移后的位置 */
			*p_q->msg_q.read = p_void;
		}

		RAW_CRITICAL_EXIT();

		/* 当创建queue控制块时注册有回调函数,那么在这里就会调用此回调函数 */
		if (p_q->queue_send_notify) {
			/* 回调函数以queue控制块为传入参数 */
			p_q->queue_send_notify(p_q);
		}

		TRACE_QUEUE_MSG_POST(raw_task_active, p_q, p_void, opt_send_method);
		return RAW_SUCCESS;
	}

	/* 如果有任务阻塞在queue的阻塞链表上时,在这里唤醒任务,根据用户标识选择唤醒所有任务还是单个任务 */
	if (opt_wake_all) {
		/* 唤醒queue阻塞链表的所有任务 */
		while (!is_list_empty(block_list_head)) {
			/* 将消息发送到queue阻塞链表所有阻塞任务中 */
			wake_send_msg(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list), p_void);

			TRACE_QUEUE_WAKE_TASK(raw_task_active, list_entry(block_list_head->next, RAW_TASK_OBJ, task_list), p_void, opt_wake_all);
		}
	}

	/* 唤醒queue阻塞链表中的第一个任务(最高优先级任务) */
	else {
		/* 将消息发送到queue阻塞链表的第一个任务(阻塞的最高优先级任务) */
		wake_send_msg(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list), p_void);

		TRACE_QUEUE_WAKE_TASK(raw_task_active, list_entry(block_list_head->next, RAW_TASK_OBJ, task_list), p_void, opt_wake_all);
	}

	RAW_CRITICAL_EXIT();
	/* 系统调度 */
	raw_sched();
	return RAW_SUCCESS;
}


        queue接收

RAW_U16 raw_queue_receive(RAW_QUEUE *p_q, RAW_TICK_TYPE wait_option, RAW_VOID **msg)
{
	RAW_VOID *pmsg;
	RAW_U16 result;
	/* 定义CPU状态机字变量 */
	RAW_SR_ALLOC();

	#if (RAW_QUEUE_FUNCTION_CHECK > 0)
	/* 检查中断嵌套,和用户设置的接收阻塞标志,仅当用户设置接收不到消息时不发生阻塞才能在中断中接收消息 */
	if (raw_int_nesting && (wait_option != RAW_NO_WAIT)) {
		return RAW_NOT_CALLED_BY_ISR;
	}
	/* 检查传入消息队列控制块的地址,为空时,说明没有实体,错误返回 */
	if (p_q == 0) {
		return RAW_NULL_OBJECT;
	}
	/* 这里传入的是用来存放接收数据后,数据存放的变量,一个二级指针 */
	if (msg == 0) {
		return RAW_NULL_POINTER;
	}
	#endif

	/* 当开启task 0后,消息由task 0转发??? */
	#if (CONFIG_RAW_ZERO_INTERRUPT > 0)
	if (raw_int_nesting) {
		return RAW_NOT_CALLED_BY_ISR;
	}
	#endif

	RAW_CRITICAL_ENTER();
	/* 如果传入的queue控制的类型对象不是Raw-OS的队列对象,错误返回 */
	if (p_q->common_block_obj.object_type != RAW_QUEUE_OBJ_TYPE) {
		RAW_CRITICAL_EXIT();
		return RAW_ERROR_OBJECT_TYPE;
	}

	/*
	 * 用传入的msg二级指针来接收queue中的数据
	 *
	 * 实现过程:
	 *     1.msg是一个二级指针,一个指针的地址,例如用户定义有接收数据的变量*tmp,那么传入的msg = &tmp
	 *     2.在queue中建立存放的消息数组是msg_array[]这样的,那么queue的msg_start = &msg_array
	 *	   3.并且queue的读写指针的定义都是二级指针,都存放msg_array[x]某一项的地址
	 *	       write = &msg_array[x];
	 *	       read = &msg_array[y];
	 *     4.那么接收时pmsg变量指向读指针的内容,就是pmsg = msg_array[y];
	 *     5.当用用户定义的接收数据变量*tmp来接收时
	 *	       *msg = pmsg = msg_array[y] = *&tmp = tmp
	 *         由此就可以得知用户定义的:tmp = msg_array[y],注意tmp是一个指针类型
	 *		   注意啦,因为,因为,在给queue发送数据的时候,Raw-OS是直接发送数据的指针当做消息的,
	 *		   所以,msg_array[]里面存放的消息是一个一个的指针,那么tmp只是取出queue中的消息
	 *		   而消息是指向数据的指针,那么要使用数据时,利用*tmp才能操作存在msg_array[]中元素指向的数据
	 *
	 *     可以得出的结论是,当创建queue控制块时,存放消息的数组要定义成:指针数组
	 *     接收过程用定义一个指针变量:RAW_VOID *tmp,接收是传入&tmp,获取消息后,*tmp = 消息指向的数据
	 */
	if (p_q->msg_q.current_numbers) {

		pmsg = *p_q->msg_q.read++;

		if (p_q->msg_q.read == p_q->msg_q.queue_end) {
			/*wrap around to start*/
			p_q->msg_q.read = p_q->msg_q.queue_start;
		}

		*msg = pmsg;

		p_q->msg_q.current_numbers--;

		RAW_CRITICAL_EXIT();

		TRACE_QUEUE_GET_MSG(raw_task_active, p_q, wait_option, *msg);

		return RAW_SUCCESS;
	}

	/* 用户设置不阻塞标志时,返回空指针消息 */
	if (wait_option == RAW_NO_WAIT) {
		*msg = (RAW_VOID *)0;
		RAW_CRITICAL_EXIT();
		return RAW_NO_PEND_WAIT;
	}

	/* 当系统上锁后,不能进行任务阻塞 */
	SYSTEM_LOCK_PROCESS_QUEUE();
	/* 当queue没有消息时,对要获取queue消息进行阻塞,阻塞到queue的阻塞链表上 */
	raw_pend_object((RAW_COMMON_BLOCK_OBJECT *)p_q, raw_task_active, wait_option);

	RAW_CRITICAL_EXIT();

	TRACE_QUEUE_GET_BLOCK(raw_task_active, p_q, wait_option);
	/* 系统任务调度 */
	raw_sched();

	/* 当阻塞任务被调度后,返回到这里,将msg清空,用以接收阻塞任务控制的消息 */
	*msg = (RAW_VOID *)0;
	/* 之前分析过post msg时,消息是直接发送到任务控制块中的,当阻塞任务被调度时,从任务控制块中取出消息给msg,返回调度后阻塞状态 */
	result = block_state_post_process(raw_task_active, msg);

	return result;
}

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值