linux 内核时间计量方法
定时器中断由系统定时硬件以规律地间隔产生; 这个间隔在启动时由内核根据 HZ 值来编
程, HZ 是一个体系依赖的值, 在 <linux/param.h>中定义或者它所包含的一个子平台文
件中. 在发布的内核源码中的缺省值在真实硬件上从 50 到 1200 嘀哒每秒, 在软件模拟
器中往下到 24. 大部分平台运行在 100 或者 1000 中断每秒; 流行的 x86 PC 缺省是
1000, 尽管它在以前版本上(向上直到并且包括 2.4)常常是 100. 作为一个通用的规则,
即便如果你知道 HZ 的值, 在编程时你应当从不依赖这个特定值.
可能改变 HZ 的值, 对于那些要系统有一个不同的时钟中断频率的人. 如果你在头文件中
改变 HZ 的值, 你需要使用新的值重编译内核和所有的模块. 如果你愿意付出额外的时间
中断的代价来获得你的目标, 你可能想提升 HZ 来得到你的异步任务的更细粒度的精度.
实际上, 提升 HZ 到 1000 在使用 2.4 或 2.2 内核版本的 x86 工业系统中是相当普遍
的. 但是, 对于当前版本, 最好的方法是保持 HZ 的缺省值, 由于我们完全信任内核开发
者, 他们肯定已经选择了最好的值. 另外, 一些内部计算当前实现为只为从 12 到 1535
范围的 HZ (见 <linux/timex.h> 和 RFC-1589).
每次发生一个时钟中断, 一个内核计数器的值递增. 这个计数器在系统启动时初始化为 0,
因此它代表从最后一次启动以来的时钟嘀哒的数目. 这个计数器是一个 64-位 变量( 即
便在 32-位的体系上)并且称为 jiffies_64. 但是, 驱动编写者正常地存取 jiffies 变
量, 一个 unsigned long, 或者和 jiffies_64 是同一个或者它的低有效位. 使用
jiffies 常常是首选, 因为它更快, 并且再所有的体系上存取 64-位的 jiffies_64 值不
必要是原子的.
除了低精度的内核管理的 jiffy 机制, 一些 CPU 平台特有一个高精度的软件可读的计数
器. 尽管它的实际使用有些在各个平台不同, 它有时是一个非常有力的工具.
使用 jiffies 计数器
这个计数器和来读取它的实用函数位于 <linux/jiffies.h>, 尽管你会常常只是包含
<linux/sched.h>, 它会自动地将 jiffies.h 拉进来. 不用说, jiffies 和 jiffies_64
必须当作只读的.
无论何时你的代码需要记住当前的 jiffies 值, 可以简单地存取这个 unsigned long 变
量, 它被声明做 volatile 来告知编译器不要优化内存读. 你需要读取当前的计数器, 无
论何时你的代码需要计算一个将来的时间戳, 如下面例子所示:
#include <linux/jiffies.h>
unsigned long j, stamp_1, stamp_half, stamp_n;
j = jiffies; /* read the current value */
stamp_1 = j + HZ; /* 1 second in the future */
stamp_half = j + HZ/2; /* half a second */
stamp_n = j + n * HZ / 1000; /* n milliseconds */
这个代码对于 jiffies 回绕没有问题, 只要不同的值以正确的方式进行比较. 尽管在
32-位 平台上当 HZ 是 1000 时, 计数器只是每 50 天回绕一次, 你的代码应当准备面对
这个事件. 为比较你的被缓存的值( 象上面的 stamp_1 ) 和当前值, 你应当使用下面一
个宏定义:
#include <linux/jiffies.h>
int time_after(unsigned long a, unsigned long b);
int time_before(unsigned long a, unsigned long b);
int time_after_eq(unsigned long a, unsigned long b);
int time_before_eq(unsigned long a, unsigned long b);
第一个当 a, 作为一个 jiffies 的快照, 代表 b 之后的一个时间时, 取值为真, 第二个
当 时间 a 在时间 b 之前时取值为真, 以及最后 2 个比较"之后或相同"和"之前或相同".
这个代码工作通过转换这个值为 signed long, 减它们, 并且比较结果. 如果你需要以一
种安全的方式知道 2 个 jiffies 实例之间的差, 你可以使用同样的技巧: diff =
(long)t2 - (long)t1;.
你可以转换一个 jiffies 差为毫秒, 一般地通过:
msec = diff * 1000 / HZ;
有时, 但是, 你需要与用户空间程序交换时间表示, 它们打算使用 struct timeval 和
struct timespec 来表示时间. 这 2 个结构代表一个精确的时间量, 使用 2 个成员:
seconds 和 microseconds 在旧的流行的 struct timeval 中使用, seconds 和
nanoseconds 在新的 struct timespec 中使用. 内核输出 4 个帮助函数来转换以
jiffies 表达的时间值, 到和从这些结构:
#include <linux/time.h>
unsigned long timespec_to_jiffies(struct timespec *value);
void jiffies_to_timespec(unsigned long jiffies, struct timespec *value);
unsigned long timeval_to_jiffies(struct timeval *value);
void jiffies_to_timeval(unsigned long jiffies, struct timeval *value);
存取这个 64-位 jiffy 计数值不象存取 jiffies 那样直接. 而在 64-位 计算机体系上,
这 2 个变量实际上是一个, 存取这个值对于 32-位 处理器不是原子的. 这意味着你可能
读到错误的值如果这个变量的两半在你正在读取它们时被更新. 极不可能你会需要读取这
个 64-位 计数器, 但是万一你需要, 你会高兴地得知内核输出了一个特别地帮助函数,
为你完成正确地加锁:
#include <linux/jiffies.h>
u64 get_jiffies_64(void);
在上面的原型中, 使用了 u64 类型. 这是一个定义在 <linux/types.h> 中的类型, 在
11 章中讨论, 并且表示一个 unsigned 64-位 类型.
如果你在奇怪 32-位 平台如何同时更新 32-位 和 64-位 计数器, 读你的平台的连接脚
本( 查找一个文件, 它的名子匹配 valinux*.lds*). 在那里, jiffies 符号被定义来存
取这个 64-位 值的低有效字, 根据平台是小端或者大端. 实际上, 同样的技巧也用在
64-位 平台上, 因此这个 unsigned long 和 u64 变量在同一个地址被存取.
最后, 注意实际的时钟频率几乎完全对用户空间隐藏. 宏 HZ 一直扩展为 100 当用户空
间程序包含 param.h, 并且每个报告给用户空间的计数器都对应地被转换. 这应用于
clock(3), times(2), 以及任何相关的函数. 对 HZ 值的用户可用的唯一证据是时钟中断
多快发生, 如在 /proc/interrupts 所显示的. 例如, 你可以获得 HZ, 通过用在
/proc/uptime 中报告的系统 uptime 除这个计数值.
处理器特定的寄存器
如果你需要测量非常短时间间隔, 或者你需要非常高精度, 你可以借助平台依赖的资源,
一个要精度不要移植性的选择.
在现代处理器中, 对于经验性能数字的迫切需求被大部分 CPU 设计中内在的指令定时不
确定性所阻碍, 这是由于缓存内存, 指令调度, 以及分支预测引起. 作为回应, CPU 制造
商引入一个方法来计数时钟周期, 作为一个容易并且可靠的方法来测量时间流失. 因此,
大部分现代处理器包含一个计数器寄存器, 它在每个时钟周期固定地递增一次. 现在, 资
格时钟计数器是唯一可靠的方法来进行高精度的时间管理任务.
细节每个平台不同: 这个寄存器可以或者不可以从用户空间可读, 它可以或者不可以写,
并且它可能是 64 或者 32 位宽. 在后一种情况, 你必须准备处理溢出, 就象我们处理
jiffy 计数器一样. 这个寄存器甚至可能对你的平台来说不存在, 或者它可能被硬件设计
者在一个外部设备实现, 如果 CPU 缺少这个特性并且你在使用一个特殊用途的计算机.
无论是否寄存器可以被清零, 我们强烈不鼓励复位它, 即便当硬件允许时. 毕竟, 在任何
给定时间你可能不是这个计数器的唯一用户; 在一些支持 SMP 的平台上, 例如, 内核依
赖这样一个计数器来在处理器之间同步. 因为你可以一直测量各个值的差, 只要差没有超
过溢出时间, 你可以通过修改它的当前值来做这个事情不用声明独自拥有这个寄存器.
最有名的计数器寄存器是 TSC ( timestamp counter), 在 x86 处理器中随 Pentium 引
入的并且在所有从那之后的 CPU 中出现 -- 包括 x86_64 平台. 它是一个 64-位 寄存器
计数 CPU 的时钟周期; 它可从内核和用户空间读取.
在包含了 <asm/msr.h> (一个 x86-特定的头文件, 它的名子代表"machine-specific
registers"), 你可使用一个这些宏:
rdtsc(low32,high32);
rdtscl(low32);
rdtscll(var64);
第一个宏自动读取 64-位 值到 2 个 32-位 变量; 下一个("read low half") 读取寄存
器的低半部到一个 32-位 变量, 丢弃高半部; 最后一个读 64-位 值到一个 long long
变量, 由此得名. 所有这些宏存储数值到它们的参数中.
对大部分的 TSC 应用, 读取这个计数器的的低半部足够了. 一个 1-GHz 的 CPU 只在每
4.2 秒溢出一次, 因此你不会需要处理多寄存器变量, 如果你在使用的时间流失确定地使
用更少时间. 但是, 随着 CPU 频率不断上升以及定时需求的提高, 将来你会几乎可能需
要常常读取 64-位 计数器.
作为一个只使用寄存器低半部的例子, 下面的代码行测量了指令自身的执行:
unsigned long ini, end;
rdtscl(ini); rdtscl(end);
printk("time lapse: %li\n", end - ini);
一些其他的平台提供相似的功能, 并且内核头文件提供一个体系独立的功能, 你可用来代
替 rdtsc. 它称为 get_cycles, 定义在 <asm/timex.h>( 由 <linux/timex.h> 包含).
它的原型是:
#include <linux/timex.h>
cycles_t get_cycles(void);
这个函数为每个平台定义, 并且它一直返回 0 在没有周期-计数器寄存器的平台上.
cycles_t 类型是一个合适的 unsigned 类型来持有读到的值.
不论一个体系独立的函数是否可用, 我们最好利用机会来展示一个内联汇编代码的例子.
为此, 我们实现一个 rdtscl 函数给 MIPS 处理器, 它与在 x86 上同样的方式工作.
拖尾的 nop 指令被要求来阻止编译器在 mfc0 之后马上存取指令中的目标寄存器. 这种
内部锁在 RISC 处理器中是典型的, 并且编译器仍然可以在延迟时隙中调度有用的指令.
在这个情况中, 我们使用 nop 因为内联汇编对编译器是一个黑盒并且不会进行优化.
[26]26
#define rdtscl(dest) \
__asm__ __volatile__("mfc0 %0,$9; nop" : "=r" (dest))
有这个宏在, MIPS 处理器可以执行同样的代码, 如同前面为 x86 展示的一样的代码.
使用 gcc 内联汇编, 通用寄存器的分配留给编译器. 刚刚展示的这个宏使用 %0 作为"参
数 0"的一个占位符, 之后它被指定为"任何用作输出( = )的寄存器( r )". 这个宏还声
明输出寄存器必须对应 C 表达式 dest. 内联函数的语法是非常强大但是有些复杂, 特别
对于那些有限制每个寄存器可以做什么的体系上(就是说, x86 家族). 这个用法在 gcc
文档中描述, 常常在 info 文档目录树中有.
本节已展示的这个简短的 C-代码片段已在一个 K7-级 x86 处理器 和一个 MIPS VR4181
( 使用刚刚描述过的宏 )上运行. 前者报告了一个 11 个时钟嘀哒的时间流失而后者只是
2 个时钟嘀哒. 小的数字是期望的, 因为 RISC 处理器常常每个时钟周期执行一条指令.
有另一个关于时戳计数器的事情值得知道: 它们在一个 SMP 系统中不必要跨处理器同步.
为保证得到一个一致的值, 你应当为查询这个计数器的代码禁止抢占.
相关文章:

linux 内核时间计量方法
定时器中断由系统定时硬件以规律地间隔产生; 这个间隔在启动时由内核根据 HZ 值来编 程, HZ 是一个体系依赖的值, 在 <linux/param.h>中定义或者它所包含的一个子平台文 件中. 在发布的内核源码中的缺省值在真实硬件上从 50 到 1200 嘀哒每秒, 在软件模拟 器中往下到 24.…...
循环神经网络中的梯度消失或梯度爆炸问题产生原因分析(二)
上一篇中讨论了一般性的原则,这里我们具体讨论通过时间反向传播(backpropagation through time,BPTT)的细节。我们将展示目标函数对于所有模型参数的梯度计算方法。 出于简单的目的,我们以一个没有偏置参数的循环神经…...

JWT signature does not match locally computed signature
1. 问题背景 最近在协助团队小盆友调试一个验签问题,结果还“节外生枝”了,原来不是签名过程的问题,是token的问题。 当你看到“JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not…...

vitepress项目使用github的action自动部署到github-pages中,理论上可以通用所有
使用github的action自动部署到github-pages中 创建部署的deploy.yml文件,在项目的根目录下面 .github\workflows\deploy.yml 完整的代码:使用的是pnpm进行依赖安装。 name: 部署VitePresson:push:branches:- docs # 这段是在推送到 docs 分支时触发该…...

Python爬虫---解析---JSONPath
Xpath可以解析本地文件和服务器响应的文件,JSONPath只能解析本地文件 1. 安装jsonpath:pip install jsonpath 注意:需要安装在python解释器相同的位置,例如:D:\Program Files\Python3.11.4\Scripts 2. 使用步骤 2.1 导入&…...

路由器介绍和命令操作
先来回顾一下上次的内容: ip地址就是由32位二进制数组 二进位数就是只有数字0和1组成 网络位:类似于区号,表示区域作用 主机位:类似于号码,表示区域中编号 网络名称:网络位不变,主机位全为0 …...

Hadoop——分布式计算
一、分布式计算概述 1. 什么是计算、分布式计算? 计算:对数据进行处理,使用统计分析等手段得到需要的结果分布式计算:多台服务器协同工作,共同完成一个计算任务2. 分布式计算常见的2种工作模式分散->汇总 (MapReduce就是这种模式)将数据分片,多台服务器各自负责一…...

LaTeX引用参考文献 | Texstudio引用参考文献
图片版教程: 文字版教程: ref.bib里面写参考的文献,ref.bib和document.tex要挨着放,同一个目录里面. 解析一下bib文件格式:aboyeji2023effect是引用文献的关键字,需要在正文document.tex里面使用\cite指令…...

如何在Go中使用模板
引言 您是否需要以格式良好的输出、文本报告或HTML页面呈现一些数据?你可以使用Go模板来做到这一点。任何Go程序都可以使用text/template或html/template包(两者都包含在Go标准库中)来整齐地显示数据。 这两个包都允许你编写文本模板并将数据传递给它们,以按你喜欢的格式呈…...

云原生之深入解析基于FunctionGraph在Serverless领域的FinOps的探索和实践
一、背景 Serverless 精确到毫秒级的按用付费模式使得用户不再需要为资源的空闲时间付费。然而,对于给定的某个应用函数,由于影响其计费成本的因素并不唯一,使得用户对函数运行期间的总计费进行精确的事先估计变成了一项困难的工作。以传统云…...

电子电器架构(E/E)演化 —— 主流主机厂域集中架构概述
电子电器架构(E/E)演化 —— 主流主机厂域集中架构概述 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。…...

Python常用的几个函数
print()函数:用于打印输出信息到控制台。 input()函数:用于从控制台获取用户输入。 len()函数:用于获取字符串、列表、元组、字典等对象的长度。 range()函数:用于生成一个整数序列,常用于循环中。 type()函数&…...

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch等各类软件
实战章节:在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面,我们学习了许多的Linux命令和高级技巧,这些知识点比较零散,同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用,…...

HarmonyOS4.0系统性深入开发01应用模型的构成要素
应用模型的构成要素 应用模型是HarmonyOS为开发者提供的应用程序所需能力的抽象提炼,它提供了应用程序必备的组件和运行机制。有了应用模型,开发者可以基于一套统一的模型进行应用开发,使应用开发更简单、高效。 HarmonyOS应用模型的构成要…...

线下终端门店调研包含哪些内容
品牌渠道一般分为线上和线下,线上的价格、促销信息、店铺优惠机制等都可以通过登录查看,但是线下门店的数据则需要进店巡查,否则无法得到真实的店铺销售数据,当然也有品牌是靠线下的业务团队报备机制获得这些信息,但是…...

倾斜摄影三维模型数据在行业应用分析
倾斜摄影三维模型数据在行业应用分析 倾斜摄影三维模型数据是一种重要的地理信息资源,可以广泛应用于各个行业和场景,以解决不同领域的问题。以下将详细探讨几个典型的行业或场景,它们利用倾斜摄影三维模型数据解决问题的应用。 1、地理测绘…...

Apache Flink 进阶教程(七):网络流控及反压剖析
目录 前言 网络流控的概念与背景 为什么需要网络流控 网络流控的实现:静态限速 网络流控的实现:动态反馈/自动反压 案例一:Storm 反压实现 案例二:Spark Streaming 反压实现 疑问:为什么 Flink(bef…...

k8s学习 — (DevOps实践)第十三章 DevOps 环境搭建
k8s学习 — (DevOps实践)第十三章 DevOps 环境搭建 学习资料1 Gitlab1.1 安装 Gitlab1.2 页面配置1.3 配置 Secret1.4 为项目配置 Webhook1.5 卸载 2 Harbor2.1 安装 Harbor2.1 配置 Secret 3 SonarQube3.1 安装 SonarQube3.2 生成服务 token3.3 创建 We…...

Java_Stream流
一、JDK8新特性(Stream流) 接下来学习一个全新的知识,叫做Stream流(也叫Stream API)。它是从JDK8以后才有的一个新特性,是专业用于对集合或者数组进行便捷操作的。有多方便呢?我们用一个案例体…...

delphi中,tstringlist使用方法示例
delphi中,tstringlist使用方法示例 在 Delphi 中,TStringList 是一个常用的字符串列表类,它提供了许多用于处理字符串列表的方法。以下是一个示例,演示了如何在 Delphi 中使用 TStringList 类: program TStringListEx…...

【飞凌 OK113i-C 全志T113-i开发板】视频编解码测试
前言 本文测试OK113i-S开发板-视频编解码的功能 OK113i-S开发板是支持视频的编解码的,下面是官方介绍的编解码功能 T113-i 是一种为多媒体解码平台设计的高级应用处理器。T113-i 集成了64位玄铁C906 RISC-V CPU, 双核 Cortex - A7 CPU 和 HiFi4 DSP&a…...

全部没有问题 (一.5)
java mooc练习 基础练习: 进阶练习: final 赋值一次 局部 必须赋值 抽象类 多态测试 package com.book;public class moocDraft1 {static int variable1;public void fatherMethod(moocDraft1 a){System.out.println(variable);}public static void…...

C++归并排序详解以及代码实现
1. 介绍 归并排序(Merge Sort)是一种采用分治法(Divide and Conquer)策略的排序算法。该算法首先将已有序的子序列合并,得到完全有序的序列。在归并排序中,合并操作是将两个有序表合并成一个有序表的过程。…...

springboot整合JPA 多表关联 :一对多 多对多
补充一下自定义SQL 这是连表查询,可以任意查出字符,用Map接收 Testvoid test3() {JPAQueryFactory jpaQueryFactory new JPAQueryFactory(em);QStudent student QStudent.student;QMessage message QMessage.message;//constructor(StuMesDto.class, …...

Python 数据分析 Matplotlib篇 plt.rcParams 字典(第5讲)
Python 数据分析 Matplotlib篇 plt.rcParams字典(第5讲) 🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…...

DeamonSet详解
目录 1.1 何为DaemonSet 1.2 DaemonSet 的 API 对象的定义 1.3 DaemonSet实践 1.3.1 创建 DaemonSet 对象 1.3.2 查看 DaemonSet 对象 1.3.3 DaemonSet 版本管理 1.3.4 DaemonSet 的容器镜像版本到 v2.2.0 1.1 何为DaemonSet 介绍DaemonSet我们先来思考一个问题&#x…...

TwIST算法MALTLAB主程序详解
TwIST算法MALTLAB主程序详解 关于TwIST算法的具体原理可以参考: 链接: https://ieeexplore.ieee.org/abstract/document/4358846 链接: https://blog.csdn.net/jbb0523/article/details/52193209 该算法的MATLAB源代码: 链接: http://www.lx.it.pt/~bi…...

Flutter 三: Dart
1 数据类型 数字(number) int double 字符串转换成 num int.parse(“1”) double.parse(“1”);double 四舍五入保留两位小数 toStringAsFixed(2) 返回值为stringdouble 直接舍弃小数点后几位的数据 可使用字符串截取的方式 字符串(string) 单引号 双引号 三引号三引号 可以输…...

redis基本用法学习(C#调用FreeRedis操作redis)
FreeRedis属于常用的基于.net的redis客户端,EasyCaching中也提供适配FreeRedis的包。根据参考文献4中的说法,FreeRedis和CsRedis算是近亲(都是GitHub中账号为2881099下的开源项目),因此其用法特别相似。FreeRedis的主要…...

Postman接口测试(超详细整理)
常用的接口测试工具主要有以下几种 Postman:简单方便的接口调试工具,便于分享和协作。具有接口调试,接口集管理,环境配置,参数化,断言,批量执行,录制接口,Mock Server, …...