Linux定时器

姓名:尤乐航            学号:19029100006   学院:电子工程学院

【嵌牛导读】Linux定时器

【嵌牛鼻子】嵌入式  Linux系统

【嵌牛提问】Linux定时器

【嵌牛正文】

6.1定时器类型

6.1.1sleep() 在不同的Gcc版本编译器,使用的头文件可能略有不同,如果不清楚,查一下资料即可。它主要是让当前线程睡眠指定的秒数,注意是second,秒。其函数原型是unsigned int sleep(unsigned int seconds);其返回值代表被打断定时后,剩余的定时器时间,和下面的定时器有相同的效果。它的缺点是精度不太高,而且容易受信号机制影响,太多定时器有可能导致进程的整体挂起

6.1.2usleep()

这个函数精度比较高,但是基本各个平台都已经不再推荐使用。它是非线程安全的。虽然精度高,但精度却不怎么靠谱。其函数原型如下:int usleep(int micro_seconds);usleep功能把进程挂起一段时间, 单位是微秒(百万分之一秒)。如果返回0表示成功,否则表示出现了错误,可以查看ERRORS的信息来确定错误原因。

6.1.3nanosleep()

这个函数是内核从2.0时才提供的一个函数,这个可以推荐使用替代usleep()这个函数,它的精度更高到了纳秒(1秒= 1000^3纳秒)。它可以使当前进程暂停到指定时间后恢复执行。调用nanosleep将导致进程进入TASK_INTERRUPTIBLE,从而使信号进入TASK_RUNNING状态。所以一定要小心,这极有可能在没有到达指定时间前而被其它信号唤醒。可以通过判断返回值来判断是否出现这种情况(返回-1),在这种情况下,可以在rem中查询剩余的时间。它的函数原型是:

int nanosleep(const struct timespec * req, struct timespec * rem);

struct timespec

{

    time_t  tv_sec;         // seconds

    long    tv_nsec;        // nanoseconds

};

6.2普通定时器

在Linux2.6.4之前的版本上没有高精度定时器的情况下,低精度的定时器就成了首选,但是这种低精度的定时器他有个缺点,他的最高精度只能达到毫秒级别,大概是10ms左右。这种内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,我们可以在他的回调函数中实现一些自己的功能。

struct timer_list {

     struct list_head entry;

     unsigned long expires;

     void (*function)(unsigned long);

     unsigned long data;

     struct tvec_base *base;

};

6.3高精度定时器

而随着内核的不断演进,大牛们已经对这种低分辨率定时器的精度不再满足,而且,硬件也在不断地发展,系统中的定时器硬件的精度也越来越高,这也给高分辨率定时器的出现创造了条件。内核从2.6.16开始加入了高精度定时器架构。为此,内核为高精度定时器重新设计了一套软件架构,它可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动,例如多媒体应用,音频设备的驱动程序等等。如用hrtimer(high resolution timer)表示高精度定时器。

6.1定时器类型

6.1.1sleep() 在不同的Gcc版本编译器,使用的头文件可能略有不同,如果不清楚,查一下资料即可。它主要是让当前线程睡眠指定的秒数,注意是second,秒。其函数原型是unsigned int sleep(unsigned int seconds);其返回值代表被打断定时后,剩余的定时器时间,和下面的定时器有相同的效果。它的缺点是精度不太高,而且容易受信号机制影响,太多定时器有可能导致进程的整体挂起

6.1.2usleep()

这个函数精度比较高,但是基本各个平台都已经不再推荐使用。它是非线程安全的。虽然精度高,但精度却不怎么靠谱。其函数原型如下:int usleep(int micro_seconds);usleep功能把进程挂起一段时间, 单位是微秒(百万分之一秒)。如果返回0表示成功,否则表示出现了错误,可以查看ERRORS的信息来确定错误原因。

6.1.3nanosleep()

这个函数是内核从2.0时才提供的一个函数,这个可以推荐使用替代usleep()这个函数,它的精度更高到了纳秒(1秒= 1000^3纳秒)。它可以使当前进程暂停到指定时间后恢复执行。调用nanosleep将导致进程进入TASK_INTERRUPTIBLE,从而使信号进入TASK_RUNNING状态。所以一定要小心,这极有可能在没有到达指定时间前而被其它信号唤醒。可以通过判断返回值来判断是否出现这种情况(返回-1),在这种情况下,可以在rem中查询剩余的时间。它的函数原型是:

int nanosleep(const struct timespec * req, struct timespec * rem);

struct timespec

{

    time_t  tv_sec;         // seconds

    long    tv_nsec;        // nanoseconds

};

6.2普通定时器

在Linux2.6.4之前的版本上没有高精度定时器的情况下,低精度的定时器就成了首选,但是这种低精度的定时器他有个缺点,他的最高精度只能达到毫秒级别,大概是10ms左右。这种内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,我们可以在他的回调函数中实现一些自己的功能。

struct timer_list {

     struct list_head entry;

     unsigned long expires;

     void (*function)(unsigned long);

     unsigned long data;

     struct tvec_base *base;

};

6.3高精度定时器

而随着内核的不断演进,大牛们已经对这种低分辨率定时器的精度不再满足,而且,硬件也在不断地发展,系统中的定时器硬件的精度也越来越高,这也给高分辨率定时器的出现创造了条件。内核从2.6.16开始加入了高精度定时器架构。为此,内核为高精度定时器重新设计了一套软件架构,它可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动,例如多媒体应用,音频设备的驱动程序等等。如用hrtimer(high resolution timer)表示高精度定时器。

