嵌入式操作系统内核原理和开发(信号量)

 

【 声明:版权所有,欢迎转载,请勿用于商业用途。  联系信箱:feixiaoxing @163.com】 

 

    之前因为工作的原因,操作系统这块一直没有继续写下去。一方面是自己没有这方面的经历,另外一方面就是操作系统比较复杂和琐碎,调试起来比较麻烦。目前在实际项目中,使用的实时操作系统很多,很多国内的朋友也写过操作系统,有些项目现在还在维护和修改中,这是十分难得的。就我知道和熟悉的就有三个系统,比如

 

     (1)RT-THREAD

     (2)RAW-OS

     (3)ClearRTOS

 

    这里有比较介绍一下,这三个系统是国内的三位朋友开发的。其中rt-thread时间比较久一点,模块也比较全,bsp、cpu、fs、lwip、gui等辅助的代码也比较多,有兴趣的朋友可以到网站上面下载代码看一看。raw-os是我今年才发现的一个实时系统,从网站的注册时间和软件版本号上来看,系统开发的时间不是很长,不过整个系统代码的结构非常清晰,是我重点推荐阅读的代码。如果朋友们自己download下来,好好看一下其中的代码,肯定会有不少的收获。最后一个代码是作者李云在编写《专业嵌入式软件开发》这本书的时候,为了说明os的基本原理而开发的软件,前后设计了线程、互斥、内存、定时器、驱动框架等内容,值得一读。

 

    当然有了这么多优秀的代码,我觉得现在自己的工作就不是重新造一个车轮了,而是和大家分享这些优秀的代码是如何设计的。理解代码本身不是目的,关键是理解代码背后的基本思路。就我个人看过来,rt-thread和raw-os都可以用来学习,不过raw-os更好一些,主要是因为作者将raw-os移植到的vc上面,学起来也十分方便,要是个人在使用过程中有什么疑问,可以通过邮件和作者及时交流。不过由于raw-os的版本在一直在update之中,所以部分代码在前后稍微有点差别,不过这些都不是重点,暂时不了解的内容可以通过后面的了解和学习逐步掌握,不会成为太大的障碍。

 

    就像今天的题目一样,我们重点介绍一下信号量的设计原理。首先看一下信号量的数据结构是怎么样的,

typedef struct RAW_SEMAPHORE
{ 
	RAW_COMMON_BLOCK_OBJECT       common_block_obj;
	RAW_U32                       count;
	
} RAW_SEMAPHORE;


    这些代码都是从raw-os上面摘抄下来的,这个版本是0.94版本,和最新的0.96c版本有点差别。首先分析一下信号量的基本结构,其实非常简单,就两个变量,其中comm_block_obj是一个通用类型,记录了当前结构的名称、类型和阻塞队列,而count就是计数,判断是否还有释放的资源。

 

    说到了信号量的操作,无非就是信号量的创建、获取、释放、删除操作,当然这里作者考虑的比较详细,在信号量释放的时候还分成了 WAKE_ONE_SEM和WAKE_ALL_SEM两种类型。意思很简单,就是当信号量来临的时候是唤醒一个等待线程呢,还是唤醒所有的等待线程呢,就是这么回事。下面,我们就按照顺序介绍这几个函数,首先是创建函数,

RAW_U16 raw_semaphore_create(RAW_SEMAPHORE *semaphore_ptr, RAW_U8 *name_ptr, RAW_U32 initial_count)
{
	#if (RAW_SEMA_FUNCTION_CHECK > 0)
	
	if (semaphore_ptr == 0) {
		
		return RAW_NULL_OBJECT;
	}

	if (initial_count == 0xffffffff) {

		return RAW_SEMOPHORE_OVERFLOW;

	}
	
	#endif

	/*Init the list*/
	list_init(&semaphore_ptr->common_block_obj.block_list);
	
	/*Init resource*/
	semaphore_ptr->count     = initial_count;                                 
	
	semaphore_ptr->common_block_obj.name = name_ptr;  
	
	semaphore_ptr->common_block_obj.block_way = 0;
	
	return RAW_SUCCESS;

}

    看着初始化函数,我们发现信号量的初始化其实也非常简单,基本工作主要有:

    (1)判断参数合法性;

    (2)初始化阻塞队列、名称等;

    (3)初始化信号量的计数。

 

    说完了这些,我们看看信号量的获取是怎么完成的,代码可能长度稍微长一些,不过也不用太紧张,

