Linux 系统Tick
1. Tick的作用
操作系统,tick仿佛是人的脉搏,不停的向各个器官提供血液。 Tick在操作系统中,会进行调度,是分时调度最基础的组成部分。在学习Linux内核时,我只知道这个概念,却没能真正看到它是如何实现的。在经历了大约2个周的仔细阅读之后,终于能窥其全貌。
jiffies的更新。
kernel/time/timekeeping.c
void do_timer(unsigned long ticks)
{
jiffies_64 += ticks;
update_wall_time();
calc_global_load(ticks);
}
kernel/time/tick-common.c
static void tick_periodic(int cpu)
{
if (tick_do_timer_cpu == cpu) {
write_seqlock(&jiffies_lock);
/* Keep track of the next tick event */
tick_next_period = ktime_add(tick_next_period, tick_period);
do_timer(1);
write_sequnlock(&jiffies_lock);
}
update_process_times(user_mode(get_irq_regs()));
profile_tick(CPU_PROFILING);
}
/*
* Event handler for periodic ticks
*/
void tick_handle_periodic(struct clock_event_device *dev)
{
int cpu = smp_processor_id();
ktime_t next;
tick_periodic(cpu);
if (dev->mode != CLOCK_EVT_MODE_ONESHOT)
return;
/*
* Setup the next period for devices, which do not have
* periodic mode:
*/
next = ktime_add(dev->next_event, tick_period);
for (;;) {
if (!clockevents_program_event(dev, next, false))
return;
/*
* Have to be careful here. If we're in oneshot mode,
* before we call tick_periodic() in a loop, we need
* to be sure we're using a real hardware clocksource.
* Otherwise we could get trapped in an infinite
* loop, as the tick_periodic() increments jiffies,
* when then will increment time, posibly causing
* the loop to trigger again and again.
*/
if (timekeeping_valid_for_hres())
tick_periodic(cpu);
next = ktime_add(next, tick_period);
}
}
kernel/time/tick-broadcast.c
/*
* Set the periodic handler depending on broadcast on/off
*/
void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast)
{
if (!broadcast)
dev->event_handler = tick_handle_periodic;
else
dev->event_handler = tick_handle_periodic_broadcast;
}
kernel/time/tick-common.c
/*
* Setup the device for a periodic tick
*/
void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
{
tick_set_periodic_handler(dev, broadcast);
/* Broadcast setup ? */
if (!tick_device_is_functional(dev))
return;
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
!tick_broadcast_oneshot_active()) {
clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
} else {
unsigned long seq;
ktime_t next;
do {
seq = read_seqbegin(&jiffies_lock);
next = tick_next_period;
} while (read_seqretry(&jiffies_lock, seq));
clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
for (;;) {
if (!clockevents_program_event(dev, next, false))
return;
next = ktime_add(next, tick_period);
}
}
}
/*
* Setup the tick device
*/
static void tick_setup_device(struct tick_device *td,
struct clock_event_device *newdev, int cpu,
const struct cpumask *cpumask)
{
ktime_t next_event;
void (*handler)(struct clock_event_device *) = NULL;
/*
.....
*/
if (tick_device_uses_broadcast(newdev, cpu))
return;
if (td->mode == TICKDEV_MODE_PERIODIC)
tick_setup_periodic(newdev, 0);
else
tick_setup_oneshot(newdev, handler, next_event);
}
/*
* Check, if the new registered device should be used.
*/
void tick_check_new_device(struct clock_event_device *newdev)
{
struct clock_event_device *curdev;
struct tick_device *td;
int cpu;
unsigned long flags;
........
}
clockevents_exchange_device(curdev, newdev);
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
raw_spin_unlock_irqrestore(&tick_device_lock, flags);
return;
out_bc:
/*
* Can the new device be used as a broadcast device ?
*/
tick_install_broadcast_device(newdev);
raw_spin_unlock_irqrestore(&tick_device_lock, flags);
}
Tick的eventhandler是在clock_event_device的注册过程中设置的。
2. clock_event_device的注册
继续沿着调用关系向下寻找。
kernel/time/clockevents.c
**
* clockevents_register_device - register a clock event device
* @dev: device to register
*/
void clockevents_register_device(struct clock_event_device *dev)
{
unsigned long flags;
BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
if (!dev->cpumask) {
WARN_ON(num_possible_cpus() > 1);
dev->cpumask = cpumask_of(smp_processor_id());
}
raw_spin_lock_irqsave(&clockevents_lock, flags);
list_add(&dev->list, &clockevent_devices);
tick_check_new_device(dev);
clockevents_notify_released();
raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}
void clockevents_config_and_register(struct clock_event_device *dev,
u32 freq, unsigned long min_delta,
unsigned long max_delta)
{
dev->min_delta_ticks = min_delta;
dev->max_delta_ticks = max_delta;
clockevents_config(dev, freq);
clockevents_register_device(dev);
}
还是基于我相对熟悉的Arm作为例子。
drivers/clocksource/arm_arch_timer.c
static void __cpuinit __arch_timer_setup(unsigned type,
struct clock_event_device *clk)
{
clk->features = CLOCK_EVT_FEAT_ONESHOT;
if (type == ARCH_CP15_TIMER) {
clk->features |= CLOCK_EVT_FEAT_C3STOP;
clk->name = "arch_sys_timer";
clk->rating = 450;
clk->cpumask = cpumask_of(smp_processor_id());
if (arch_timer_use_virtual) {
clk->irq = arch_timer_ppi[VIRT_PPI];
clk->set_mode = arch_timer_set_mode_virt;
clk->set_next_event = arch_timer_set_next_event_virt;
} else {
clk->irq = arch_timer_ppi[PHYS_SECURE_PPI];
clk->set_mode = arch_timer_set_mode_phys;
clk->set_next_event = arch_timer_set_next_event_phys;
}
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
clk->rating = 400;
clk->cpumask = cpu_all_mask;
if (arch_timer_mem_use_virtual) {
clk->set_mode = arch_timer_set_mode_virt_mem;
clk->set_next_event =
arch_timer_set_next_event_virt_mem;
} else {
clk->set_mode = arch_timer_set_mode_phys_mem;
clk->set_next_event =
arch_timer_set_next_event_phys_mem;
}
}
clk->set_mode(CLOCK_EVT_MODE_SHUTDOWN, clk);
clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff);
}
static int __cpuinit arch_timer_setup(struct clock_event_device *clk)
{
__arch_timer_setup(ARCH_CP15_TIMER, clk);
......
}
static int __init arch_timer_register(void)
{
int err;
int ppi;
arch_timer_evt = alloc_percpu(struct clock_event_device);
if (!arch_timer_evt) {
err = -ENOMEM;
goto out;
}
........
/* Immediately configure the timer on the boot CPU */
arch_timer_setup(this_cpu_ptr(arch_timer_evt));
return 0;
out_unreg_notify:
unregister_cpu_notifier(&arch_timer_cpu_nb);
out_free_irq:
if (arch_timer_use_virtual)
free_percpu_irq(arch_timer_ppi[VIRT_PPI], arch_timer_evt);
else {
free_percpu_irq(arch_timer_ppi[PHYS_SECURE_PPI],
arch_timer_evt);
if (arch_timer_ppi[PHYS_NONSECURE_PPI])
free_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI],
arch_timer_evt);
}
out_free:
free_percpu(arch_timer_evt);
out:
return err;
}
static void __init arch_timer_init(struct device_node *np)
{
int i;
if (arch_timers_present & ARCH_CP15_TIMER) {
pr_warn("arch_timer: multiple nodes in dt, skipping\n");
return;
}
arch_timers_present |= ARCH_CP15_TIMER;
for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
arch_timer_detect_rate(NULL, np);
/*
* If HYP mode is available, we know that the physical timer
* has been configured to be accessible from PL1. Use it, so
* that a guest can use the virtual timer instead.
*
* If no interrupt provided for virtual timer, we'll have to
* stick to the physical timer. It'd better be accessible...
*/
if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) {
arch_timer_use_virtual = false;
if (!arch_timer_ppi[PHYS_SECURE_PPI] ||
!arch_timer_ppi[PHYS_NONSECURE_PPI]) {
pr_warn("arch_timer: No interrupt available, giving up\n");
return;
}
}
arch_timer_register();
arch_timer_common_init();
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init);
CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init);
arch_timer_init对整个时钟进行设置。
#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \
static const struct of_device_id __clksrc_of_table_##name \
__used __section(__clksrc_of_table) \
= { .compatible = compat, \
.data = (fn == (clocksource_of_init_fn)NULL) ? fn : fn }
CLOCKSOURCE_OF_DECLARE生成一个表项。
3. time_init
arch/arm64/kernel/time.c
void __init time_init(void)
{
u32 arch_timer_rate;
of_clk_init(NULL);
clocksource_of_init();
tick_setup_hrtimer_broadcast();
arch_timer_rate = arch_timer_get_rate();
if (!arch_timer_rate)
panic("Unable to initialise architected timer.\n");
/* Calibrate the delay loop directly */
lpj_fine = arch_timer_rate / HZ;
}
drivers/clocksource/clksrc-of.c
void __init clocksource_of_init(void)
{
struct device_node *np;
const struct of_device_id *match;
clocksource_of_init_fn init_func;
for_each_matching_node_and_match(np, __clksrc_of_table, &match) {
init_func = match->data;
init_func(np);
}
}
整个故事到了尾声, clocksource_of_init将遍历device tree所有的节点,如果找到和__clksrc_of_tablematch的, 就调用相应的函数。
device tree的一个Node:
timer {
compatible = "arm,armv8-timer";
interrupts = <0x1 0x2 0xff08 0x1 0x3 0xff08 0x1 0x4 0xff08 0x1 0x1 0xff08>;
clock-frequency = <0x124f800>;
};
Linux 系统Tick相关推荐
- linux系统tick维护,Linux时间子系统之(十二):periodic tick
Linux时间子系统之(十二):periodic tick 作者:linuxer 发布于:2014-12-11 18:59 分类:时间子系统 一.tick device概念介绍 1.数据结构 在内核中 ...
- Linux系统——架构浅析
导语:掐指一算自己从研究生开始投入到Linux的海洋也有几年的时间,即便如此依然对其各种功能模块一知半解.无数次看了Linux内核的技术文章后一头雾水,为了更系统地更有方法的学Linux,特此记录. ...
- Linux系统时间与RTC时间【转】
http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3637782 Linux的RTC驱动相对还是比较简单的,可以将它作为一个普通的字符 ...
- linux系统cpu文件,Linux_学堂:当前Linux系统运行的各种参数, 1、CPU 在文件“/proc/ - phpStudy...
学堂:当前Linux系统运行的各种参数 1.CPU 在文件"/proc/stat"里面就包含了CPU的信息.每一个CPU的每一tick用在什么地方都在这个文件里面记着.后面的数字含 ...
- Linux系统时间同步方法小结
原文装载自:https://www.cnblogs.com/ibnode/p/3573302.html 在Windwos中,系统时间的设置很简单,界面操作,通俗易懂,而且设置后,重启,关机都没关系.系 ...
- linux系统cpu性能测试工具
这里写自定义目录标题 uptime 欢迎使用Markdown编辑器 新的改变 功能快捷键 合理的创建标题,有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你 ...
- 用U盘在虚拟机中装Linux系统
** 用U盘在虚拟机中装Linux系统 ** 首先:(U盘启动盘制作) 一,准备相关文件 (1)8G以上U盘 (2)下载虚拟光驱,地址:https://cn.ultraiso.net/xiazai.h ...
- cmd调取windows组件的dos命令大全和常用网络工具,及Linux系统下命令大全
前言 windows与liunx系统,有许多相同,但有些不通的地方,通过一下的比较,可以加深记忆.若在windows下,某个组件不工作,或者网络不通,或许cmd调取windows组件的dos命令大全: ...
- Python3.7版---双人联机雷霆战机(2D特效+音效+道具+Linux系统)
作品原名称:MultiplayerGameEngine-master 作者:Tomcat(好像是这个名字没注意-) 改良者:CSDN阿坤 Hello everyone! Today ,I will t ...
最新文章
- 重磅 | 谷歌Waymo最新发布可完全脱离人类操控的自动驾驶汽车
- call指令和ret指令的配合使用
- linux普通文件的特点是,linux系统文件系统上有哪些特点
- Hadoop的分布式文件系统
- java国际规范标准,国际化 - Java Servlet 3.1 规范
- 高并发解决方案--负载均衡
- 黑进iPhone让手机崩溃重启,只需15行代码:iOS漏洞你可知?
- 流媒体基本要点简述:如何在H264数据中获取PTS?
- 使用RDP报表工具实现交叉报表
- 我看谷歌为啥要退出中国市场
- 工业元宇宙的价值和发展
- 倪光南华为鸿蒙,倪光南谈华为鸿蒙:国产鸿蒙操作系统需要生态支持
- ArcGIS配图/地图符号化的一些技巧与相关资料
- java8/jdk1.8 官网下载地址
- Qt获取图片色值(提供源码)
- 关于Amazon AWS —— 免费的午餐不好吃
- 工程伦理--4.2 划界法
- springBoot管理AOP日志+注解实现
- Mock工具wiremock-py
- excel打开多个表格文件显示在一个窗口/表格怎么办?excel缩放一个文件,另一个文件也缩放怎么办?excel打开多个文件重叠怎么办?Excel2010文档在同一个窗口中显示问题如何解决
热门文章
- typora简历主题推荐
- 红黑树动画+红黑树算法详细介绍
- 双11福利!买课即赠书——11个品牌的“增长炼金术”+《私域不完全指南》
- conda虚拟环境绑定PYTHONPATH
- 阿里播放器踩坑记录 进度条重构 video loadByUrl失效解决方案
- CSS3盒子边框阴影,内阴影外阴影
- 编写程序 - 计算三角形面积
- VMware虚拟机nat模式访问外网
- micropython开发板有什么用_迷惘的邂逅 的想法: 玩转MicroPython开发板!你和Ta会有什么…...
- JS——检测当前浏览器环境为微信OR企业微信