问题遇到的现象和发生背景
在使用wait_event_timeout接口时,我传的参数是定时20毫秒,但是偶尔会出现14毫秒就超时唤醒的情况。
问题相关代码,请勿粘贴截图
wait_event_timeout(tmp_que, flag == 1, msecs_to_jiffies(20));
我的解答思路和尝试过的方法
- 第一种可能:因为kernel刚启动时是使用基于tick的低精度定时器,当系统中有合适的高频率clock source注册以后,kernel会切换至高精度定时器,所以我猜测是不是高低精度定时器之间的切换导致了这种情况的偶发,目前从代码层面还没有直接的证据证明,只能是猜测;
- 第二种可能:因为该情况是在反复上下电的情况下才偶尔出现,我猜测是硬件clock source偶尔的不稳定导致的,当然仅仅只是猜测。
我想要达到的结果
我个人更倾向于第二种可能,算是一种自我安慰吧,欢迎各位Code people来交流讨论!
附录
#define wait_event_timeout(wq, condition, timeout) \
({ \
long __ret = timeout; \
if (!(condition)) \
__wait_event_timeout(wq, condition, __ret); \
__ret; \
})
#define __wait_event_timeout(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
\
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \
if (condition) \
break; \
ret = schedule_timeout(ret); \
if (!ret) \
break; \
} \
if (!ret && (condition)) \
ret = 1; \
//设置当前线程为运行态,并从给定的等待队列中删除等待描述符
finish_wait(&wq, &__wait); \
} while (0)
signed long __sched schedule_timeout(signed long timeout)
{
struct timer_list timer;
unsigned long expire;
switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
schedule();
goto out;
default:
if (timeout < 0) {
printk(KERN_ERR "schedule_timeout: wrong timeout value %lx\n", timeout);
dump_stack();
current->state = TASK_RUNNING;
goto out;
}
}
expire = timeout + jiffies;
setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
__mod_timer(&timer, expire, false, TIMER_NOT_PINNED); //设置定时时间
schedule(); //让出CPU进入休眠,timer的callback(即process_timeout)会唤醒当前进程
del_singleshot_timer_sync(&timer);
/* Remove the timer from the object tracker */
destroy_timer_on_stack(&timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
static inline int __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only, int pinned)
{
struct tvec_base *base, *new_base;
unsigned long flags;
int ret = 0 , cpu;
timer_stats_timer_set_start_info(timer);
BUG_ON(!timer->function);
base = lock_timer_base(timer, &flags);
cpu = smp_processor_id();
······
timer->expires = expires;
//将当前这个timer挂接到合适的链表上,继续跟踪代码会发现,内核根据距离超时的时间长短设置了4个链表(tv1-tv4)
internal_add_timer(base, timer);
out_unlock:
spin_unlock_irqrestore(&base->lock, flags);
return ret;
}