linux内核 时间同步机理分析笔记

1 内核时间管理的相关组件

1.1 clocksource 和 clock_event_device

1.1.1 简介

外部时钟设备的主要作用是提供精确的计时功能和定期产生中断的功能,内部把提供精确计时的功能抽象为clocksource对象,把定期产生中断的功能抽象为clock_event_device对象。                
                                《Linux内核精析》12.2.1 clocksource概述

                                《深⼊ LINUX 内核架构》P716

1.1.2 常见的外部时钟设备

TSC,HPET,ACPI PMT
                                《精通Linux内核开发》10.1 时间表⽰
                                《深⼊理解linux内核》P229

1.1.3 调试

/sys/devices/system/clocksource/
/sys/devices/system/clockevents/

1.2 timekeeping模块

1.2.1 简介

struct timekeeper定义在include/linux/timekeeper_internal.h中,保存了各种计时值。它是维护并操纵不同时间线的计时数据的主要数据结构,比如单调时间和原始时间
                                《Linux内核精析》12.3.2 timeval和timespec

1.2.2 数据结构

tk_core 

//kernel/time/timekeeping.c
/** The most important data for readout fits into a single 64 byte* cache line.*/
static struct {seqcount_t      seq; struct timekeeper   timekeeper;
} tk_core ____cacheline_aligned = {.seq = SEQCNT_ZERO(tk_core.seq),
};

 struct timekeeper;

//include/linux/timekeeper_internal.h
/*** struct timekeeper - Structure holding internal timekeeping values.* @tkr_mono:       The readout base structure for CLOCK_MONOTONIC* @tkr_raw:        The readout base structure for CLOCK_MONOTONIC_RAW* @xtime_sec:      Current CLOCK_REALTIME time in seconds* ......*/
struct timekeeper {struct tk_read_base tkr_mono;struct tk_read_base tkr_raw;u64         xtime_sec;unsigned long       ktime_sec;......
};

struct tk_read_base;

//include/linux/timekeeper_internal.h
/*** struct tk_read_base - base structure for timekeeping readout* @clock:  Current clocksource used for timekeeping.* @mask:   Bitmask for two's complement subtraction of non 64bit clocks* @cycle_last: @clock cycle value at last update* @mult:   (NTP adjusted) multiplier for scaled math conversion* @shift:  Shift value for scaled math conversion* @xtime_nsec: Shifted (fractional) nano seconds offset for readout* @base:   ktime_t (nanoseconds) base time for readout* @base_real:  Nanoseconds base value for clock REALTIME readout* ......*/struct tk_read_base {struct clocksource  *clock;u64         mask;u64         cycle_last;u32         mult;            /********* 时间同步的关键变量 ********/u32         shift;u64         xtime_nsec;ktime_t         base;u64         base_real;
};

1.2.3 struct timekeeper中时间变量的更新流程

tick_sched_timer();-> tick_sched_do_timer();-> tick_do_update_jiffies64();                                      -> update_wall_time();                                          -> timekeeping_advance();                                   -> accumulate_nsecs_to_secs();                          -> k->xtime_sec++;                                  -> timekeeping_update();                                -> tk_update_ktime_data(tk);                        -> tk->ktime_sec = seconds;                     -> tk->tkr_mono.base = ns_to_ktime(seconds * NSEC_PER_SEC + nsec);

2 计算时间的流逝

时钟源硬件会产生固定周期的物理信号送给外部时钟设备,时钟设备硬件可以记录收到了多少个周期的时钟信号。
内核代码读取时钟设备硬件记录的周期数,然后将其转换成时间,周期数转换成时间的算法如下:

static inline s64 clocksource_cyc2ns(u64 cycles, u32 mult, u32 shift)
{return ((u64) cycles * mult) >> shift;
}

时钟源硬件并不总是精确的,它们的频率可能不⼀样。这个时钟变化会导 致时间漂移。在这种情况下,可以调整mult变量来弥补这个时间漂移。
                                《精通Linux内核开发》10.2 硬件抽象

3 内核时间同步的关键变量:mult

应用层的时间同步程序如何修改内核的mult变量

应用层的时间同步程序(chronyd, phc2sys等)最终都会调用内核的do_adjtimex()来进行时间调整,这个流程会修改mult变量,如下:

