linux内核中的延时函数详解

author author     2022-12-04     641

关键词:


内核中涉及的延时主要有两种实现方式:忙等待或者睡眠等待。前者阻塞程序,在延时时间到达前一直占用CPU,而后者是将进程挂起(置进程于睡眠状态并释放CPU资源)。所以,前者一般用在延时时间在毫秒以内的精确延时,后者用于延时时间在毫秒以上的长延时。为了充分利用 CPU 资源,使系统有更好的吞吐性能,在对延迟时间的要求并不是很精确的情况下,睡眠等待通常是值得推荐的。

1、忙等待短延时

内核中提供了如下3个函数用于纳秒、微秒和毫秒级的延时:

void ndelay(unsigned long nsecs); 
void udelay(unsigned long usecs);
void mdelay(unsigned long msecs); //一般不建议直接使用mdelay()函数,这将无谓地耗费 CPU 资源

上述延迟的实现原理本质上是忙等待,它根据 CPU 频率进行一定次数的循环。其本质同如下代码:

void delay(unsigned int time) 

while (time--);

2、忙等待长延时函数

内核中进行延迟的一个很直观的方法是比较当前的 jiffies 和目标 jiffies(设置为当前 jiffies 加上时间间隔的 jiffies),直到未来的 jiffies 达到目标 jiffies。

  • 利用jiffies和time_befor实现延时100个jiffies和2秒的代码:
