tick的概念与实现
摘抄并整理 http://www.wowotech.net/timer_subsystem/time_subsystem_index.html
tick的概念
时间子系统1.0
1. 生产者
在每个architecture相关的代码中要有实现clock event和clock source模块。
clock event
就是通过timer硬件的中断处理函数完成的,在此基础上可以构建tick模块,tick模块维护了系统的tick
例如系统存在10ms的tick,每次tick到来的时候,timekeeping模块就增加系统时间
如果timekeeping完全是tick驱动,那么它精度只能是10ms
clock source
为了更高精度,clock source模块就是一个提供tick之间的offset时间信息的接口函数。
但是实际上并没有提供高精度
2.内核消费者
最初的linux kernel中只支持低精度的timer,也就是基于tick的timer。
如果内核采用10ms的tick,那么低精度的timer的最高的精度也只有10ms,想要取得us甚至ns级别的精度是不可能的。
各个内核的驱动模块都可以使用低精度timer实现自己的定时功能。
各个进程的时间统计也是基于tick的,内核的调度器根据这些信息进行调度。
System Load和Kernel Profiling模块也是基于tick的,用于计算系统负荷和进行内核性能剖析。
3.用户消费者
从用户空间空间来看,有两种需求
一种是获取或者设定当前的系统时间的接口函数:例如time,stime,gettimeofday等。
另外一种是timer相关的接口函数:例如setitimer、alarm等,当timer到期后会向该进程发送signal。
问题:
1. 嵌入式设备需要较好的电源管理策略 , 但是当前系统 周期性时钟会导致从低功耗(idle)状态进入高功耗,不符合电源管理的需求
2. 多媒体的应用程序需要非常精确的timer(ns),但是当前系统 不能提供足够精度的timer(ms)
时间子系统2.0
改变:
A. 新增内核配置 CONFIG_NO_HZ_IDLE // 在系统idle的时候,停掉周期性tick
B. 新增 高精度 timer ,同时 保留 低精度 timer
C. 其实 A B 的更改都是在 生产者 这块改动的,内核消费者与用户消费者都没变化
1.时间子系统2.0的选择
CONFIG_GENERIC_CLOCKEVENTS
2.时间子系统2.0下,针对A的配置,三种情况
a. 非idle, 周期性tick idle, 周期性tick : CONFIG_HZ_PERIODIC
b. 非idle, 周期性tick idle,停掉周期性tick : CONFIG_NO_HZ_IDLE
c. 非idle,停掉周期性tick idle,停掉周期性tick : CONFIG_NO_HZ_FULL
3.时间子系统2.0下,针对B的配置,两种情况
a. 无高精度timer : CONFIG_HIGH_RES_TIMERS=n
b. 有高精度timer : CONFIG_HIGH_RES_TIMERS CONFIG_TICK_ONESHOT
// 在 时间子系统上封装出了
// 1. tick device : 负责调度(周期性调度和非周期性调用)
// 2. timer : 负责定时器(高精度定时器和低精度定时器)
tick device 的类型
每一个cpu 有一个 tick device (clock event device) // SMP下 ,多个cpu对应的 tick device 中有一个被选择做global tick device,该device负责维护整个系统的jiffies以及更新哪些基于jiffies进行的全系统统计信息。
tick device 根据 类型 分为两种
periodic tick device
可以按照固定的时间间隔产生tick event
event handler是 tick_handle_periodic (没有配置高精度timer)或 hrtimer_interrupt(配置了高精度timer)
one-shot tick device
设定后只能产生一次tick event,如果要连续产生tick event,那么需要每次都进行设定
event handler是 tick_nohz_handler(没有配置高精度timer)或 hrtimer_interrupt(配置了高精度timer)
tick device 的实例
linux-5.11 ok6410a 采用 了
时间子系统2.0
无高精度timer
非idle, 周期性tick idle, 周期性tick : CONFIG_HZ_PERIODIC
每当底层有新的 clockevent device 加入到系统中的时候
1.会调用 clockevents_register_device 或者clockevents_config_and_register 向通用clockevent layer注册一个新的clockevent设备
2.会调用 tick_check_new_device 通知 tick device layer有新货到来。
3.如果tick device和clockevent device你情我愿,那么就会调用tick_setup_device函数setup这个tick device了。
a.一般而言,刚系统初始化的时候,所有cpu的tick device都没有匹配clock event device
b.因此,该cpu的local tick device也就是global tick device了。
c.而且,如果tick device是新婚(匹配之前,tick device的clock event device等于NULL),那么tick device的模式将被设定为 TICKDEV_MODE_PERIODIC ,即便clock event有one shot能力,即便系统配置了NO HZ。
4. 对应 TICKDEV_MODE_PERIODIC , setup函数 是 tick_setup_periodic // tick_setup_periodic 函数用来设定一个periodic tick device。
5. setup第一部分:设定 event handler 为 tick_handle_periodic // 对于周期性tick device,其clock event device的handler被设定为 tick_handle_periodic。
6. setup第二部分:调用 clockevent device layer 的接口函数 kick off底层的硬件,让其周期性的产生clock event,
time_init
machine_desc->init_time/s3c64xx_timer_init
samsung_pwm_clocksource_init(S3C_VA_TIMER, timer_irqs, &s3c64xx_pwm_variant);
pwm.base = base;
...
pwm.timerclk = clk_get(NULL, "timers");
_samsung_pwm_clocksource_init
samsung_timer_resources
samsung_clockevent_init
clockevents_config_and_register
clockevents_register_device
tick_check_new_device
tick_setup_device
void (*handler)(struct clock_event_device *) = NULL;
tick_setup_periodic(newdev, 0);
tick_set_periodic_handler
dev->event_handler = tick_handle_periodic;
clockevents_program_event
tick_setup_oneshot(newdev, handler, next_event);
request_irq(irq_number, samsung_clock_event_isr, IRQF_TIMER | IRQF_IRQPOLL, "samsung_time_irq", &time_event_device)
samsung_clocksource_init
sched_clock_register
什么时候开始有tick?
time_init
boot cpu ,在其初始化过程中会调用 time_init
这里会启动clocksource的初始化过程。这时候,周期性的tick就会开始了。
smp_init
其他的processor会启动,然后会注册其自己的local timer,这样,各个cpu上的tick就都启动了。
tick 的发生
// tick 发生时,走的是中断
// 这里面主要设置的 是 need_resched
irq_usr
...
...
samsung_clock_event_isr
evt->event_handler/tick_handle_periodic
tick_periodic(cpu);
update_process_times // 根据task_struct 里面时间片相关的成员 做 set_tsk_need_resched
scheduler_tick
sched_clock_tick
curr->sched_class->task_tick(rq, curr, 0);
if (!clockevent_state_oneshot(dev)) return;
next = ktime_add_ns(next, TICK_NSEC);
tick 与 schedule
调用 调度 的 途径有很多
1. 直接调用
进程主动放弃cpu执行调度
2. 系统调用(通过检查 need_resched )
中断返回
系统调用返回用户空间
信号处理完成后返回内核空间
设置 need_resched 的途径还有很多// tick 是 设置 need_resched 的 一个途径
1.
2.
tick 时的 schedule
返回用户空间时调度
need_resched
tick 属于 第一种
__irq_usr
irq_handler
b ret_to_user_from_irq
// 从任务的TI_FLAGS标志判断是否需要处理抢占或者信号
ldr r1, [tsk, #TI_FLAGS]
movs r1, r1, lsl #16
// 处理抢占或者信号
bne slow_work_pending
mov r0, sp @ 'regs'
mov r2, why @ 'syscall'
bl do_work_pending
// 该函数内会调用 schedule
// 此时 表示 没有抢占或者信号要处理
// 或者 抢占或者信号 已经处理完毕
no_work_pending:
...
restore_user_regs fast = 0, offset = 0
__irq_svc
irq_handler
#ifdef CONFIG_PREEMPTION
blne svc_preempt
bl preempt_schedule_irq
__schedule
#endif
svc_exit r5, irq = 1
上一篇:OK6410A 开发板 (八) 66 linux-5.11 OK6410A linux 并发 竞态 与 同步
下一篇:OK6410A 开发板 (八) 64 linux-5.11 OK6410A linux异常解析
推荐阅读最新更新时间:2024-11-08 08:30
设计资源 培训 开发板 精华推荐
- 使用 ROHM Semiconductor 的 BD45331 的参考设计
- 使用 Analog Devices 的 LT1182CS 的参考设计
- LT1952EGN 36V 至 72V 输入至 12V 在 20A 无(光耦合器)同步总线转换器的典型应用电路
- DN1015,使用节省空间的三路输出稳压器驱动大型 TFT-LCD 显示器
- 我的二号
- LT3091EDE GND 引脚参考 SHDN 信号的典型应用
- 【模拟电路】简易星形呼吸灯
- 新风系统(stm32最小系统板+LM385电机驱动+电压转换)
- 【涂鸦智能】智能窗帘机器人-涂鸦版-931640A
- DC2442A,基于具有遥测功能的 LTC4282 200A 热插拔控制器的演示板