do_adjtimex();-> __do_adjtimex();-> ntp_update_frequency();-> tick_length     += new_base - tick_length_base;-> timekeeping_advance();-> timekeeping_adjust();    //Adjust the multiplier to correct NTP error-> tk->ntp_tick = ntp_tick_length();-> mult = div64_u64((tk->ntp_tick >> tk->ntp_error_shift) -tk->xtime_remainder, tk->cycle_interval);-> timekeeping_apply_adjustment(tk, offset, mult - tk->tkr_mono.mult);-> tk->tkr_mono.mult += mult_adj;

4 抓取实际的内核数据进行验证

4.1 查看当前clocksource的频率

当前系统的clocksource是TSC,如下:

# cat /sys/devices/system/clocksource/clocksource0/current_clocksource 
tsc

TSC时钟源的频率是 2419.200 MHz,信息如下:

# dmesg | grep -i TSC
[    0.000000] tsc: Detected 2400.000 MHz processor
[    0.000000] tsc: Detected 2419.200 MHz TSC
[    0.044651] TSC deadline timer available
[    0.159823] clocksource: tsc-early: mask: 0xffffffffffffffff max_cycles: 0x22df1149949, max_idle_ns: 440795312789 ns
[    0.778100] clocksource: Switched to clocksource tsc-early
[    0.805072] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x22df1149949, max_idle_ns: 440795312789 ns
[    0.805086] clocksource: Switched to clocksource tsc

4.2 使用kprobe模块抓取内核的mult和shift变量

4.2.1 查看tk_core结构体对象的地址

数据结构关系如下,要想抓取mult和shift变量,我们需要首先获取tk_core。

通过/proc/kallsyms文件中查看到的tk_core地址为0xffffffffae4a0100,信息如下:

# cat /proc/kallsyms | grep tk_core
ffffffffae4a0100 b tk_core

4.2.2 实现kprobe模块

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>#include <linux/timekeeper_internal.h>
#include "your_kernel_src/kernel/time/tick-internal.h"
#include "your_kernel_src/kernel/time/ntp_internal.h"
#include "your_kernel_src/kernel/time/timekeeping_internal.h"#define MAX_SYMBOL_LEN  64
static char symbol[MAX_SYMBOL_LEN] = "do_adjtimex";
module_param_string(symbol, symbol, sizeof(symbol), 0644);struct test_tk_core {seqcount_t      seq;struct timekeeper   timekeeper;
}; struct test_tk_core * tk_core = 0xffffffffae4a0100;   /* /proc/kallsyms中查看到的tk_core地址 *//* For each probe you need to allocate a kprobe structure */
static struct kprobe kp = {.symbol_name    = symbol,
};/* kprobe pre_handler: called just before the probed instruction is executed */
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{printk("--------------- clocksource name:%s, mult=%u, shift=%u \n", tk_core->timekeeper.tkr_mono.clock->name,tk_core->timekeeper.tkr_mono.mult,tk_core->timekeeper.tkr_mono.shift);/* A dump_stack() here will give a stack backtrace */return 0;
}/* kprobe post_handler: called after the probed instruction is executed */
static void handler_post(struct kprobe *p, struct pt_regs *regs,unsigned long flags)
{printk("--------------- clocksource name:%s, mult=%u, shift=%u \n", tk_core->timekeeper.tkr_mono.clock->name,tk_core->timekeeper.tkr_mono.mult,tk_core->timekeeper.tkr_mono.shift);
}static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);/* Return 0 because we don't handle the fault. */return 0;
}static int __init kprobe_init(void)
{int ret;kp.pre_handler = handler_pre;kp.post_handler = handler_post;//kp.fault_handler = handler_fault;ret = register_kprobe(&kp);if (ret < 0) {pr_err("register_kprobe failed, returned %d\n", ret);return ret;}pr_info("Planted kprobe at %p\n", kp.addr);return 0;
}static void __exit kprobe_exit(void)
{unregister_kprobe(&kp);pr_info("kprobe at %p unregistered\n", kp.addr);
}module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");

将上面代码编译成内核模块(.ko),然后insmod安装即可。

4.3 对kprobe模块抓取到的数据进行分析

4.3.1 本机时间准确时

本机时间准确时,使用dmesg看到kprobe模块抓取的信息如下:

[21821.544394] --------------- clocksource name:tsc, mult=6935128, shift=24 
[21821.544395] --------------- clocksource name:tsc, mult=6935128, shift=24

时钟频率是2419.200 MHz

2419200000 * 6935128 >> 24 = 1000 014 642ns

4.3.2 将本机时间调慢7分钟

将本机时间比标准时间调慢7分钟,使用dmesg看到kprobe模块抓取的信息如下:

