Linux 内核延时

短延时

Linux 内核中提供了以下 3 个函数分别进行纳秒、微妙和毫秒延迟:

void ndelay(unsigned long nsecs);

void udelay(unsigned long usecs);

void mdelay(unsigned long msecs);

上述延迟的实现原理本质上是忙等待,它根据 CPU 频率进行一定次数的循环。

注意:毫秒延时(以及更大的秒延时)已经比较大了,在内核中,最好不要直接使用 mdelay() 函数,这将耗费 CPU 资源,对于毫秒级以上的延时,内核提供了下述函数:

void msleep(unsigned int millisecs);

unsigned long msleep_interruptible(unsigned int millisecs);

void ssleep(unsigned int seconds);

上述函数将使得调用它的进程睡眠参数指定的时间为 millisecs,msleep()、ssleep() 不能被打断,而 msleep_interrupt() 则可以被打断。

一点题外话,如下:

内核在启动时,会运行一个延迟循环校准(Delay Loop Calibration),计算出 lpj (Loops Per Jiffy),内核启动时会打印如下类似信息:

Calibrating delay loop... 530.84 BogoMIPS(lpj = 1327104)

可以直接在 bootloader 传递给内核的 bootargs 中设置 lpj=1327104,则可以省略掉这个校准过程,节省约百毫秒级的开机时间。


长延迟

在内核中进行延迟的一个很直观的方法是比较当前的 jiffies 和目标 jiffies(设置为当前 jiffies 加上时间间隔的 jiffies),直到未来的 jiffies 达到目标的 jiffies。

代码清单如下:

/* 延迟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(),它们在内核中定义为:
#define time_after(a,b)		\
	(typecheck(unsigned long, a) && \
	 typecheck(unsigned long, b) && \
	 ((long)((b) - (a)) < 0))
#define time_before(a,b)	time_after(b,a)


睡着延迟

睡着延迟无疑是比忙等待更好的方式,睡着延迟是在等待的时间到来之前进程处于睡眠状态,CPU 资源被其他进程使用,在当前任务的休眠到指定的 jiffies 之后再重新被调度执行。

睡着延迟使用 msleep() 和msleep_interrupt() 函数实现,如下所示:

void msleep(unsigned int msecs)
{
	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);
}
在以上的代码中可以看到 msleep() 函数调用了 schedule_timeout_uninterruptible() 函数,msleep_interruptible() 调用了 schedule_timeout_interruptible() 函数,这两个函数的区别在于前者将进程的状态置为 TASK_INTERRUPTIBLE,后者将进程的状态置为 TASK_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);
}
从以上的两个函数可以看出,最终实现延迟功能的函数是 schedule_timeout()。

schedule_timeout() 的实现原理是向系统添加一个定时器,在定时器处理函数中唤醒与参数对应的进程。



猜你喜欢

转载自blog.csdn.net/linuxweiyh/article/details/79130262