一、简介

Linux中应用层和驱动层编写代码时都会用到延时,本文主要介绍两种情况下延时功能的实现。

1.应用层:sleep、usleep;

2.驱动层:udelay、mdelay、ssleep、通过jiffies 、定时器、中断底半部;

二、应用层延时

1.sleep

头文件:#include <unistd.h>

原    型:unsigned int sleep(unsigned int seconds);

参    数:seconds:延时时长,单位s;

返回值:0 - 表示成功休眠seconds时长;

other - 表示在延时过程中因信号导致缺少的时长;

注   意:1.sleep的底层逻辑使用nanosleep实现,并且在一些系统中,sleep可以通过alarm和SIGALRM信号实现,但是不要混合调用alarm和sleep;

2.当进程运行到sleep时,进程将进入到可中断休眠态,唤醒休眠的方式:到达延时时间 和 信号中断休眠;

3.在运行sleep的时候,将会释放CPU的占有率,占用的资源较少;

4.在sleep的时候,如果使用信号中断,将会运行sleep下一指令;

关于注意中第三点相关的测试代码及效果:

#include <stdio.h>
#include <unistd.h>int main(int argc,const char argv[])
{printf("strat\n");sleep(10);printf("stop\n");return 0;
}

2.usleep

头文件:#include <unistd.h>

原    型:int usleep(useconds_t usec);

参    数:usec:延时时长,单位us;

返回值:0 - 表示成功休眠usec时长;

-1 - 表示失败,重置errno;

errno为EINTR 由于信号中断导致;

errno为EINVAL设置的休眠时间超出范围;

注   意:1.运行usleep将使线程暂停(至少)usec微秒,睡眠时间可能会因任何系统活动或处理调用所花费的时间或系统计时器的粒度而略微延长;

2.休眠时长范围为0 ~ 1000000,超过范围将报错;

3.在运行usleep的时候,将会释放CPU的占有率,占用的资源较少;

三、驱动层延时

在了解延时方法之前,先了解一下忙等待的概念:一种进程执行状态。进程执行到一段循环程序的时候,由于循环判断条件不能满足而导致处理器反复循环,处于繁忙状态,该进程虽然繁忙但无法前进。忙等待通常效率低下,并且可能导致意外死锁,因为忙等待线程不会释放锁定的资源。

(一)delay函数

1.udelay

头文件:#include <linux/delay.h>

原    型:void udelay(unsigned long usecs);         //基本所有架构都包含有此函数,一般作为内联函数编译;

参    数:usec:延时时长,单位us;

注   意:1.只能处理时间较短的延时,参数usec最大只能设置为2000,超出2000编译会出现__bad_udela错误提示;

2.udelay属于忙等待,延时的时间内不能运行其他的任务;udelay是通过计算处理器速度和使用整数变量loops_per_second循环实现;

3.由于loops_per_second的精度值只有8位,所以当控制的延时较长时,累积的误差将会比较大;

2.mdelay、ndelay

头文件:#include <linux/delay.h>

原    型:void mdelay(unsigned long msecs); //使用 udelay 做循环

void ndelay(unsigned long nsecs); //使用 udelay 做循环

注   意:1.本质都是由udelay实现,都是忙等待函数;

2.编译时遇到 “implicit declaration of function 'udelay' ”问题,一般是由于头文件的问题;

(二)sleep函数

1.msleep、ssleep

头文件:#include <linux/delay.h>

原    型:void msleep(unsigned int millisecs);
               void ssleep(unsigned int seconds);

注   意:1.获取指定(或略长)的延时时间,但不会忙等待;

2.调用时进入不可中断休眠态,不能被信号唤醒;

2.msleep_interruptible

头文件:#include <linux/delay.h>

原    型:unsigned long msleep_interruptible(unsigned int millisecs);

注   意:1.获取指定(或略长)的延时时间,但不会忙等待;

2.调用时可以被信号唤醒,并返回初始请求睡眠周期中剩余的毫秒数,;一般用于等待队列并需要打断休眠唤醒进程时;

(三)jiffes

如果想把执行延迟若干个时钟滴答,或者对延迟的精度要求不高(比如,想延迟整数数目的秒数),最简单的也是最笨的实现如下,也就是忙等待。

unsigned long j = jiffies + jit_delay * HZ;while (jiffies < j)/* nothing */;