内核的开发者考察了多种数据结构,例如基数树、哈希表等等,最终他们选择了红黑树(rbtree)来组织hrtimer,红黑树已经以库的形式存在于内核中,并被成功地使用在内存管理子系统和文件系统中,随着系统的运行,hrtimer不停地被创建和销毁,新的hrtimer按顺序被插入到红黑树中,树的最左边的节点就是最快到期的定时器,内核用一个hrtimer结构来表示一个高精度定时器:

struct hrtimer {

struct timerqueue_node node;

ktime_t _softexpires;

enum hrtimer_restart (*function)(struct hrtimer *);

struct hrtimer_clock_base *base;

unsigned long state;

......

};


6.4动态时钟

Linux中的时钟事件都是由一个周期时钟提供,不管系统中的clock_event_device是工作于周期触发模式,还是工作于单触发模式,也不管定时器系统是工作于低分辨率模式,还是高精度模式,内核都竭尽所能,用不同的方式提供周期时钟,以产生定期的tick事件,tick事件或者用于全局的时间管理(jiffies和时间的更新),或者用于本地cpu的进程统计、时间轮定时器框架等等。周期性时钟虽然简单有效,但是也带来了一些缺点,尤其在系统的功耗上,因为就算系统目前无事可做,也必须定期地发出时钟事件,激活系统。为此,内核的开发者提出了动态时钟这一概念,我们可以通过内核的配置项CONFIG_NO_HZ来激活特性。有时候这一特性也被叫做tickless,不过还是把它称呼为动态时钟比较合适,因为并不是真的没有tick事件了,只是在系统无事所做的idle阶段,我们可以通过停止周期时钟来达到降低系统功耗的目的,只要有进程处于活动状态,时钟事件依然会被周期性地发出。

在动态时钟正确工作之前,系统需要切换至动态时钟模式,而要切换至动态时钟模式,需要一些前提条件,最主要的一条就是cpu的时钟事件设备必须要支持单触发模式,当条件满足时,系统切换至动态时钟模式,接着,由idle进程决定是否可以停止周期时钟,退出idle进程时则需要恢复周期时钟。

切换到高精度模式后,高精度定时器系统需要使用一个高精度定时器来模拟传统的周期时钟,其中利用了tick_sched结构中的一些字段,事实上,tick_sched结构也是实现动态时钟的一个重要的数据结构,在smp系统中,内核会为每个cpu都定义一个tick_sched结构,这通过一个percpu全局变量tick_cpu_sched来实现,它在kernel/time/tick-sched.c中定义:static DEFINE_PER_CPU(struct tick_sched, tick_cpu_sched);

6.5时钟中断处理

在Linux的0号中断是一个定时器中断。在固定的时间间隔都发生一次中断,也是说每秒发生该中断的频率都是固定的。该频率是常量HZ,该值一般是在100 ~ 1000之间。该中断的作用是为了定时更新系统日期和时间,使系统时间不断地得到跳转。另外该中断的中断处理函数除了更新系统时间外,还需要更新本地CPU统计数。指的是调用scheduler_tick递减进程的时间片,若进程的时间片递减到0,进程则被调度出去而放弃CPU使用权。

Linux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。

“时钟中断”是特别重要的一个中断,因为整个操作系统的活动都受到它的激励。系统利用时钟中断维持系统时间、促使环境的切换,以保证所有进程共享CPU;利用时钟中断进行记帐、监督系统工作以及确定未来的调度优先级等工作。可以说,“时钟中断”是整个操作系统的脉搏。

操作系统对可编程定时/计数器进行有关初始化,然后定时/计数器就对输入脉冲进行计数(分频),产生的三个输出脉冲Out0、Out1、Out2各有用途,很多接口书都介绍了这个问题,我们只看Out0上的输出脉冲,这个脉冲信号接到中断控制器8259A_1的0号管脚,触发一个周期性的中断,我们就把这个中断叫做时钟中断,时钟中断的周期,也就是脉冲信号的周期,我们叫做“滴答”或“时标”(tick)。从本质上说,时钟中断只是一个周期性的信号,完全是硬件行为,该信号触发CPU去执行一个中断服务程序,但是为了方便,我们就把这个服务程序叫做时钟中断。

6.6延迟执行

在linux中,可以设定延时执行命令,以及定时执行命令,延时任务是指在多久以后或在指定的时间系统自动执行命令,延时任务一次设定只能起效一次;定时任务是指在指定的某个时间自动执行某个命令,或程序,它与延时的区别在于定时任务可以重复的执行,例如每天的1点关机,就是定时任务。

常用at命令创建延时任务,具体步骤为:

1.安装at,执行sudo apt install at

2.安装完成后,Ctrl+T打开终端,输入at + [开始执行的时间],回车;

3.在出现的命令行内输入需要执行的指令;

4.按回车可以继续输入第二条命令;

5.所有命令输入完毕后,按Ctrl+d执行。

at·命令可以接受多种时间指定方式,比如at now+2min表示在2分钟后开始执行,at 21:00表示在晚上9点开始执行,at 2018/10/22表示在2018年10月22日开始执行。需要注意的是,对于指定具体时间的运行方式,如果指定的时间已经过了,系统将会在次日这个时间运行程序。

通过at命令开始的程序,在开始执行后似乎没有直接的方法能够停止程序,所以建议在设置命令前,最好确保输入的命令正确。且at是一次性命令,即执行完毕就退出了,不会循环执行。

at可以查看当前等待执行的任务列表和任务id,并允许取消还未执行的列表内任务(atrm)。具体操作可以在终端输入at查看帮助

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容