Linux延迟和定时器管理

JoyboyCZ
发布于 2023-3-29 17:27
浏览
0收藏

时间是继内存之后常用的资源之一。它用于执行几乎所有的事情:延迟工作、睡眠、调度、超时以及许多其它任务。

时间有两类:

  • 绝对时间:指一天的日期和时间,内核使用绝对时间了解具体时间。有一种硬件芯片称为实时时钟(RTC)。
  • 相对时间:相对时间的作用是被内核调度程序使用,为了处理相对时间,内核依赖于被称作定时器的CPU功能(外设),也称作内核定时器。

内核定时器分为两个不同的部分

  • 标准定时器或系统定时器
  • 高精度定时器

标准定时器

  1. Jiffy和HZ

    Jiffy是在***<linux/jiffies.h>***中声明的内核时间单位,HZ是Jiffies在1s内递增的次数,取决于硬件和内核版本,也决定了时钟中断触发的频率。Jiffies每个增量被称为一个Tick。

    不同的系统上采用了不同类型的Jiffy变量防止溢出。

    <linux/jiffies.h>
    extern u64 __cacheline_aligned_in_smp jiffies_64;
    extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;
    
  2. 定时器API

定时器在内核中表示为timer_list的一个实例:

#include <linux/timer.h>
struct timer_list {
struct list_head  entry;
unsigned long     expires;
struct tvec_base  *base;
void              (*function)(unsigned long);
unsigned long     data;
};
  • expires:定时器到期时间,时间以用节拍表示。
  • entry:双向链表,定时器链表入口。
  • data:传递给回调函数。
  • base:用来指定定时器在哪个CPU上执行。
  • function:定时器到期时执行函数的地址。

初始化定时器

  1. 设置定时器,提供用户定义的回调函数和数据:

    void setup_timer ( struct timer_list *timer, void (*function) (unsigned long), unsigned long data);
    
  2. 设置过期时间。当定时器初始化时,需要在启动回调之前设置它的过期时间:

    int mod_timer( struct timer_list *timer, unsigned long expires);
    
  3. 释放定时器。定时器用过之后需要释放:

    void del_timer(struct timer_list *timer);
    int del_timer_sync(struct timer_list *timer);
    

高精度定时器(HRT)

高精度定时器之所以被称为高精度,是因为它的精度能达到微妙(最高可至纳秒),而标准定时器的精度则为毫秒。标准定时器取决于HZ,而HRT实现是基于ktime。<linux/hrtimer.h>

struct hrtimer {
	struct timerqueue_node		node;
	ktime_t				_softexpires;
	enum hrtimer_restart		(*function)(struct hrtimer *);
	struct hrtimer_clock_base	*base;
	u8				state;
	u8				is_rel;
	u8				is_soft;
};

初始化HRT

  1. 初始化hrtimer。hrtimer初始化之前,需要设置ktime,它代表持续时间:

    static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id, enum hrtimer_mode mode);
    
  2. 启动hrtimer:

    static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode)
    {
    	hrtimer_start_range_ns(timer, tim, 0, mode);
    }
    
  3. 取消hrtimer。取消定时器或者查看是否可能取消它:

int hrtimer_cancel(struct hrtimer *timer)
{
	for (;;) {
		int ret = hrtimer_try_to_cancel(timer);
		if (ret >= 0)
			return ret;
		cpu_relax();
	}
}

int hrtimer_try_to_cancel(struct hrtimer *timer)
{
	struct hrtimer_clock_base *base;
	unsigned long flags;
	int ret = -1;
	if (!hrtimer_active(timer))
		return 0;
	base = lock_hrtimer_base(timer, &flags);
	if (!hrtimer_callback_running(timer))
		ret = remove_hrtimer(timer, base, false);
	unlock_hrtimer_base(timer, &flags);
	return ret;
}

内核中的延迟和睡眠

延迟有两种类型,取决于代码运行的上下文:原子的或非原子的。<linux/delay.h>

  1. 原子上下文

    原子上下文的任务无法进入睡眠状态,无法进行调度,所以延迟必须使用繁忙-等待。

    以下几个函数作用是消耗足够长的时间,达到延迟的效果:

    • ndelay (unsigned long nseus) /* 精度取决于硬件定时器的精度 */

    • udelay (unsigned long usecs)

    • mdelay (unsigned long msecs)

  2. 非原子上下文

    非原子上下文中,内核提供 ***sleep[_range]***系列函数,使用哪个函数取决于需要延迟多长时间。

    • udelay(unsigned long usecs):基于繁忙-等待循环。睡眠时长小于或等于10us左右建议使用该函数。
    • usleep_range(unsigned long min, unsigned long max):依赖于hrtimer,睡眠数微妙到数毫秒(10ms~20ms)时建议使用。
    • msleep(unsigned long msecs):由jiffies/传统定时器支持。对于数毫秒以上的长睡眠(10ms+)建议使用该函数。

收藏
回复
举报
回复
    相关推荐