(笔记)Linux内核学习(八)之定时器和时间管理

一 内核中的时间观念

内核在硬件的帮助下计算和管理时间。硬件为内核提供一个系统定时器用以计算流逝的时间。系

统定时器以某种频率自行触发,产生时钟中断,进入内核时钟中断处理程序中进行处理。

墙上时间和系统运行时间根据时钟间隔来计算。

利用时间中断周期执行的工作:

更新系统运行时间;

更新实际时间;

在smp系统上,均衡调度程序中各处理器上运行队列;

检查当前进程是否用尽了时间片,重新进行调度;

运行超时的动态定时器;

更新资源消耗和处理器时间的统计值;

二 节拍率

系统定时器的频率;通过静态预处理定义的——HZ;系统启动按照HZ值对硬件进行设置。体系结构不同,HZ值也不同;HZ可变的。

    //内核时间频率

    #define HZ 1000

提高节拍率中断产生更加频繁带来的好处:

提高时间驱动事件的解析度;

提高时间驱动事件的准确度;

内核定时器以更高的频度和准确度;

依赖顶上执行的系统调用poll()和select()能更高的精度运行;

系统时间测量更精细;

提高进程抢占的准确度;

提高节拍率带来的副作用:

中断频率增高系统负担增加;

中断处理程序占用处理器时间增多;

频繁打断处理器高速缓存;

节拍率HZ值需要在其中进行平衡。

三 jiffies

  jiffies:全局变量,用来记录自系统启动以来产生的节拍总数。启动时内核将该变量初始化为0;

此后每次时钟中断处理程序增加该变量的值。每一秒钟中断次数HZ,jiffies一秒内增加HZ。系统运行时间 = jiffie/HZ.

jiffies用途:计算流逝时间和时间管理

jiffies内部表示:

extern u64 jiffies_64;

extern unsigned long volatile jiffies;     //位长更系统有关32/64

  32位:497天后溢出

  64位:……

//0.5秒后超时

unsigned long timeout = jiffies + HZ/2;
……

//注意jiffies值溢出回绕用宏time_before 而非 直timeout > jiffies
if(time_before(jiffies,timeout)){

       //没有超时
}else{

       //超时
}

四 硬时钟和定时器

  两种设备进行计时:系统定时器和实时时钟。

实时时钟(RTC):用来持久存放系统时间的设备,即便系统关闭后,靠主板上的微型电池提供电力保持系统的计时。

    系统启动内核通过读取RTC来初始化墙上时间,改时间存放在xtime变量中。

系统定时器:内核定时机制,注册中断处理程序,周期性触发中断,响应中断处理程序,进行处理执行以下工作:

  l  获得xtime_lock锁,访问jiffies和更新墙上时间xtime;

  l  更新实时时钟;

  l  更新资源统计值:当前进程耗时,系统时间等;

  l  执行已到期的动态定时器;

  l  执行scheduler_tick()

//中断处理程序
irqreturn_t timer_interrupt(int irq, void *dev)
{
    //ticks have passed
    long nticks;

    xtime_update(nticks);

    while (nticks--)
           update_process_times(user_mode(get_irq_regs()));

    return IRQ_HANDLED;
}

void xtime_update(unsigned long ticks)
{
    //seq锁
    write_seqlock(&xtime_lock);

    do_timer(ticks);

    write_sequnlock(&xtime_lock);
}

void do_timer(unsigned long ticks)
{
    jiffies_64 += ticks;

    //更新墙上时间 ——实际时间
    update_wall_time();

    calc_global_load(ticks);
}

void update_process_times(int user_tick)
{
    struct task_struct *p = current;

    //计算当前进程执行时间
    account_process_tick(p, user_tick);

    //触发软中断TIMER_SOFTIRQ 超时的timer
    run_local_timers();

    //计算进程时间片
    scheduler_tick();

}

五 定时器

定时器:管理内核时间的基础,推后或执行时间执行某些代码。

定时器数据结构:

struct timer_list {
              struct list_head entry;

              //定时值基于jiffies
              unsigned long expires;

              //定时器内部值
              struct tvec_base *base;

              //定时器处理函数
              void (*function)(unsigned long);

              //定时器处理函数参数
              unsigned long data;

              ……
       };

定时器使用:

    struct timer_list my_timer;

       //初始化定时器
       init_timer(&my_timer);

       ……

       //激活定时器
       add_timer(&my_timer);

       //删除定时器
       del_timer(my_timer);

       ……

六 延迟执行

使用定时器和下半部机制推迟执行任务。还有其他延迟执行的机制:

忙等待:

利用节拍,精确率不高

unsigned long delay = jiffies + 2*HZ ; //2秒 节拍整数倍才行;

while(time_before(jiffies,delay))

;

短延迟:延迟时间精确到毫秒,微妙;短暂等待某个动作完成时,比时钟节拍更短;依靠数次循环达到延迟效果。

void udelay(unsigned long usecs)

void mdelay(unsigned long msecs)

schedule_timeout()延迟:使执行的任务睡眠指定时间,达到延迟

signed long __sched schedule_timeout(signed long timeout)
{
       struct timer_list timer;
       unsigned long expire;

       switch (timeout)
       {
         case MAX_SCHEDULE_TIMEOUT:

              //无限期睡眠
              schedule();
              goto out;
         default:
              if (timeout < 0) {
                     current->state = TASK_RUNNING;
                     goto out;
              }
       }
       //超时时间
       expire = timeout + jiffies;

       //初始化一个timer定时器 参数current task
       setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);

       __mod_timer(&timer, expire, false, TIMER_NOT_PINNED);

       schedule();

       del_singleshot_timer_sync(&timer);

       /* Remove the timer from the object tracker */
       destroy_timer_on_stack(&timer);
       timeout = expire - jiffies;

 out:
       return timeout < 0 ? 0 : timeout;
}