jiffies在内核的头文件中声明为volatile类型变量,每次C代码访问都会重新读取,通过循环可以实现延迟作用,但是在忙等待期间处理器被占用,调度器不断运行在内核空间的进程。如果在进入循环之前正好关闭了中断,jiffies 值就不会得到更新,那么 while 循环的条件就永远为真,只能通过断电重启结束循环。

(四)内核定时器

1.定义

用来调度函数在指定的时间(基于内部时钟滴答)执行任务,内核定时器本质是一个数据结构,相关函数定义在<linux/timer.h>。

struct timer_list {
    struct list_head entry;                  //内核定时器链表
    unsigned long expires;                //定时的时间
    void (*function)(unsigned long);  //定时器处理函数(当定时时间到了之后,执行的函数)
    unsigned long data;/*传递给函数的参数,若需要在参数中传递多个数据项,可以将它们捆绑成单个数据结构并且将它的指针强制转换为 unsiged long 的指针传入。*/
    struct tvec_t_base_s *base;
#ifdef CONFIG_TIMER_STATS
    void *start_site;
    char start_comm[16];
    int start_pid;
#endif
};

当在进程上下文之外(即在中断上下文)中运行程序时, 必须遵守下列规则:
        (1)不允许访问用户空间;
        (2)current 指针在原子态没有意义;
        (3)不能进行睡眠或者调度.;

2.应用

1.分配内核定时器的对象

struct timer_list mytimer;
(1)通过jiffies获取当前的时间jiffies:它是内核时钟节拍数,从内核启动开始,这个值就一直在增加。
(2)定时器的频率在内核中可以设置,当前内核设置的频率的值在内核目录下.config中保存着。如:443 CONFIG_HZ=100 ===>它就是内核定时器的频率,即定时器每增加1走的时间是10ms

2.定时器的初始化

mytimer.expires = jiffies + 100;
timer_setup(&mytimer, 定时器处理函数, 0);

3.注册定时器

void add_timer(struct timer_list *timer)//注册定时器,在注册定时器的时候,定时器就开始启动了,并且只会执行一次,//定时器注册只能注册一次,如果再次注册定时器内核会崩溃int mod_timer(struct timer_list *timer, unsigned long expires)//功能:当定时器注册完之后,如果想再次启动定时器,需要借助这个函数

4.注销定时器

int del_timer(struct timer_list *timer)//删除定时器

3.发展现状

因为受到 jitter 、硬件中断,还有其他定时器和其他异步任务的影响,内核定时器暂时处理单任务具有优势,但是不适合在工业环境中生产系统,如果注册之后再次注册,将会导致内核的崩溃。

(五)中断底半部

在中断处理函数中是不能够做延时,耗时,甚至休眠的操作,即中断处理函数只能够做简短,不耗时的,紧急的事情。但是有的时候在中断到来的时候又希望做耗时的操作,所以就产生了矛盾。内核为了解决这一矛盾推出了中断底半部的机制。例如在网卡产生中断的时候,需要去网络上读取数据,这个读取数据的过程就是相对耗时操作,所以可以在中断底半部中完成读取网络数据的过程。中断底半部有软中断,tasklet,工作队列这个三个机制。

1.软中断

软中断在内核中一共有32个,都是给内核使用的,所以驱动工程师一般不能够使用这个软中断来处理相对耗时的操作。

2.tasklet:

tasklet是基于软中断实现的,tasklet没有个数限制,tasklet工作在中断上下文,里面可以做相对耗时的操作,但是不能够做休眠的操作。tasklet是中断的一个部分不能脱离中断单独执行。在中断顶半部执行即将结束的时候给开启底半部标志位置位即可。

