内核开发基础-如何使用内核延时

简介: 从事Linux内核开发特别是驱动开发的小伙伴,肯定需要经常使用到定时器,比如,按键的去抖、LED屏幕显存buffer的刷新等。同时,在控制硬件时,可能会用到十分精确地短延时,这时,定时器的精度就不能满足这种需求了,这时就会使用到高精度定时器和忙等延时。今天就来简要说一下如何正确的使用内核提供的delay和sleep函数。

从事Linux内核开发特别是驱动开发的小伙伴,肯定需要经常使用到定时器,比如,按键的去抖、LED屏幕显存buffer的刷新等。同时,在控制硬件时,可能会用到十分精确地短延时,这时,定时器的精度就不能满足这种需求了,这时就会使用到高精度定时器和忙等延时。今天就来简要说一下如何正确的使用内核提供的delay和sleep函数。


这篇文章面对的读者是从事与驱动程序开发,但是,对于内核delay和sleep实现机制不是很熟悉的开发人员。


如何插入delays


首先,你需要回答一个问题,“需要使用delay的代码存在于原子性的上下文中吗?”或者“是否真的需要在原子性的上下文中插入delay吗?”。对于初学者来说,可能对于原子性的上下文不是很理解,下面简要解释一下。


对于任何Linux程序来说,无非运行于以下几种上下文之中:1)进程-用户空间上下文; 2)进程-内核空间上下文;3)硬件中断上下文;4)软件中断上下文;其中,3)和4)为原子性的上下文,其使用遵循以下几种原则:


  1. 不允许访问用户空间。因为其没有处于任何进程上下文之中,而用户空间都是依附于进程上下文的,所以,原子性上下文中,无法将任何进程与用户空间关联;


  1. 原子性的上下文中,使用current指针是没有任何意义的。current指针指向当前的任务(进程或者线程),而原子性的上下文不与任何进程有关系;


  1. 不能执行任何休眠或者调度。原子性的上下文中,不能调用任何可能会引起进程切换、调度、休眠的函数,比如schedul、wait_event、kmalloc、copy_from_user、msleep、del_timer_sync等等。


原子性上下文


好了,解释了原子性的上下文之后,我们言归正传,继续说一下,原子性的上下文中延时函数都有哪些。


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


这三个函数原理,都是通过使CPU处于忙等状态,直到指定的指令周期执行完毕。其中,mdelay函数是udelay函数的简单封装。ndelay不是在所有平台上都能达到ns的延时精度。


这三个函数一般用在需要延时很短时间的硬件驱动程序中,比如,等待网卡寄存器初始化完成,等待直流电机启动完成等。


一般情况下,mdelay是不推荐使用的,msleep是较好的替代方式。


非原子性上下文


非原子性上下文,应该使用*sleep[_range]函数族,该函数族中的任何函数都能正确执行,“合理的”使用这些函数,可以帮助调度器、电源管理模块、驱动程序更好的工作。


-- 基于忙等:
  udelay(unsigned long usecs)
-- 基于hrtimers高分辨率定时器:
  usleep_range(unsigned long min, unsigned long max)
-- 基于jiffies / legacy_timers
  msleep(unsigned long msecs)
  msleep_interruptible(unsigned long msecs)


除了udelay函数,其他函数都会使调用进程睡眠,从而被调度出去,这是合理的,因为简单的忙等会浪费大量的CPU时间,从而严重的降低系统性能。


使用惯例


对于不同的延时时间,使用何种延时函数,内核提供了一些惯例,下面一一说明一下。


延时几微妙(< 10us)


使用udelay


  • 为何选用udelay? 对于嵌入式系统或者speed-steped PC,使用基于hrtimer实现的usleep,往往得不偿失,不过,这依赖于具体的使用环境而定,不过,你需要注意到这一点。


延时几毫秒(10us~20ms)


使用usleep_range


  • 为何不使用msleep(1ms20ms)?原始的解析,一般情况下,msleep(1ms20ms)往往不会达到使用者预期,其延时时长一般会稍微长于1ms~20ms,这对精度十分敏感的应用来说是不可接受的。


  • 为何不存在usleep函数? 因为usleep_range基于hrtimer实现,所以其有很好的精度,而usleep会引起大量的中断,从而降低系统性能。


  • 什么是合理的range?


通过引入范围,调度程序可以自由地将您的唤醒与由于其他原因而发生的任何其他唤醒结合在一起,或者在最坏的情况下,为您的上限触发中断。


您提供的范围越大,则不会触发中断的机会就越大; 这应该与特定代码路径在延迟/性能上可接受的上限之间取得平衡。 此处的精确公差是非常具体的情况,因此,由调用者确定一个合理的范围。


延时较大的毫秒(> 10ms)


使用msleep或者msleep_interruptible


  • msleep和msleep_interruptible的区别?


msleep将当前任务设置为TASK_UNINTERRUPTIBLE,而msleep_interruptible将当前任务设置为TASK_INTERRUPTIBLE,然后调度睡眠。 简而言之,区别在于睡眠是否可以通过信号提前结束。 通常,除非知道需要使用可中断的变体,否则请使用msleep。



相关文章
|
缓存 监控 关系型数据库
二十、Linux性能优化实战学习笔记- 为什么系统的Swap变高了?
swap分区已在系统安装初期已经分配了swap分区。
143 0
|
18天前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
18天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
2月前
|
缓存 编解码 监控
深入探索Linux内核调度机制的奥秘###
【10月更文挑战第19天】 本文旨在以通俗易懂的语言,深入浅出地剖析Linux操作系统内核中的进程调度机制,揭示其背后的设计哲学与实现策略。我们将从基础概念入手,逐步揭开Linux调度策略的神秘面纱,探讨其如何高效、公平地管理系统资源,以及这些机制对系统性能和用户体验的影响。通过本文,您将获得关于Linux调度机制的全新视角,理解其在日常计算中扮演的关键角色。 ###
54 1
|
7月前
|
存储 NoSQL Redis
高性能存储 SIG 月度动态:多项内核特性移植到 6.6,erofs 完成共享特性 POC
高性能存储 SIG 月度动态送达,一键了解各项目当前进展。
|
7月前
|
监控 Linux 编译器
Linux C++ 定时器任务接口深度解析: 从理论到实践
Linux C++ 定时器任务接口深度解析: 从理论到实践
262 2
|
7月前
|
移动开发 网络协议 Shell
最强优化指令大全 | 【Linux技术专题】「系统性能调优实战」终极关注应用系统性能调优及原理剖析(下册)(一)
最强优化指令大全 | 【Linux技术专题】「系统性能调优实战」终极关注应用系统性能调优及原理剖析(下册)
70 0
|
存储 缓存 算法
十九、Linux性能优化实战学习笔记- 为什么系统的Swap变高了?
十九、Linux性能优化实战学习笔记- 为什么系统的Swap变高了?
109 0
十九、Linux性能优化实战学习笔记- 为什么系统的Swap变高了?
|
缓存 固态存储 Ubuntu
十七、Linux性能优化实战学习笔记-如何利用系统缓存优化程序的运行效率?
Buffer 和Cache 的设计目的,是为了提升系统的 I/O 性能。它们利用内存,充当起慢速磁盘与快速CPU 之间的桥梁,可以加速 I/O 的访问速度。
302 0
|
存储 缓存 Prometheus
十九、Linux性能优化实战学习笔记- 为什么系统的Swap变高了?(markdown版本)
十九、Linux性能优化实战学习笔记- 为什么系统的Swap变高了?(markdown版本)
222 0