• 前言
  • 思考
  • 延时函数的定义
  • mdelay的定义
  • msleep的定义
  • 延迟函数的使用
  • mdelay的使用
  • msleep的使用
  • 注意事项
  • mdelay的注意事项
  • msleep的注意事项
  • 总结
  • 最后


前言

linux 驱动开发过程中,经常会用到延迟函数:udelaymdelayusleepmsleepusleep_range,所以本篇记录下获取内核延时所用到的API使用,用的比较多的mdelaymsleep。本篇讲下mdelaymsleep的使用

思考

msleepmdelay都是内核的ms级延时函数使用时有何区别,面试题经常会被问到,不会的同学看过来

延时函数的定义

mdelay的定义

延迟函数mdelay的定义位于file: include/linux/delay.h文件中

#ifndef mdelay
44 #define mdelay(n) (\
45         (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \
46         ({unsigned long __ms=(n); while (__ms--) udelay(1000);}))
47 #endif

可以从定义看到,CPU在做忙等待,延时效果是精准延时

msleep的定义

延迟函数msleep的定义位于file: kernel/time/timer.c文件中

2029 /**
2030  * msleep - sleep safely even with waitqueue interruptions
2031  * @msecs: Time in milliseconds to sleep for
2032  */
2033 void msleep(unsigned int msecs)
2034 {      
2035         unsigned long timeout = msecs_to_jiffies(msecs) + 1;
2036 
2037         while (timeout)
2038                 timeout = schedule_timeout_uninterruptible(timeout);
2039 }      
2040 
2041 EXPORT_SYMBOL(msleep);

我们看下schedule_timeout_uninterruptible函数做了哪些事情

1914 signed long __sched schedule_timeout_uninterruptible(signed long timeout)
1915 {
1916         __set_current_state(TASK_UNINTERRUPTIBLE);
1917         return schedule_timeout(timeout);
1918 }
1919 EXPORT_SYMBOL(schedule_timeout_uninterruptible·);

解释下:__set_current_state(TASK_UNINTERRUPTIBLE)是设置当前进程设置不可中断状态, 就是让进程睡够指定时间才能唤醒,睡眠过程不可被中断(即UNINTERRUPTIBLE),意思不会被信号打断提前唤醒,然后schedule_timeout(timeout)函数timeout时间内主动调用schedule()主动让出CPU资源timeout时间,也就是说msleep会有上下文切换的开销,所以说msleep要比实际timeout的时间多延时一点时间

延迟函数的使用

我们就用mdelaymsleep都写个都延时1s,看看他们的延时效果

mdelay的使用

来看个mdelay的使用demo

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>

static int __init my_module_init(void)
{
    printk(KERN_INFO "Module loading...\n");
    
    mdelay(1000); // 延迟 1000 毫秒 (1 秒)

    printk(KERN_INFO "1 second delay finished.\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    printk(KERN_INFO "Module unloading...\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple module using mdelay");
MODULE_AUTHOR("Your Name");

然后编译完放板子上加载驱动

Linux驱动基础 | 延时函数的使用_linux

可以看到mdelay的实验结果是精准延时1s

msleep的使用

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>

static int __init my_module_init(void)
{
    printk(KERN_INFO "Module loading...\n");
    
    msleep(1000); // 延迟 1000 毫秒 (1 秒)

    printk(KERN_INFO "1 second delay finished.\n");
    return 0;
}

static void __exit my_module_exit(void)
{
    printk(KERN_INFO "Module unloading...\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple module using msleep");
MODULE_AUTHOR("Pan Shuai");

然后编译完放板子上加载驱动

Linux驱动基础 | 延时函数的使用_中断上下文_02

可以看到msleep的实验结果是延时略大于1s.

注意事项

mdelay的注意事项

  • 禁止中断:在某些上下文中(例如在中断上下文中使用时),可能会禁止中断,因此需要小心使用 mdelay
  • 延迟精度:比较适合精确的延迟

msleep的注意事项

  • 上下文msleep 不能在中断上下文中调用,因为中断上下文不能被挂起。应在进程上下文中使用。
  • 精度:由于任务调度的性质,msleep 可能无法保证非常精确的延迟

总结

1、msleep是休眠函数,它不涉及忙等待.时间是不准确的,比如msleep(200),大概率是会等待多于200ms的。适用于非实时场景

2、mdelay是忙等待函数,在延迟过程中无法运行其他任务.这个延迟的时间是准确的,所以适用于精准延时的时序场景

最后

本文学习到了msleepmdelay使用时有何区别。本篇文章学废的话,可以一键三连!欢迎关注公众号[Linux随笔录],不定期分享Linux小知识