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

简介: 从事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 编译器 API
深度剖析Linux内核同步机制:实现高效可靠的并发编程
深度剖析Linux内核同步机制:实现高效可靠的并发编程
|
28天前
|
缓存 编解码 监控
深入探索Linux内核调度机制的奥秘###
【10月更文挑战第19天】 本文旨在以通俗易懂的语言,深入浅出地剖析Linux操作系统内核中的进程调度机制,揭示其背后的设计哲学与实现策略。我们将从基础概念入手,逐步揭开Linux调度策略的神秘面纱,探讨其如何高效、公平地管理系统资源,以及这些机制对系统性能和用户体验的影响。通过本文,您将获得关于Linux调度机制的全新视角,理解其在日常计算中扮演的关键角色。 ###
44 1
|
2月前
|
Linux API 调度
关于在Linux内核中使用不同延迟/休眠机制 【ChatGPT】
关于在Linux内核中使用不同延迟/休眠机制 【ChatGPT】
|
6月前
|
数据采集 缓存 Linux
Linux C++ 应用开发:在Linux单线程应用中精确把握空闲时机
Linux C++ 应用开发:在Linux单线程应用中精确把握空闲时机
105 1
|
11月前
|
网络协议 Linux Android开发
【Linux高性能服务器编程】I/O复用的高级应用
【Linux高性能服务器编程】I/O复用的高级应用
80 0
【Linux高性能服务器编程】I/O复用的高级应用
|
存储 缓存 算法
十五、Linux性能优化实战学习笔记 - Linux内存是怎么工作的
内存管理也是操作系统最核心的功能之一。内存主要用来存储系统和应用程序的指令、数据、缓存等
304 1
|
缓存 固态存储 Ubuntu
十七、Linux性能优化实战学习笔记-如何利用系统缓存优化程序的运行效率?
Buffer 和Cache 的设计目的,是为了提升系统的 I/O 性能。它们利用内存,充当起慢速磁盘与快速CPU 之间的桥梁,可以加速 I/O 的访问速度。
287 0
带你读《C/OS-III内核实现与应用开发实战指南:基于STM32》之二:裸机系统与多任务系统
本书分为两部分,第一部分先教你如何从0到1把uC/OS-III内核写出来,从底层的汇编开始讲解任务如何定义、如何切换,还讲解了阻塞延时如何实现、如何支持多优先级、如何实现任务延时列表以及时间片等uC/OS的核心知识点;第二部分讲解uC/OS-III内核组件的应用以及使用uC/OS-III进行多任务编程。
|
存储 调度
带你读《C/OS-III内核实现与应用开发实战指南:基于STM32》之三:任务的定义与任务切换
本书分为两部分,第一部分先教你如何从0到1把uC/OS-III内核写出来,从底层的汇编开始讲解任务如何定义、如何切换,还讲解了阻塞延时如何实现、如何支持多优先级、如何实现任务延时列表以及时间片等uC/OS的核心知识点;第二部分讲解uC/OS-III内核组件的应用以及使用uC/OS-III进行多任务编程。
|
Linux Shell 容器
怎么用最短时间高效而踏实地学习Linux?
在技术行业里,人才的唯一衡量标准就是技术能力,而技术能力,就代表着你的薪资、职位、话语权。很多人都经历过,跟自己同时入行甚至入行还晚的人,成长速度却远超自己,短短两三年就拉开了差距。 秘密就在于,有些人学习就是比别人快。
下一篇
无影云桌面