A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 梦缠绕的时候 黑马粉丝团   /  2018-9-12 09:36  /  1155 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

短延时

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() 的实现原理是向系统添加一个定时器,在定时器处理函数中唤醒与参数对应的进程。


1 个回复

倒序浏览

很不错,受教了
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马