static void process_timeout(unsigned long __data)
{
       //唤醒被睡眠的任务
       wake_up_process((struct task_struct *)__data);
}

时间: 2024-08-01 14:18:16

(笔记)Linux内核学习(八)之定时器和时间管理的相关文章

(笔记)Linux内核学习(九)之内核内存管理方式

一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页大小8KB 内核用相应的数据结构表示系统中的每个物理页: <linux/mm_types.h> struct page {} 内核通过这样的数据结构管理系统中所有的页,因此内核判断一个页是否空闲,谁有拥有这个页 ,拥有者可能是:用户空间进程.动态分配的内核数据.静态内核代码.页高速缓存-- 系统中

(笔记)Linux内核学习(三)之进程调度

进程调度: 在可运行态进程之间分配有限处理器时间资源的内核子系统. 一 调度策略 1 进程类型 I/O消耗型进程:大部分时间用来提交I/O请求或是等待I/O请求,经常处于可运行状态,但运行时间短,等待请求过程时处于阻塞状态.如交互式程序. 处理器消耗型进程:时间大都用在执行代码上,除非被抢占否则一直不停的运行. 综合型:既是I/O消耗型又是处理器消耗型. 调度策略要在:进程响应迅速(响应时间短)和最大系统利用率(高吞吐量)之间寻找平衡. 2 调度概念 优先级:基于进程价值和对处理器时间需求进行进

(笔记)Linux内核学习(十)之虚拟文件系统概念

虚拟文件系统 虚拟文件系统:内核子系统VFS,VFS是内核中文件系统的抽象层,为用户空间提供文件系统相关接口: 通过虚拟文件系统,程序可以利用标准Linux文件系统调用在不同的文件系统中进行交互和操作. VFS作为抽象层: 文件系统被安装在一个特定的安装点上,该安装点在全局层次结构中被称作命名空间, 所有的已安装文件系统都作为根文件系统树的枝叶出现在系统中. 文件系统主要的对象: 超级块对象:代表一个已安装文件系统:struct super_block {} 索引节点对象:代表一个文件:stru

(笔记)Linux内核学习(十一)之I/O层和I/O调度机制

一 块I/O基本概念 字符设备:按照字符流的方式被有序访问的设备.如串口.键盘等. 块设备:系统中不能随机(不需要按顺序)访问固定大小的数据片(chunk 块)的设备. 如:硬盘.软盘.CD-ROM驱动器.闪存等.都是通过以安装文件系统的方式使用. 块设备的组成: 扇区:是块设备中最小的可寻址单元(常见大小512字节):是块设备的基本寻址和操作单元. 块:是文件系统最小逻辑可寻址单元,文件系统的抽象,只能通过块访问文件系统.通常包含多个扇区. 当一个块被调入内存时(读入后或等待写出时),它要存储

(笔记)Linux内核学习(七)之内核同步机制和实现方式

一 原子操作 指令以原子的方式执行--执行过程不被打断. 1 原子整数操作 原子操作函数接收的操作数类型--atomic_t //定义 atomic_t v;//初始化 atomic_t u = ATOMIC_INIT(0); //操作 atomic_set(&v,4); // v = 4 atomic_add(2,&v); // v = v + 2 = 6 atomic_inc(&v); // v = v + 1 = 7 //实现原子操作函数实现 static inline vo

(笔记)Linux内核学习(六)之并发和同步概念

一 临界区和竞争条件 临界区:访问和操作共享数据的代码段. 竞争条件:多个执行线程处于同一个临界区中. 处于竞争条件:造成访问的数据或者资源不一致状态: 对资源i的访问:ProcessA和B访问后得到正确的结果应该是9: 进程是并发执行,有可能得到的结果是:8 防止这种情况的发生:保证对资源的访问原子操作. 二 加锁 锁:采用原子操作实现,原子操作不存在竞争. 造成并发原因: l  中断:随时可以打断当前执行的进程代码: l  软中断和tasklet:任何时刻能唤醒调度软中断和tasklet,打

Linux内核学习总结(final)

Linux内核学习总结 符钰婧 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这八周以来,我从拼不出来"Linux"这个词到知道了很多专有名词,也能大概了解Linux的工作机制,这一系列的进步都是一周周积累下来的.现在回过头来看,有种阳光总在风雨后的感觉,虽然这个比喻好像不太恰当. 闲话少说,接下来就进入这次的正题. 一.首先是对Linux操作系统的理解 1.操作系

Linux内核学习-进程

先说几个术语: 一.Linux进程的五个段 下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区都是干什么的.重点:代码段.数据段.堆栈段,这是一个概念堆.栈.全局区.常量区,这是另一个概念1)代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像.代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作--它是不可写的.代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域.这部分

linux内核学习:中断

编程相关 注册中断 int request_irq( unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) typedef irqreturn_t (*irq_handler_t)(int, void *); IRQF_DISABLED 会禁用除本本身以外的其它中断,一般是不用的 IRQF_SAMPLE_RANDOM 可以帮助内核随机数的产生.如果中断产生地毫无规律,可