RAW_U16 raw_semaphore_get(RAW_SEMAPHORE *semaphore_ptr,  RAW_U32 wait_option)
{

	RAW_U16 error_status;

	RAW_SR_ALLOC();

	#if (RAW_SEMA_FUNCTION_CHECK > 0)

	if (semaphore_ptr == 0) {
		
		return RAW_NULL_OBJECT;
	}
	
	if (raw_int_nesting) {

		return RAW_NOT_CALLED_BY_ISR;
	}

	#endif
	
	
	RAW_CRITICAL_ENTER();
	if (semaphore_ptr->count) {                      
		semaphore_ptr->count--;                                       

		RAW_CRITICAL_EXIT();
		
		return RAW_SUCCESS;
	}
	
	/*Cann't get semphore, and return immediately if wait_option is  RAW_NO_WAIT*/
	if (wait_option == RAW_NO_WAIT) { 

		RAW_CRITICAL_EXIT();
		return RAW_NO_PEND_WAIT;
	}      
	
	if (raw_sched_lock) {   
		RAW_CRITICAL_EXIT();	
		return RAW_SCHED_DISABLE;
	}

	raw_pend_object(&semaphore_ptr->common_block_obj, raw_task_active, wait_option);
	RAW_CRITICAL_EXIT();

	raw_sched(); 
	
	error_status = block_state_post_process(raw_task_active, 0);
	return error_status;

}

    信号量的获取情况比较复杂一些,这在长度上也体现出来了。不过没关系,我们一步一步看函数做了什么,

    (1)判断参数合法性;

    (2)判断当前函数是否处于中断处理的流程中,如果是选择返回;

    (3)判断当前count是否为0,如果不为 0,则减1返回;

    (4)如果当前count是0,且线程不愿意等待,那么选择返回;

    (5)如果当前禁止调度,那么依然选择返回;

     (6)当前线程将自己挂起,从ready队列中删除,把自己pend到信号量的阻塞队列中;

     (7)阻塞的线程再次获得了运行的机会,我们从task数据结构获得返回结果,此时也不一定是因为获得了资源的缘故哦。

 

    上面的get函数看上去比较复杂,但是所有的同步函数基本上都是这样设计的,看多了反而有一种八股文的感觉。刚开始看的同学可能觉得不是很习惯。不要紧,每天多看两眼,时间长了就ok了。好了,接着我们继续去看看信号量的释放函数是怎么处理的,大家做好心理准备哦,

static RAW_U16 internal_semaphore_put(RAW_SEMAPHORE *semaphore_ptr, RAW_U8 opt_wake_all)
{
	LIST *block_list_head;
	
	RAW_SR_ALLOC();

	#if (RAW_SEMA_FUNCTION_CHECK > 0)
	
	if (semaphore_ptr == 0) {
		
		return RAW_NULL_OBJECT;
	}
	
	#endif

	block_list_head = &semaphore_ptr->common_block_obj.block_list;
	
	RAW_CRITICAL_ENTER();
	/*if no block task on this list just return*/
	if (is_list_empty(block_list_head)) {        
	    
		if (semaphore_ptr->count == 0xffffffff) {

			RAW_CRITICAL_EXIT();
			return RAW_SEMOPHORE_OVERFLOW;

		}
		/*increase resource*/
	    semaphore_ptr->count++;                                      
	    
	    RAW_CRITICAL_EXIT();
	    return RAW_SUCCESS;
	}

	/*wake all the task blocked on this semphore*/
	if (opt_wake_all) {

		while (!is_list_empty(block_list_head)) {
			raw_wake_object(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list));
		}

	}

	else {
		
		/*Wake up the highest priority task block on the semaphore*/
		raw_wake_object(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list));
	}
	
	RAW_CRITICAL_EXIT();

	raw_sched();    

	return RAW_SUCCESS;
}

    看上去,信号量的释放函数也比较长,不过只要有耐心,都是可以看明白的,我们就来具体分析一下,

    (1)判断参数的合法性;

    (2)判断当前是否有等待队列,如果没有,则count自增,函数返回,当然如果count达到了0xffffffff也要返回,不过概率极低;

    (3) 当前存在等待队列,根据opt_wake_all的要求是唤醒一个线程还是唤醒所有的线程;

    (4)调用系统调度函数,让高优先级任务及时得到运行的机会;

    (5)当前线程再次得到运行的机会,函数返回。

 

    有了上面的讲解,我们发现os的代码其实也没有那么恐怖。所以,请大家一鼓作气,看看信号量是怎么删除的吧,

RAW_U16 raw_semaphore_delete(RAW_SEMAPHORE *semaphore_ptr)
{
	LIST *block_list_head;
	
	RAW_SR_ALLOC();

	#if (RAW_SEMA_FUNCTION_CHECK > 0)
	
	if (semaphore_ptr == 0) {
		
		return RAW_NULL_OBJECT;
	}
	
	#endif

	block_list_head = &semaphore_ptr->common_block_obj.block_list;
	
	RAW_CRITICAL_ENTER();

	/*All task blocked on this queue is waken up*/
	while (!is_list_empty(block_list_head)) {
		delete_pend_obj(list_entry(block_list_head->next, RAW_TASK_OBJ, task_list));	
	}                             

	RAW_CRITICAL_EXIT();
	raw_sched(); 
	return RAW_SUCCESS;
}

    信号量删除的工作其实很少,也很简单,同样我们也来梳理一下,

    (1)判断参数合法性;

    (2)唤醒阻塞队列中的每一个线程;

    (3)调用系统调度函数,因为高优先级的任务很有可能刚刚从阻塞队列中释放出来;

    (4)当前线程再次运行,函数返回。

 

    通过上面几个函数的讲解,我们发现关于os互斥部分的代码其实也不复杂。只要对系统本身和中断有一些了解,其实代码都是可以看懂的。当然,上面的代码我们还是讲的比较粗糙,所以有些细节还是要补充一下,

 

    (1)全局变量操作的函数必须在关中断的情况下进行操作;

    (2)实时系统的抢占是每时每刻都在进行的,比如中断返回时、信号量释放时、调用延时函数、调用调度函数的时候,所以大家心中要有抢占的概念;

    (3)互斥函数中大量使用了链表的结构,建议大家好好掌握链表的相关算法;

    (4)关于os的代码一定要多看、多思考、多练习才会有进步和提高,纸上得来终觉浅、绝知此事要躬行。

  

 

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式-老费

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值