1.分配对象struct tasklet_struct{struct tasklet_struct *next;//构成内核链表unsigned long state; //是否触发的状态atomic_t count; //触发的次数bool use_callback; //设置为真表示使用新版的函数union {void (*func)(unsigned long data);void (*callback)(struct tasklet_struct *t);//新版的底半部处理函数};unsigned long data; //向底半部传递的参数};2.对象初始化void tasklet_setup(struct tasklet_struct *t,void (*callback)(struct tasklet_struct *));3.调用执行void tasklet_schedule(struct tasklet_struct *t);

3.工作队列

在内核启动的时候默认会启动一个events线程,这个线程维护了一个工作队列,如果你向这个工作队列中提交work,并唤醒这个休眠的进程,此时就可以回调工作队列的底半部处理函数。工作队列工作于进程上下文,可以脱离中断单独执行。工作队列中以做延时,耗时,甚至休眠的操作。

1.分配对象struct work_struct {/* atomic_long_t data; *///向底半部处理函数中传递的参数struct list_head entry;//构成队列work_func_t func;          //工作队列的底半部处理函数};typedef void (*work_func_t)(struct work_struct *work);2.对象初始化INIT_WORK(_work, _func)3对象的调用bool schedule_work(struct work_struct *work);

Linux中的休眠函数相关推荐

  1. linux内核的延时函数,linux中内核延时函数 (转)

    第一类延时函数原型是:(忙等) void ndelay(unsigned long nsecs); void udelay(unsigned long usecs); void mdelay(unsi ...

  2. linux中进程退出函数:exit()和_exit()的区别

    linux中进程退出函数:exit()和_exit()的区别 (1)_exit()执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核. (2)调用_exit函数时,其会关闭 ...

  3. linux中的umask 函数

    我们创建文件的默认权限是怎么来的?如何改变这个默认权限呢? umask是什么? 当我们登录系统之后创建一个文件总是有一个默认权限的,那么这个权限是怎么来的呢?这就是umask干的事情.umask设置了 ...

  4. Linux中 C++ main函数参数argc和argv含义及用法

    Linux中 C++ main函数参数argc和argv含义及用法 简介 argc 是 argument count的缩写,表示传入main函数的参数个数: argv 是 argument vecto ...

  5. linux之getcwd函数解析,如何在Linux 中使用getcwd()函数

    如何在Linux 中使用getcwd()函数 发布时间:2021-03-15 17:35:21 来源:亿速云 阅读:106 作者:Leah 这期内容当中小编将会给大家带来有关如何在Linux 中使用g ...

  6. linux运行getch吗,在linux中使用getch()函数

    http://blog.csdn.net/xiaoweige207/article/details/6206265 由于在Linux中没有conio.h文件,所以不能直接用getch()函数,下面介绍 ...

  7. 在linux中使用getch()函数

    #include <termio.h>int getch(void) {struct termios tm, tm_old;int fd = 0, ch;if (tcgetattr(fd, ...

  8. linux中的sleep函数和delay函数

    对于做过单片机程序的朋友来说,delay是很常见的函数,通常就是while或者for循环,进行空指令的执行,由于单片机的晶振固定,一个机器周期的时间是固定的,执行多少个空指令, 就可以完成多少个机器周 ...

  9. 时间基础概念及Linux中的时间函数

    时间基础概念及Linux中的时间函数 时间相关概念 GMT 时间 UTC 时间 时区 `Time Zone` 夏令时 `DST` 本地时间 `localtime` Linux 系统中的时间 时钟基础概 ...

最新文章

  1. webpack简易入门
  2. MCU(多点控制单元)
  3. 老大让我优化数据库,我上来就分库分表,他过来就是一jio。。。
  4. android 常用 style,Android中 Styles和Themes
  5. python创建矩阵行向量_python矩阵和向量的转置问题
  6. 数据挖掘之关联分析六(子图模式)
  7. 红山虚拟化一点都不虚
  8. 你可能没有听说过 js中的 DOM操作还有这个: HTMLCollection 和 NodeList
  9. 高并发系统 3 大利器之缓存
  10. textview根据文字行数自动变化大小
  11. 【jQuery学习】—jQuery操作CSS和表格
  12. JSK-386 分段函数【入门】
  13. 图像分割:直方图区域划分及信息统计介绍
  14. java中aop和aoc的区别_你喝到的波尔多AOC、AOP红酒是真的吗?
  15. 小白如何连续两年美赛斩获M奖
  16. ting56 下载 有声小说 超禁忌游戏
  17. 如何筹办一场千人技术峰会?
  18. 微软力挺程序员硬刚996!
  19. 省心!2021精选APP macOS装机必备清单来了
  20. FTPS“严重错误: gnutls_handshake: A TLS fatal alert has been received.”

热门文章

  1. 专升本计算机基础笔记-1
  2. 20230517-外婆
  3. pe系统怎么删除服务器密码,win10系统怎么用pe删除系统开机密码
  4. PS如何快速的去掉斑点和将皮肤变白(磨皮)
  5. 上古卷轴 java_我打通了197KB的《上古卷轴四:湮灭》,诺基亚手机上的那一种...
  6. jumpserver堡垒机部署及应用
  7. 最有效的最新防360拦截方法大全!
  8. gmod无法连接更新服务器未响应,steam gmod更新不动 | 手游网游页游攻略大全
  9. 微信小程序定位地址与胶囊对齐
  10. 志愿者:一块最没有悬念的金牌