[20967.796255] --------------- clocksource name:tsc, mult=7628528, shift=24 
[20967.796257] --------------- clocksource name:tsc, mult=7628528, shift=24

2419200000 * 7628528 >> 24 = 1099 999 841ns

4.3.3 将本机时间调快6分钟

将本机时间比标准时间调快6分钟,使用dmesg看到kprobe模块抓取的信息如下:

[21149.432284] --------------- clocksource name:tsc, mult=6241523, shift=24 
[21149.432288] --------------- clocksource name:tsc, mult=6241523, shift=24

2419200000 * 6241523 >> 24 = 899 999 883ns

5 总结

当linux内核记录的时间比标准时间慢时,时间同步程序会修改内核的mult变量,让内核时间走的快一些;
当linux内核记录的时间比标准时间快时,时间同步程序会修改内核的mult变量,让内核时间走的慢一些。

扩展,时间同步的时间源

PTP, PPS: Linux时间同步(PPS、PTP、chrony)分析笔记_linux pps 授时-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/52098.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信支付流程

1. 创建订单 请求创建订单的 API 接口&#xff1a;把 订单金额、收货地址、订单中包含的商品信息 发送到服务器服务器响应的结果&#xff1a;订单编号 2.订单预支付 请求订单预支付的 API 接口&#xff1a;把步骤1得到的 订单编号 发送到服务器服务器响应的结果&#xff1a;…

P9145 [THUPC 2023 初赛] 世界杯

[题目通道]([THUPC 2023 初赛] 世界杯 - 洛谷) 简要题意&#xff1a;输出五常中的最强球队。 众所周知&#xff0c;每个国家的球队都有自己的长处&#xff0c;在不同规则下最强球队也有所不同。而小 M 制定的规则是输球场数最少&#xff0c;这是有道理的&#xff0c;因为输球…

《黑神话:悟空》全网互动量超1.8亿,百万天命人重走西游

四年磨一剑&#xff0c;一剑破长空。 8月20日10点&#xff0c;《黑神话&#xff1a;悟空》正式开服&#xff0c;同时轰动了国内外游戏圈子&#xff0c;冲顶国内各大社媒平台&#xff0c;一天包揽120个热搜。上线之后&#xff0c;《黑神话&#xff1a;悟空》火速登顶Steam热销游…

网络通信编程UDP/TCP

一、不同体系结构间的互联互通 通信本质&#xff1a;不同主机上进程间的通信。通过 IP 标识网络中的一台主机&#xff0c;本质上是 32 位的整型数据&#xff1b;通过端口号标识某个进程&#xff0c;本质上是 16 位的整型数据。网络模型&#xff1a;主要有 C/S&#xff08;客户…

克服编程学习中的挫折感:从心态到策略的全方位指南

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《热点时事》 期待您的关注 目录 引言 一、心态调整&#xff1a;积极乐观&#xff0c;合理期望 二、学习方法&#xff1a;有效策…

一起学Java(3)-Java项目构建工具Gradle和Maven场景定位和优缺点对比

在第一步创建的项目&#xff08;java-all-in-one&#xff09;项目里&#xff0c;我们提到了使用Gradle作为项目构建工具。看到这里&#xff0c;不知道你是否有疑惑&#xff0c;什么是项目构建工具。Java项目常用构建工具有哪些&#xff1f;都有什么特点&#xff1f; 带着疑惑&…

【教程】Ubuntu给pycharm添加侧边栏快捷方式

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 以下教程不仅限于pycharm&#xff0c;其他软件也是一样操作 1、进入到pycharm的目录&#xff0c;先通过命令行打开pycharm&#xff1a; ./bin/pycharm…

ETL开发团队应对突发技术故障和危机的策略

建立有效的监控和警报系统 首先&#xff0c;建立有效的监控和警报系统是应对突发技术故障和危机的重要步骤。通过监控ETL流程和相关系统的关键指标&#xff0c;例如数据传输速度、资源利用率和错误日志&#xff0c;您可以提前发现潜在的问题。设置警报机制&#xff0c;及时通知…

[Leetcode 61][Medium]-旋转链表

目录 一、题目描述 二、整体思路 三、代码 一、题目描述 原题链接 二、整体思路 首先发现这样的规律&#xff1a;当k大于等于链表中节点总数n时&#xff0c;会发现此时旋转后的链表和kk%n时的旋转后的链表一样。同时对于特殊情况n0和n1时&#xff0c;无论k的值为多少都可以…

C程序设计——常量后缀

