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 系統中不必要跨處理器同步.
為保證得到一個一致的值, 你應當為查詢這個計數器的代碼禁止搶占.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/447660.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447660.shtml
英文地址,請注明出處:http://en.pswp.cn/news/447660.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux 關閉、開啟、查看 防火墻命令

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 在外部訪問CentOS中部署應用時&#xff0c;需要關閉防火墻。 關閉防火墻命令&#xff1a;systemctl stop firewalld.service 開啟防火…

男人八大折壽惡習

男人看似比女人強壯&#xff0c;可往往更容易被疾病擊倒。世界衛生組織一項調查顯示&#xff0c;男人的心臟、腸胃天生就比女人脆弱&#xff0c;心腦血管病和腸胃病的發病率是女人的好幾倍。另外&#xff0c;由于在社會中男人承擔著更多的壓力&#xff0c;心理健康也備受挑戰。…

解決: Incorrect username or password, or no permission ( Docker 方式運行 Nexus3 登陸密碼不為 admin123 、重置登陸密碼)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 登陸 輸入 admin admin123 直接報錯&#xff1a; 查日志 發現的確登陸不成功&#xff1a; 2. 后來注意到在登陸時有提示信息 &#…

已婚男人的心理五大變化

很多女生都會說&#xff1a;他結婚經常會給我驚喜&#xff0c;婚后就完全變了。人們也常說&#xff1a;男人婚前婚后總是兩個樣。那到底婚后的男人心理到底出現了哪些變化&#xff0c;造成了這些行為上的變化呢&#xff1f; 一、婚后男人變得很懶 這仿佛是所有已婚男人的變化…

Docker 方式安裝、運行 Nexus3 、重置默認密碼、推送 jar 包到私服

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 找鏡像&#xff1a; docker search nexus2. 拉取鏡像&#xff1a;&#xff08;我已經拉取過&#xff0c;不選擇版本時&#xff0c;默認…

每天2分鐘,10個簡單方法改變你的生活

千里之行始于足下。——老子 距離沒什么可怕的&#xff0c;但邁開第一步卻是困難的。——瑪麗&#xff0e;狄&#xff0e;代芳夫人 想要生活有一個積極的改變&#xff0c;并不意味著要做一個巨大的飛躍。而且我相信&#xff0c;在眾多因素中&#xff0c;信念也可使人們遠離快速…

解決:Docker 啟動的容器內部時間比服務器時間晚 8 小時,容器內部時間與宿主機時間不一致

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1.docker 方式啟動容器 nexus3 &#xff0c;運行正常&#xff0c;但查日志時發現容器時間比宿主機時間晚8小時&#xff0c;內外時間不一…

從技術崗位走向管理崗位:機會是留給有準備的人

摘要&#xff1a;機會總是留給有準備的人。在被從技術崗位提拔到管理崗位之前&#xff0c;技術人員就要具備管理崗位所需要的基本素質和能力&#xff0c;將功課做在前面&#xff0c;提拔只是最后一步。作者結合自己十年的工作經驗談了自己的看法。 機會總是留給有準備的人。在…

springCloud - 第2篇 - 服務的發現 seeParam

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、創建 Client 1. file - new - module 2. spring Initializr - module SDK 選擇自己的 JDK &#xff0c;其余的可以不用填寫&…

讓你成為有錢一族的黃金理財法則?

想要獲得財富的每個人來說&#xff0c;愿意進行創富探索的意愿就是重要的財富&#xff0c;也許你不知道該怎么做&#xff0c;很多事確實有待嘗試。而愿意去探索&#xff0c;失敗&#xff0c;再探索是成功的核心所在&#xff0c;只要你有了創富的意愿&#xff0c;加以規劃&#…

maven 之 setting.xm 的配置詳解、說明

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 文件存放位置 全局配置: ${M2_HOME}/conf/settings.xml 用戶配置: ${user.home}/.m2/settings.xml note&#xff1a;用戶配置優先于全…

提取單圖元輪廓

一. 應用場景&#xff01; 使用過Genesis的朋友都知道&#xff0c;它可以提取你點擊單圖元中心<提取圖元輪廓計算中心點>&#xff01; 二. 由于工作需要&#xff0c;去年在師傅的知道下寫了一個單圖元輪廓提取算法&#xff01; 三. 原理 提取輪廓即需要找出單圖元最外…

解決: Failed to execute ... maven-deploy-plugin... Return code is: 401, ReasonPhrase: Unauthorized.

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 執行 mvn clean deploy , 想把本地代碼 打包并推送到私服 &#xff0c;報錯&#xff1a; [ERROR] Failed to execute goal org.a…

高速行車12條技巧,每一條都關乎你的生命

高速行車12條技巧&#xff0c;每一條都關乎你寶貴的生命。 1、長途旅行前&#xff0c;全面檢查整車&#xff0c;防止漏油&#xff0c;輪胎檢查是重點&#xff01; 在中國很多高速事故是可以避免的&#xff0c;絕大部分司機缺少本應該在駕校學到的必備知識&#xff0c;這導致很多…

文件逆序輸出及根據行號索取該行內容

一. 起因 年初&#xff0c;一位同學提取了如何實現倒敘輸出文件&#xff0c;根據行號索引該行內容&#xff0c;我思考了一下&#xff0c;得出以下二種方案&#xff01; 二. 方案 1&#xff09;方案1&#xff1a;&#xff08;此方案局限性較大&#xff0c;并且耗內存&#xff…

pom.xml 配置之:snapshot 快照庫和 release發布庫 的區別

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1、如果在發布時使用 mvn deploy -P release 的命令&#xff0c;那么會自動使用0.1作為發布版本&#xff0c;那么根據 maven 處理 snaps…

米飯涼一涼再吃會更加地健康

米飯&#xff0c;是人們日常飲食中的主角之一;一味米飯&#xff0c;與五味調配&#xff0c;幾乎可以供給全身所需營養。大米性平、味甘;有補中益氣、健脾養胃、益精強志、和五臟、通血脈、聰耳明目、止煩、止渴、止瀉的功效。 人們往往認為米飯應該趁熱的時候吃。 但是用電飯煲…

內存映射處理大文件并實現逆序輸出

上一篇介紹了一種常見的文件處理方法(可優化為&#xff1a;分次讀取文件&#xff0c;但要滿足根據行號能快速索引該行內容時會遇到麻煩),所以此片我將介紹另一種更高效&#xff0c;實用&#xff0c;并對本進程的內存空間地址消耗小的方法&#xff01; 一. 預備知識 1&#xff…

解決: tar: Removing leading `/‘ from member names

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 我的情況 。 使用絕對路徑 執行 tar 命令&#xff1a; tar -zcvf clientOne.tar /root/jiangyu/projects/springCloud/clientOne/s…

ObjectArx創建指定塊

ObjectArx創建自定義塊 一. 目的仿照AutoCad的Block命令&#xff0c;實現簡版創建塊功能!二. 開發環境Win7操作系統&#xff0c;AutoCad2012&#xff0c; VS2008, ObjectArx_SDK_2012三. 相關函數簡介1) int acedSSGet (const ACHAR *str, const void *pt1,const void *pt2…