第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > linux内核那些事之delay延迟技术

linux内核那些事之delay延迟技术

时间:2018-08-28 14:03:41

相关推荐

linux内核那些事之delay延迟技术

在开发驱动过程中在与硬件交互的过程中,不可避免的需要使用到延迟技术,例如在设置外设寄存器后,由于硬件延迟等原因 设置之后并不会立即马上上生效,需要等待延迟一小段时间以便设置能够生效,驱动开发人员不可避免的需要使用到内核delay技术。

采用何种延迟技术一直都是是很多开发人员都没有注意到,Robert Love在《Linux Kernel Development》一书中专门对delay技术做出了比较详细的说明。

Busy Looping

Busy Looping即使用使用一个循环计数进行忙等待,直到满足条件。在满足需延迟的时间之后 退出循环,内核中提供了一种使用tick 计数方法,通过控制延迟多少个tick数目,来控制整个延迟时间,方法如下:

unsigned long timeout = jiffies + 10; /* ten ticks */while (time_before(jiffies, timeout));

jifferes为内核种获取当前clock数目,当获取到的clock数目超过10个时退出循环,time_before函数其本质上就是一个数目比较函数:

#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)

通过上述宏展开 可知timer_before()就是比较a的计数是否在b前面,如果a<b则返回true,否则返回fase 。

上述jiffies是在进入循环前获取到当前系统时钟滴答tick数目,当tick数目超过进入timeout数目之后将会退出循环,意思就是延迟10个滴答数目。jiffies获取到的是系统当前的滴答数目,为了防止缓存等影响不能获取最新的当前系统滴答数目,该变量使用volatie变量。

由于每个cpu的频率不一致,例如如果当前系统频率为1000HZ,则延迟10个滴答数目则为10ms,所以每个cpu延迟的时间并不一致,为了可以增加程序可移值性,延迟具体某个时间可以将要延迟的滴答数目* HZ(系统时钟)方法,下面程序可以修改为:

unsigned long delay = jiffies + 2*HZ; /* two seconds */while (time_before(jiffies, delay));

上述用例采用2*HZ意味这就是延迟2s。

Busy Looping危害

采用上述busy looping方法,使用忙等待的方式,会长期占用cpu资料,使其他任务得不到调度,为了增加防止在忙等待时候可以调度更好优先级得任务,可以在循环种加入cond_resched,其方法如下:

unsigned long delay = jiffies + 5*HZ;while (time_before(jiffies, delay))cond_resched();

在忙等待时间,如果有更高优先级任务,可以将cpu资源让给其他任务,提高整体性能。但是采用cond_resched(),意味着该方法不能在中断函数种使用。其延迟时间也将会不准确,因为其他任务调度时间可能超过延迟时间。在时间要求不准确的场景可以使用上述方法。

Small Delays

busy loops在时间延迟要求为tick整数倍或者时间要求不是很准确场景下使用,如果要求延迟时间段,且时间精确的场景下,内核提供了一系列的函数分别针对microsecond(微秒), nanosecond(纳秒), and millisecond(毫秒)函数,分别为:

void udelay(unsigned long usecs)void ndelay(unsigned long nsecs)void mdelay(unsigned long msecs)

udelay是微秒级的延迟,ndelay是纳秒级的延迟,mdelay是毫秒级别的延迟。特别要指出的是其使用的延迟参照最好不要超过该函数的级别,比如微秒级的延迟最好使用udelay,为了防止数据溢出,其参数最好不要超过微妙级别。

The udelay() function should be called only for small delays because larger delays on fast machines might result in overflow.As a rule, do not use udelay() for delays more than one millisecond in duration. For longer durations, mdelay() works fine。

除了上述几个函数之前,还有几个函数可以用于非atomic场景:

usleep_range(unsigned long min, unsigned long max)msleep(unsigned long msecs)msleep_interruptible(unsigned long msecs)

schedule_timeout()

除了上述方法之外,内核还从调度角度提供延迟方法:schedule_timeout()

该函数可以将当前进程进行休眠,将cpu资源让给其他任务进行调度,直到所设置的延迟时间达到,唤醒。由于当前进行会休眠所以延迟的时间并不是很精确。使用方法如下:

set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(s * HZ);

延迟s秒,再设置之前需要将当前任务状态设置为TASK_INTERRUPTIBLE,意思是当任务被标记为TASK_INTERRUPTIBLE,且处于延迟休眠状态时,如果该任务收到一个signal时,该任务也会被唤醒(即使延迟时间还未到),如果在延迟休眠时间段内不想收到signal唤醒打扰可以设置为一下:

set_current_state(TASK_UNINTERRUPTIBLE);schedule_timeout(s * HZ);

特别要说明的时,在使用schedule_timeout()函数之前,必须将task状态设置为TASK_INTERRUPTIBLE或者TASK_UNINTERRUPTIBLE。

具体schedule_timeout()函数说明可以详见《Linux Kernel Development》

参考资料

《Linux Kernel Development》

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。