rt-thread操作系统中当线程时间片耗尽,或是当线程sleep一段时间后唤醒再被调度,此过程又是如何进行的呢?到底是谁来驱动这一过程的呢?
答案是时钟中断源。且来看看时钟中断例程:
在bsp/stm32f20x/drivers/board.c源文件中存在这么一个时钟中断例程代码:(这里以stm32f20x的MCU为例)
/** * This is the timer interrupt service routine. * */ void SysTick_Handler(void) { /* enter interrupt */ rt_interrupt_enter();//进入中断 rt_tick_increase();//增加tick /* leave interrupt */ rt_interrupt_leave();//离开中断 }
/** * This function will notify kernel there is one tick passed. Normally, * this function is invoked by clock ISR. */ void rt_tick_increase(void) { struct rt_thread *thread; /* increase the global tick */ ++ rt_tick;//全局变量rt_tick加1 /* check time slice */ thread = rt_thread_self();//获取当前调度的线程 -- thread->remaining_tick;//当前调度的线程的剩余时间片减1 if (thread->remaining_tick == 0)//如果当前调度的线程没有剩余时间片了 { /* change to initialized tick */ thread->remaining_tick = thread->init_tick;//重新将剩余时间片设置为初始化时间片 /* yield */ rt_thread_yield();//将此线程从调度器就绪队列中取出来放到末尾,再次然后调度 } /* check timer */ rt_timer_check();//检查时钟链表上是否有时间到达的时钟 }rt_thread_yield函数已在之前的文章 http://blog.csdn.net/flydream0/article/details/8584362一文的第7章已有介绍,而rt_timer_check函数在 http://blog.csdn.net/flydream0/article/details/8570841#t4一文中第3.3节也已有介绍。
由上述代码可见,一旦系统产生时钟中断,在中断例程中,系统首先将检查当前正在运行的线程剩余时间片是否耗尽,如果耗尽则将其从调度器就绪队列中取出放到末尾,然后再重新调度线程,接着检查是否有休眠的线程时间到达,如果有,则触发其时钟超时回调函数,这个时钟超时回调函数在之前的文章http://blog.csdn.net/flydream0/article/details/8584362#t2一文中的2.1节介绍初始化线程函数_rt_thread_init时有如下代码:
//... /* init thread timer */ rt_timer_init(&(thread->thread_timer),//初始化线程的定时器 thread->name, rt_thread_timeout, thread, 0, RT_TIMER_FLAG_ONE_SHOT); //...
/** * This function is the timeout function for thread, normally which is invoked * when thread is timeout to wait some resource. * * @param parameter the parameter of thread timeout function */ void rt_thread_timeout(void *parameter) { struct rt_thread *thread; thread = (struct rt_thread *)parameter; /* thread check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(thread->stat == RT_THREAD_SUSPEND); /* set error number */ thread->error = -RT_ETIMEOUT; /* remove from suspend list *///从挂起链表中移除 rt_list_remove(&(thread->tlist)); /* insert to schedule ready list */ rt_schedule_insert_thread(thread);//加入调度器 /* do schedule */ rt_schedule();//重新调度 }可见,其会将当前挂起的线程加入到调度器就绪队列,然后重新调度。
综上所述,当系统产生时钟中断时,首先检查当前正在运行的线程是否还有剩余时间片,如果耗尽则从就绪队列中移除放到末尾再重新调度,接着检查是否存在挂起的线程有时间到达的,如果有,则加入到调度器就绪队列中,然后重新调度。
此外,需要注意地是,如果用户设置使用软件软件定时器方式,则系统中还存在一时钟线程timer_thread,见http://blog.csdn.net/flydream0/article/details/8570841一文,此线程专门随时系统时钟tick的增加来检查定时器是否时间到达,这其中就包含线程的定时器,一旦线程对应的定时器时间到达,则将加入到线程调度器就绪队列中进行调度。由此可见,在设置了软件定时器模式时(默认情况下,rt-thread使用硬件定时器),这个timer_thread线程也是rt_thread操作系统线程调度的驱动引擎.
查看rt-thread操作系统的用户手册时,上面有提到rt_thead操作系统的时钟每个tick的默认间隔为10ms,那么这个10 ms双是如何来的呢?
答案是在/bsp/stm32f20x/drivers/board.c源文件中,且看SysTick_Configuration函数的实现:
/******************************************************************************* * Function Name : SysTick_Configuration * Description : Configures the SysTick for OS tick. * Input : None * Output : None * Return : None *******************************************************************************/ void SysTick_Configuration(void) { RCC_ClocksTypeDef rcc_clocks; rt_uint32_t cnts; RCC_GetClocksFreq(&rcc_clocks);//获得系统的晶振频率 cnts = (rt_uint32_t)rcc_clocks.HCLK_Frequency / RT_TICK_PER_SECOND;//计算出多少次晶振才是一个tick时间片 SysTick_Config(cnts);//配置系统tick SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//配置时钟源 }
/* Tick per Second */ #define RT_TICK_PER_SECOND 100此参数的含义是1秒包含多少个tick, 这里默认是100,则默认情况下1秒包含100个tick,那么即每个tick为10ms,现在明白了吧?
如果我们要修改每个tick的时间隔,则只需要修改RT_TICK_PER_SECOND这个宏的值即可.