本节内容&#xff0c;如果感觉学的比较吃力&#xff0c;可以最后再翻回来看。 上一节&#xff0c;我们讲了两种常量&#xff0c;在这里我个人给他们起名字&#xff0c;分别叫做数值常量、名称常量。 下面这些是数值常量&#xff1a; // 下面是数值常量 987; 0123; 0xba9; 6.…

关于HTTP HEAD介绍

一、HTTP HEAD介绍 HTTP HEAD 是一种 HTTP 请求方法&#xff0c;它用于请求服务器返回指定资源的元信息&#xff08;metadata&#xff09;&#xff0c;而不包括响应体的内容。这种请求方式常用于客户端预先评估资源的大小、最后修改日期或其他头信息&#xff0c;而无需实际下载…

论文翻译:Universal and Transferable Adversarial Attacks on Aligned Language Models

Universal and Transferable Adversarial Attacks on Aligned Language Models https://arxiv.org/pdf/2307.15043v2 通用且可转移的对抗性攻击对齐语言模型 文章目录 通用且可转移的对抗性攻击对齐语言模型摘要1 引言2 一个针对LLMs的通用攻击2.1 产生肯定回应2.2 贪婪坐标梯…

Prometheus Alertmanager告警之邮件、钉钉群、企业微信群机器人报警

文章目录 一、部署alertmanager相关组件1.alertmanager-config2.alertmanager-message-tmpl3.alertmanager 二、调试邮件告警三、钉钉群/企业微信群 报警3.1添加钉钉群机器人3.2添加企业微信群机器人3.3部署alertmanager-webhook-adaptermessage-tmplalertmanager-webhook-adap…

Vue组件命名报错 Component name “index“ should always be multi-word

原因 在Vue创建组件时我们往往会使用 index.vue 之类来命名&#xff0c;但是此时组件会报错【Component name "index" should always be multi-word】&#xff0c;组件名应该总是为多个单词组成。这是因为官方文档建议组件名写成驼峰式或者-衔接的形式&#…

【PHP入门教程】PHPStudy环境搭建+HelloWorld运行

文章目录 PHP 的历史PHP 的用途PHP 的特点和优势PHP 环境搭建环境准备安装window 安装CentOS / Ubuntu / Debian 安装 第一个Hello World使用Apache服务运行命令行运行代码 PHP 的历史 PHP&#xff08;Hypertext Preprocessor&#xff09;超文本预处理器是一种开源的通用脚本语…

哈希环算法(C语言版本)

文章目录 理论分析&#xff1a;经典案例案例一&#xff1a;开放寻址法案例二&#xff1a;经典哈希环算法案例总结 理论分析&#xff1a; 所谓的哈希环就是就是数据结构里面的数组&#xff0c;只不过我们通过设置使其首尾相连&#xff0c;而这个数组又非常的大&#xff0c;这些…

设计模式实战总结与Java程序员学习路线图

经过一系列的设计模式实战练习,从在线购物系统到旅行预订系统,本专栏已涵盖了多种常用设计模式的应用与实现。本文将对整个专栏进行总结,并为Java程序员提供一份学习路线图,帮助更好地规划未来的学习与职业发展方向。 一、设计模式学习总结 本专栏分为26篇文章,内容涵盖…

网络硬件升级指南:提升性能的策略与实践

随着企业对网络依赖程度的增加&#xff0c;网络性能的提升已成为信息技术部门的首要任务。本文将探讨如何通过升级网络硬件来提高网络性能&#xff0c;包括选择正确的硬件、实施升级策略和考虑未来网络的可扩展性。 一、网络性能的重要性 在数字化时代&#xff0c;网络是企业…

[BaseCTF 2024] 高校联合新生赛 Crypto (week1-7)

半个月没有落笔了。又到开学季&#xff0c;老油条们又在教新生了。题目很多&#xff0c;本想过于简单就不提交了&#xff0c;可以总是想起去年&#xff0c;到最后总是忘掉哪个作了哪个没作&#xff0c;也挺乱的。这回一共有7周呢&#xff0c;而且有的比赛题目居然不按周排序。 …

CART决策树-基尼指数(全网最详解)

文章目录 一、基尼指数的定义二、基尼指数在CART决策树中的应用三、基尼指数与CART决策树的构建1.计算每个子集的基尼系数&#xff1a;2.计算基尼指数3.选择最优特征4.其余基尼指数5.构建决策树 四、总结 CART决策树基尼指数是CART&#xff08;Classification And Regression T…