Linux 内核中提供了如下 3 个函数分别进行纳秒、微秒和毫秒延迟。
void ndelay(unsigned long nsecs);
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs);
上述延迟的实现原理本质上是忙等待, 它根据 CPU 频率进行一定次数的循环。 有时候,可以在软件中进行这样的延迟:
void delay(unsigned int time)
{
while (time--);
}
ndelay()、udelay()和 mdelay()函数的实现方式机理与此类似。
毫秒时延(以及更大的秒时延)已经比较大了,在内核中,最好不要直接使用mdelay()函数,这将无谓地耗费 CPU 资源,对于毫秒级以上时延,内核提供了下述函数:
void msleep(unsigned int millisecs);
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);
上述函数将使得调用它的进程睡眠参数指定的时间,msleep()、ssleep()不能被打断,而 msleep_interruptible()则可以被打断。
NOTE:受系统 HZ 以及进程调度的影响,msleep()类似函数的精度是有限的。
10.6.2 长延迟
内核中进行延迟的一个很直观的方法是比较当前的 jiffies 和目标 jiffies (设置为当前 jiffies 加上时间间隔的 jiffies) ,直到未来的 jiffies 达到目标 jiffies。代码清单 10.13给出了使用忙等待先延迟 100 个 jiffies 再延迟 2s 的实例。
代码清单 10.13 忙等待时延实例
/*延迟 100 个 jiffies*/
unsigned long delay = jiffies + 100;
while (time_before(jiffies, delay));
/*再延迟 2s*/
unsigned long delay = jiffies + 2*HZ;
while (time_before(jiffies, delay));
与 time_before()对应的还有一个 time_after(),它们在内核中定义为(实际上只是将传入的未来时间 jiffies 和被调用时的 jiffies 进行一个简单的比较) :
#define time_after(a,b) \(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)
为了防止 time_before()和 time_after()的比较过程中编译器对 jiffies 的优化,内核将其定义为 volatile 变量,这将保证它每次都被重新读取。
10.6.3 睡着延迟
睡着延迟无疑是比忙等待更好的方式,随着延迟在等待的时间到来之间进程处于睡眠状态,CPU 资源被其他进程使用。schedule_timeout()可以使当前任务睡眠指定的jiffies 之后重新被调度执行,msleep()和 msleep_interruptible()在本质上都是依靠包含了schedule_timeout() 的 schedule_ timeout_uninterruptible() 和schedule_timeout_interruptible()实现的,如代码清单 10.14 所示。
代码清单 10.14 schedule_timeout()的使用
void msleep(unsigned int msecs)实际上,schedule_timeout()的实现原理是向系统添加一个定时器,在定时器处理函数中唤醒参数对应的进程。
{
unsigned long timeout = msecs_to_jiffies(msecs) + 1;
while (timeout)
timeout = schedule_timeout_uninterruptible(timeout);
}
unsigned long msleep_interruptible(unsigned int msecs)
{
unsigned long timeout = msecs_to_jiffies(msecs) + 1;
while (timeout && !signal_pending(current))
timeout = schedule_timeout_interruptible(timeout);
return jiffies_to_msecs(timeout);
}
代码清单 10.14 第 6 行和第 14 行分别调用 schedule_timeout_uninterruptible()和schedule_timeout_interruptible() , 这两个函数的区别在于前者在调用schedule_timeout()之前置进程状态为 TASK_INTERRUPTIBLE,后者置进程状态为TASK_UNINTERRUPTIBLE,如代码清单10.15 所示
代码清单 10.15 schedule_timeout_interruptible()和schedule_timeout_uninterruptible()
signed long _ _sched schedule_timeout_interruptible(signed long timeout)另外,下面两个函数可以将当前进程添加到等待队列中, 从而在等待队列上睡眠。当超时发生时,进程将被唤醒(后者可以在超时前被打断) ,如下所示:
{
_ _set_current_state(TASK_INTERRUPTIBLE);
return schedule_timeout(timeout);
}
signed long _ _sched schedule_timeout_uninterruptible(signed long timeout)
{
_ _set_current_state(TASK_UNINTERRUPTIBLE);
return schedule_timeout(timeout);
}
sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout);
interruptible_sleep_on_timeout(wait_queue_head_t*q, unsigned long timeout);