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》
linux内核那些事之delay延迟技术相关推荐
- linux内核那些事之buddy(慢速申请内存__alloc_pages_slowpath)(5)
内核提供__alloc_pages_nodemask接口申请物理内存主要分为两个部分:快速申请物理内存get_page_from_freelist(linux内核那些事之buddy(快速分配get_p ...
- linux内核那些事之buddy(anti-fragment机制)(4)
程序运行过程中,有些内存是短暂的驻留 用完一段时间之后就可以将内存释放以供后面再次使用,但是有些内存一旦申请之后,会长期使用而得不到释放.长久运行有可能造成碎片.以<professional l ...
- linux内核那些事之mmap_region流程梳理
承接<linux内核那些事之mmap>,mmap_region()是申请一个用户进程虚拟空间 并根据匿名映射或者文件映射做出相应动作,是实现mmap关键函数,趁这几天有空闲时间 整理下mm ...
- linux内核那些事之buddy
buddy算法是内核中比较古老的一个模块,很好的解决了相邻物理内存碎片的问题即"内碎片问题",同时有兼顾内存申请和释放效率问题,内核从引入该算法之后一直都能够在各种设备上完好运行, ...
- linux内核那些事之pg_data_t、zone结构初始化
free_area_init 继续接着<linux内核那些事之ZONE>,分析内核物理内存初始化过程,zone_sizes_init()在开始阶段主要负责对各个类型zone 大小进行计算, ...
- linux内核那些事之Sparse vmemmap
<inux内核那些事之物理内存模型之SPARCE(3)>中指出在传统的sparse 内存模型中,每个mem_section都有一个属于自己的section_mem_map,如下图所示: 而 ...
- linux内核那些事之buddy(anti-fragment机制-steal page)(5)
继<linux内核那些事之buddy(anti-fragment机制)(4)>,在同一个zone内指定的migrate type中没有足够内存,会启动fallback机制,从fallbac ...
- linux内核那些事之 VMA Gap
rb_subtree_gap kernel为了提高对VMA 查找.插入速度,采用了很多技术,例如VMA采用RB tree和 双向链表两种组织形式,另外对VMA结构对cache line对齐从而提高ca ...
- linux内核那些事之用户空间管理
内核主要数据结构 linux内核将用户空间抽象成struct vm_area_struct进行管理,每申请以个用户空间在内核中都会抽象成对应的vm_are_struct进行管理,同时为了区别不同进程的 ...
最新文章
- JavaScript计算两个文本框内数据的乘积(四舍五入保留两位小数)
- Effective Java读书笔记完结啦
- Codeforces Round #762 (Div. 3)
- java学习(111):日期时间格式化
- kk服务器信息及端口,kk服务器设置
- java 数据字典使用_java中数据字典怎么用?图文详解
- YOLOv3中Anchor理解
- 决策树--CART算法
- 找回丢失的QQ之普及知识全套
- Python moviepy 快速视频剪辑编辑神器
- Web测试要点(功能、性能、可用性、兼容、安全)
- 心脏支架手术后遗症 做完心脏支架手术留下后遗症
- 周易六十四卦——豫卦
- 大企业喜欢使用外包人员驻场开发软件的原因解析
- wps加载项开发和使用
- 论文解读:U-Net: Convolutional Networks for Biomedical Image Segmentation
- 三、Maven-单一架构案例(搭建环境:辅助功能,业务功能:登录)
- 其它基本扩展-URLs-base64加密
- Java缩放PDF内容_如何使用Itext缩放现有的pdf边距
- div设置背景图background:url