07定时器和时间管理

1、内核中的时间

节拍率:系统定时器以节拍率自行触发(又称为击中/射中)时钟中断。当时钟中断发生时,内核就通过一种特殊的中断处理程序对其进行处理。

计算时间:由于内核知道两次时间中断的间隔时间。这个间隔时间就称为节拍,内核就是靠这种已知的时钟中断间隔来计算墙上时间(实际时间)和系统运行时间的。

时钟中断周期执行的工作:

  • 更新系统运行时间和实际时间
  • 均衡调度程序各处理器上的运行队列
  • 检查当前进程是否用尽时间片。如果用尽,就重新进行调度
  • 更新资源消耗和处理器时间的统计值

2、节拍率HZ

节拍率是可变的:节拍率是通过静态预处理定义的,大多数体系结构的节拍率都是可调的。

高HZ的优势:

  • 更高的时钟中断解析度可提高时间驱动时间的解析度。HZ = 100时解析度为10ms,HZ = 1000时,解析度为1ms。
  • 提高时间驱动事件的准确度(平均误差是半个时钟中断周期)
  • 依赖定时值执行的系统调用,如poll()和select(),能够以更高的精度运行
  • 提高进程抢占的准确度

高HZ的劣势:

  • 节拍率越高,时钟中断频率越高,系统负担越重。因为处理器要花时间执行时钟中断处理程序。
  • 打乱处理器高度缓存增加耗电。

无节拍的OS:Linux支持无节拍操作,时间间隔并不是固定的,而是按需动态调度和重新设置。可以减少开销,省电。系统空闲时不会被不必要的时钟中断打断,减少了系统的能耗。

3、jiffies

jiffies:这个变量用来记录自系统启动依赖产生的节拍的总数。启动时该变量初始化为0。没次时钟中断处理程序就会增加该变量的值。jiffies一秒内增加的值为HZ;系统运行时间为jiffies/HZ。extern unsigned long volatile jiffies;

为了避免jiffies的溢出;使用了64位的数来对其进行存储;使用jiffies_64变量的初值覆盖jiffies变量:jiffies=jiffies_64。一般访问代码只会读取jiffies_64 的低32位。可以通过get_jiffies_64()函数读取整个64位数值。

 jiffies 的回绕:当其变量的存储值超过它的最大范围之后会发生溢出。继续增加会恢复到0。注意当使用jiffies为比较标准作为判断时,可能因为发生回绕;造成其值为0,发生错误的判断;内核提供了对应的宏来处理这种判断:time_aftertime_beforetime_after_eqtime_before_eq

用户空间和HZ:为了避免内核中的HZ值更改,造成用户空间程序的异常结果;使用USER_HZ来代表用户空间看到的HZ值。通过一定的公式来进行计算;函数jiffies_64_to_clock_t()将64位的单位从HZ转换为USER_HZ。

4、硬时钟和定时器

体系结构提供了两种设备进行计时,一种是系统定时器,另一种是实时时钟。

实时时钟:实时时钟是用来持久存放系统时间的设备。即使系统关闭后,它也可以靠主板上的微型电池提供的电力保持系统的即使。实时时钟最主要的作用是在启动时初始化墙上时间。

系统定时器:系统定时器是内核定时机制中最为重要的角色,系统定时器的根本思想是提供一种周期性触发中断机制。

5、时钟中断处理程序

可以分为两个部分:体系结构相关部分和体系结构无关部分,与体系结构相关的例程作为系统定时器的中断处理程序而注册到内核中,以便在产生时钟中断时,它能够相应地运行。虽然处理程序的具体工作依赖于特定的体系结构,但是绝大多数处理程序最低限度也都要执行如下工作:

体系结构相关部分(硬件中断);主要内容如下:

  • 获得xtime_lock锁,保护时间戳访问
  • 应答或者重新设置系统时钟(需要时)
  • 周期性地使用墙上时间更新实时时钟
  • 调用体系结构无关的时钟例程:tick_periodic()

体系结构无关部分(中断处理),使用tick_periodic()执行下面的更多工作:

  • 给jiffies_64变量增加1
  • 更新资源消耗的统计值,比如当前进程所消耗的系统时间和用户时间
  • 执行已经到期的动态定时器
  • 执行sheduler_tick()函数
  • 更新墙上时间,该时间存放在xtime变量中
  • 计算平均负载值

