ks;
8 if (_time_slice_elapsed >= _time_slice_duration) {
9
10 unsigned int key;
11
12 _time_slice_elapsed = 0;
13
14 key = irq_lock();
15 _move_thread_to_end_of_prio_q(_current);
16 irq_unlock(key);
17 }
18}
第3行,调用_is_thread_time_slicing()函数,定义在zephyr-zephyr-v1.13.0\kernel\sched.c:
1 int _is_thread_time_slicing(struct k_thread *thread)
2 {
3 int ret = 0;
4
5 /* Should fix API. Doesn't make sense for non-running threads
6 * to call this
7 */
8 __ASSERT_NO_MSG(thread == _current);
9
10 if (_time_slice_duration <= 0 || !_is_preempt(thread) ||
11 _is_prio_higher(thread->base.prio, _time_slice_prio_ceiling)) {
12 return 0;
13 }
14
15
16 LOCKED(&sched_lock) {
17 struct k_thread *next = _priq_run_best(&_kernel.ready_q.runq);
18
19 if (next) {
20 ret = thread->base.prio == next->base.prio;
21 }
22 }
23
24 return ret;
25 }
第10~13行,_time_slice_duration的值在系统启动时就设置了。_is_preempt()函数:
static inline int _is_preempt(struct k_thread *thread)
{
/* explanation in kernel_struct.h */
return thread->base.preempt <= _PREEMPT_THRESHOLD;
}
_PREEMPT_THRESHOLD的值为127。即如果线程的优先级小于128则_is_preempt()返回1。
_is_prio_higher()比较当前线程的优先级是否高于_time_slice_prio_ceiling的值(也是在系统启动时就设置了),如果这三个条件有一个成立了,则不会处理时间片相关的内容。
第17行,调用_priq_run_best()函数取出运行队列的头节点,即优先级最高的线程。只有运行队列的头节点的优先级与当前线程的优先级相等才会继续往下处理。
回到handle_time_slicing()函数,第7行,累加ticks个数。
第8行,如果累加的ticks个数大于等于配置的时间片数,则if条件成立。
第12行,将累加的ticks个数清0。
第15行,调用_move_thread_to_end_of_prio_q()函数,定义在zephyr-zephyr-v1.13.0\kernel\sched.c:
1 void _move_thread_to_end_of_prio_q(struct k_thread *thread)
2 {
3 LOCKED(&sched_lock) {
4 _priq_run_remove(&_kernel.ready_q.runq, thread);
5 _priq_run_add(&_kernel.ready_q.runq, thread);
6 _mark_thread_as_queued(thread);
7 update_cache(0);
8 }
9 }
第4~7行,这几个函数前面都已经分析过了。
到这里就可以知道,要使用时间片轮转的调度方式,需要以下设置:
1.配置时间片大小(大于0)和优先级;
2.所有创建的线程的优先级要相同,并且优先级要比1中的优先级高;
仔细思考会发现目前这种超时处理机制对延时(休眠)的时间是不准确的,因此这种机制总是以tick为单位进行延时(休眠),也即时间只能精确到tick。那有没有其他方法可以准确延时(休眠)呢?肯定是有的,就是需要打开TICKLESS_KERNEL配置,其原理就是不以tick(假如10ms)为固定时间进行定时,而是每次根据需要延时(休眠)的最小时间进行定时,这样就能实现精确的延时(休眠),zephyr是支持这种精确定时方式的,感兴趣的可以去研究研究。
|