/*延迟 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_befor()只是一个函数宏,与其对应的还有一个time_after():

#define time_after(a,b)
(typecheck(unsigned long, a) && \\
typecheck(unsigned long, b) && \\
((long)(b) - (long)(a) < 0))

#define time_before(a,b) time_after(b,a)

3、睡眠短延时

3.1 sleep类延时函数

下述函数将使得调用它的进程睡眠参数指定的时间,受系统 HZ 和进程调度的影响,msleep()类似函数的精度是有限的。msleep()、ssleep()不能被打断,而msleep_interruptible()则可以被打断。

void msleep(unsigned int millisecs); 
unsigned long msleep_interruptible(unsigned int millisecs);
void ssleep(unsigned int seconds);

3.2 schedule类睡眠延时函数

signed long  schedule_timeout_interruptible(signed long timeout);

signed long schedule_timeout_uninterruptible(signed long timeout)

schedule_timeout()可以使当前任务睡眠指定的jiffies 之后重新被调度执行,它的实现原理是向系统添加一个定时器,在定时器处理函数中唤醒参数对应的进程。上一小节的sleep类函数的底层实现也是调用它实现的:

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); //返回剩余的延时时间


signed long _ _sched schedule_timeout_interruptible(signed long timeout)

_ _set_current_state(TASK_INTERRUPTIBLE); //置进程状态为 TASK_INTERRUPTIBLE
return schedule_timeout(timeout);


signed long _ _sched schedule_timeout_uninterruptible(signed long timeout)

_ _set_current_state(TASK_UNINTERRUPTIBLE); //置进程状态为 TASK_UNINTERRUPTIBLE
return schedule_timeout(timeout);

3.3 sleep_on类,在等待队列上睡眠的延时函数

函数可以将当前进程添加到等待队列中,从而在等待队列上睡眠。当超时发生时,进程将被唤醒(后者可以在超时前被打断):

sleep_on_timeout(wait_queue_head_t *q, unsigned long timeout); 

interruptible_sleep_on_timeout(wait_queue_head_t*q, unsigned long timeout);


linux内核中的睡眠函数*delay*sleep(代码片段)

...用环境的不同,选择不同的延时2、驱动机制不同3、内核中的计算函数执行的函数三、实测两类函数的延时以及原因1、测试系统中的睡眠函数2、输出结果3、输出结果现象与解释内核中的睡眠、延时函数一、睡眠函数种类1、... 查看详情

linux内核中的睡眠函数*delay*sleep(代码片段)

...用环境的不同,选择不同的延时2、驱动机制不同3、内核中的计算函数执行的函数三、实测两类函数的延时以及原因1、测试系统中的睡眠函数2、输出结果3、输出结果现象与解释内核中的睡眠、延时函数一、睡眠函数种类1、... 查看详情

详解linux内核中的各种内存分配函数:kmallocvmallocslab__get_free_pagesmempoll_alloc

【摘要】本文叙述了在Linux内核中常见的几种内存分配函数及其异同,对理解linux底层内存分配机制有个较好理解。1、kmalloc()kmalloc()函数类似与我们常见的malloc()函数,前者用于内核态的内存分配,后者用于用户态。kmalloc()函数... 查看详情

linux内核中的软中断tasklet和工作队列详解(超详细~)(代码片段)

...址:https://www.bilibili.com/read/cv17094615本文基于Linux2.6.32内核版本。引言软中断、tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的“下半部”(bottomhalf)演变而来。下半部的机制实际上... 查看详情

linux系统调用实现机制详解(内核4.14.4)

linux系统调用实现机制详解(内核4.14.4)https://yq.aliyun.com/articles/522766?spm=a2c4e.11155435.0.0.25d33312xbNbM51.1   linux系统调用介绍linux内核中设置了一组用于实现系统功能的子程序,称为系统调用。和普通库函数调用相似,只是... 查看详情

linux---信号详解(代码片段)

...目标进程发送信号总结保存信号阻塞信号信号相关概念在内核中的表示sigset_t信号操作函数sigprocmask 查看详情

linux源码解析-内核栈与thread_info结构详解(代码片段)

1.什么是进程的内核栈?在内核态(比如应用进程执行系统调用)时,进程运行需要自己的堆栈信息(不是原用户空间中的栈),而是使用内核空间中的栈,这个栈就是进程的内核栈2.进程的内核栈在计算机中是如何描述的?linux... 查看详情

linux中断流程详解

...#xff0c;但是这也是很少的一部分,很多公用的处理函数内核已经实现,linux内核搭建了一个非常容易扩充的中断处理体系。中断系统结构涉及的方面很多,而且分布在很多的函数中, 查看详情

linux——信号详解和实操代码

...sigpending函数        CoreDump什么是用户态?什么是内核态?信号处理的过程 查看详情

virtio前端驱动详解

...模式以及原理,今天就从前端驱动的角度描述下目前Linux内核代码中的virtIO驱动是如何配合后端进行工作的。注:本节代码参考Linux内核3.11.1代码virtIO驱动从架构上来讲可以分为两部分,一个是其作为PCI设备本身的驱动,此驱动... 查看详情

linux内核——多任务内核程序head.s源码详解(代码片段)

Linux内核完全注释:基于0.11内核(修正版V3.0)的第四章,最后一节的实验,多任务内核程序head.s源码详解#多任务内核程序[32]位的启动代码#包含32位模式下的初始化设置代码,时钟中断代码,系统调用中断代码和两个任务代码LATCH=11930... 查看详情

linux内核中sk_buff结构详解(代码片段)

目录1.sk_buff结构体1.1sk_buff在内核中的结构1.2重要的长度len的解析2.sk_buff数据区2.1线性数据区2.2非线性数据区----------------------------------------------------------------------------------------------------------------------------1. 查看详情

epoll使用详解(代码片段)

...中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理... 查看详情

Linux内核中的current_thread_info()内联函数?

】Linux内核中的current_thread_info()内联函数?【英文标题】:current_thread_info()inlinefunctioninLinuxkernel?【发布时间】:2017-08-2720:09:18【问题描述】:我了解到thread_info存储在堆栈的底部。在查看内核源代码时,我试图了解如何在linux... 查看详情

linux内核定时器实验(代码片段)

目录Linux时间管理和内核定时器简介内核时间管理简介内核定时器简介Linux内核短延时函数硬件原理图分析实验程序编写修改设备树文件定时器驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试定时器是我们最常... 查看详情

bsp开发学习4linux内核时间管理(代码片段)

文章目录Linux内核时间管理概述内核延时长延时短延时睡眠延时内核定时器定时器驱动示例高精度定时器高精度定时器API初始化定时器设定超时回调函数。使用hrtimer_start激活该定时器取消定时器再次启动驱动示例Linux内核时间管... 查看详情

bsp开发学习4linux内核时间管理(代码片段)

文章目录Linux内核时间管理概述内核延时长延时短延时睡眠延时内核定时器定时器驱动示例高精度定时器高精度定时器API初始化定时器设定超时回调函数。使用hrtimer_start激活该定时器取消定时器再次启动驱动示例Linux内核时间管... 查看详情

向linux内核添加驱动的步骤详解(代码片段)

...定好,需要根据自己的板子进行适配;2、驱动在内核中的两种形式(1)直接编译进内核:内核启动时自动加载,无须在启动脚步中用insmod加载驱动。坏处是默认加载,没法使之不加载ÿ 查看详情