6、实际时间/墙上时间

数据结构:

struct timespec xtime;
struct timespec{
        _kernel_time_t tv_sec;        //秒
        long tv_nsec;                        //ns
}

xtime.tv_sec以秒为单位,存放着自1970.1.1以来经过的时间,1970.1.1被称为纪元,多数Unix系统的墙上时间都是基于该纪元而言的。xtime.v_nsec记录自上一秒开始经过的ns数。

读写需要加锁:读写xtime变量需要使用xtime_lock锁,该锁不是普通自旋锁而是一个seqlock锁

7、定时器

定时器(有时称为动态定时器或内核定时器)是管理内核流逝的时间的基础。如之前的下半部,将工作放到以后执行。但是之后这个概念很含糊,我们需要一种工具使工作在指定时间点上执行,可以用内核定时器解决这个问题。

定时器的使用很简单,只需要执行一些初始化工作,设置一个超时时间,指定超时发生后执行的函数,然后激活定时器。指定的函数将在定时器到期时自动执行。注意定时器不周期运行,超时后自动撤销。定时器被称为动态定时器的原因是:动态定时器不断地创建和撤销。

定时器由结构timer_list表示:

struct timer_list{
  struct list_head entry;           /* 定时器链表的入口 */
  unsigned long expires;            /* 以jiffies为单位的定时值 */
  void (*function)(unsigned long)   /* 定时器处理函数 */
  unsigned long data;               /* 传给处理函数的长整型参数 */
  struct tvec_t_base_s *base;       /* 定时器内部值,用户不要使用 */
}

定时器使用方法如下:

/* 创建定时器 */
struct timer_list my_timer;
/* 初始化内部定时器结构,分配内存 */
init_timer(&my_timer);
/* 预定义超时函数 */
void my_timer_function(unsigned long data);
/* 填充结构中需要的值 */
my_timer.expires=jiffies+delay /* 定时器超时时的节拍数 */
my_timer.data=0;                /* 给定时器处理函数传入0值 */
my_timer.function=my_function;   /* 定时器超时时函数的调用 */
/* 启动激活定时器 */
add_timer(&my_timer);
/* 更改定时器超时时间;设置新的定时器 */
mod_timer(&my_timer,jiffies+new_delay);
/* 删除定时器 */
del_timer(&my_timer);
/* 异步等待并删除定时器 */
del_timer_sync(&my_timer);

定时器条件竞争:因为定时器和当前执行代码的异步性,因此可能存在条件竞争;应该尽量使用del_timer_sync()取代del_timer()函数。因为内核异步执行中断处理程序,所以应该重点保护定时器中断处理程序中的共享数据。

实现定时器:内核在时钟中断之后会执行定时器,更新当前时间。

void run_local_timers(void)
{
  hrtimer_run_queues();
  /* 执行定时器软中断 */
  raise_softirq(TIMER_SOFTIRQ);
  softlockup_tick();
}

执行中断,在当前处理器上运行所有的超定时器。为了提高定时器的搜索效率,内核将定时器按照超时时间划分为五组。当定时器超时时间接近时,定时器将随组一起下移。保证了定时器管理代码的高效性。

8、延迟执行

内核提供了许多延迟方法处理各种延迟要求,有些是在延迟任务时挂起处理器,防止处理器执行任何实际工作;另一些不会挂起处理器,所以也不能确保被延迟的代码能够在指定的延迟时间运行。

忙等待:在循环中不断旋转直到时钟节拍数耗尽。

缺点:

  • 间隔比较大,100HZ的时钟中断,间隔有10ms。
  • 浪费处理器资源

在代码等待时,允许内核重新调度执行其他任务。因为需要调度,所以不能在中断上下文使用。

短延迟:基于jiffies的延迟方法间隔太大,需要更精确的延迟要求。内核知道处理器在1s内能执行多少次循环,所以可以依靠循环次数来到延迟效果。udelay、ndelay、mdelay

schedule_timeout():更为理想的延迟执行方法,该方法会让需要延迟执行的任务睡眠到指定的延迟时间耗尽后再重新运行。但该方法也不能保证时间精确。

注意:需要调用调度程序,所以调用它的代码必须保证能够睡眠。即必须处于进程上下文,且不能持有锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值