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相关推荐

  1. linux系统tick维护,Linux时间子系统之(十二):periodic tick

    Linux时间子系统之(十二):periodic tick 作者:linuxer 发布于:2014-12-11 18:59 分类:时间子系统 一.tick device概念介绍 1.数据结构 在内核中 ...

  2. Linux系统——架构浅析

    导语:掐指一算自己从研究生开始投入到Linux的海洋也有几年的时间,即便如此依然对其各种功能模块一知半解.无数次看了Linux内核的技术文章后一头雾水,为了更系统地更有方法的学Linux,特此记录. ...

  3. Linux系统时间与RTC时间【转】

    http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3637782 Linux的RTC驱动相对还是比较简单的,可以将它作为一个普通的字符 ...

  4. linux系统cpu文件,Linux_学堂:当前Linux系统运行的各种参数, 1、CPU   在文件“/proc/ - phpStudy...

    学堂:当前Linux系统运行的各种参数 1.CPU 在文件"/proc/stat"里面就包含了CPU的信息.每一个CPU的每一tick用在什么地方都在这个文件里面记着.后面的数字含 ...

  5. Linux系统时间同步方法小结

    原文装载自:https://www.cnblogs.com/ibnode/p/3573302.html 在Windwos中,系统时间的设置很简单,界面操作,通俗易懂,而且设置后,重启,关机都没关系.系 ...

  6. linux系统cpu性能测试工具

    这里写自定义目录标题 uptime 欢迎使用Markdown编辑器 新的改变 功能快捷键 合理的创建标题,有助于目录的生成 如何改变文本的样式 插入链接与图片 如何插入一段漂亮的代码片 生成一个适合你 ...

  7. 用U盘在虚拟机中装Linux系统

    ** 用U盘在虚拟机中装Linux系统 ** 首先:(U盘启动盘制作) 一,准备相关文件 (1)8G以上U盘 (2)下载虚拟光驱,地址:https://cn.ultraiso.net/xiazai.h ...

  8. cmd调取windows组件的dos命令大全和常用网络工具,及Linux系统下命令大全

    前言 windows与liunx系统,有许多相同,但有些不通的地方,通过一下的比较,可以加深记忆.若在windows下,某个组件不工作,或者网络不通,或许cmd调取windows组件的dos命令大全: ...

  9. Python3.7版---双人联机雷霆战机(2D特效+音效+道具+Linux系统)

    作品原名称:MultiplayerGameEngine-master 作者:Tomcat(好像是这个名字没注意-) 改良者:CSDN阿坤 Hello everyone! Today ,I will t ...

最新文章

  1. 重磅 | 谷歌Waymo最新发布可完全脱离人类操控的自动驾驶汽车
  2. call指令和ret指令的配合使用
  3. linux普通文件的特点是,linux系统文件系统上有哪些特点
  4. Hadoop的分布式文件系统
  5. java国际规范标准,国际化 - Java Servlet 3.1 规范
  6. 高并发解决方案--负载均衡
  7. 黑进iPhone让手机崩溃重启,只需15行代码:iOS漏洞你可知?
  8. 流媒体基本要点简述:如何在H264数据中获取PTS?
  9. 使用RDP报表工具实现交叉报表
  10. 我看谷歌为啥要退出中国市场
  11. 工业元宇宙的价值和发展
  12. 倪光南华为鸿蒙,倪光南谈华为鸿蒙:国产鸿蒙操作系统需要生态支持
  13. ArcGIS配图/地图符号化的一些技巧与相关资料
  14. java8/jdk1.8 官网下载地址
  15. Qt获取图片色值(提供源码)
  16. 关于Amazon AWS —— 免费的午餐不好吃
  17. 工程伦理--4.2 划界法
  18. springBoot管理AOP日志+注解实现
  19. Mock工具wiremock-py
  20. excel打开多个表格文件显示在一个窗口/表格怎么办?excel缩放一个文件,另一个文件也缩放怎么办?excel打开多个文件重叠怎么办?Excel2010文档在同一个窗口中显示问题如何解决

热门文章

  1. typora简历主题推荐
  2. 红黑树动画+红黑树算法详细介绍
  3. 双11福利!买课即赠书——11个品牌的“增长炼金术”+《私域不完全指南》
  4. conda虚拟环境绑定PYTHONPATH
  5. 阿里播放器踩坑记录 进度条重构 video loadByUrl失效解决方案
  6. CSS3盒子边框阴影,内阴影外阴影
  7. 编写程序 - 计算三角形面积
  8. VMware虚拟机nat模式访问外网
  9. micropython开发板有什么用_迷惘的邂逅 的想法: 玩转MicroPython开发板!你和Ta会有什么…...
  10. JS——检测当前浏览器环境为微信OR企业微信