日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

内核中的HZ 及延迟等

發(fā)布時(shí)間:2025/4/16 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 内核中的HZ 及延迟等 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
內(nèi)核中的HZ 及延遲等

時(shí)鐘中斷由系統(tǒng)定時(shí)硬件以周期性的間隔產(chǎn)生,這個(gè)間隔由內(nèi)核根據(jù) HZ 值來設(shè)定,HZ 是一個(gè)體系依賴的值,在 <linux/param.h>中定義或該文件包含的某個(gè)子平臺相關(guān)文件中。作為通用的規(guī)則,即便如果知道 HZ 的值,在編程時(shí)應(yīng)當(dāng)不依賴這個(gè)特定值,而始終使用HZ。對于當(dāng)前版本,我們應(yīng)完全信任內(nèi)核開發(fā)者,他們已經(jīng)選擇了最適合的HZ值,最好保持 HZ 的默認(rèn)值。

對用戶空間,內(nèi)核HZ幾乎完全隱藏,用戶 HZ 始終擴(kuò)展為 100。當(dāng)用戶空間程序包含 param.h,且每個(gè)報(bào)告給用戶空間的計(jì)數(shù)器都做了相應(yīng)轉(zhuǎn)換。對用戶來說確切的 HZ 值只能通過 /proc/interrupts 獲得:/proc/interrupts 的計(jì)數(shù)值除以 /proc/uptime 中報(bào)告的系統(tǒng)運(yùn)行時(shí)間。

對于ARM體系結(jié)構(gòu):在<linux/param.h>文件中的定義如下:

#ifdef __KERNEL__
# define HZ???????? CONFIG_HZ??? /* Internal kernel timer frequency */
# define USER_HZ???? 100??????? /* 用戶空間使用的HZ,User interfaces are in "ticks" */
# define CLOCKS_PER_SEC (USER_HZ) /* like times() */
#else
# define HZ???????? 100
#endif

也就是說:HZ 由__KERNEL__和CONFIG_HZ決定。若未定義__KERNEL__,HZ為100;否則為CONFIG_HZ。而CONFIG_HZ是在內(nèi)核的根目錄的.config文件中定義,并沒有在make menuconfig的配置選項(xiàng)中出現(xiàn)。Linux的\arch\arm\configs\s3c2410_defconfig文件中的定義為:

#
# Kernel Features
#
# CONFIG_PREEMPT is not set
# CONFIG_NO_IDLE_HZ is not set
CONFIG_HZ=200
# CONFIG_AEABI is not set
# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set

所以正常情況下s3c24x0的HZ為200。這一數(shù)值在后面的實(shí)驗(yàn)中可以證實(shí)。

每次發(fā)生一個(gè)時(shí)鐘中斷,內(nèi)核內(nèi)部計(jì)數(shù)器的值就加一。這個(gè)計(jì)數(shù)器在系統(tǒng)啟動(dòng)時(shí)初始化為 0, 因此它代表本次系統(tǒng)啟動(dòng)以來的時(shí)鐘嘀噠數(shù)。這個(gè)計(jì)數(shù)器是一個(gè) 64-位 變量( 即便在 32-位的體系上)并且稱為 “jiffies_64”。但是驅(qū)動(dòng)通常訪問 jiffies 變量(unsigned long)(根據(jù)體系結(jié)構(gòu)的不同:可能是 jiffies_64 ,可能是jiffies_64 的低32位)。使用 jiffies 是首選,因?yàn)樗L問更快,且無需在所有的體系上實(shí)現(xiàn)原子地訪問 64-位的 jiffies_64 值。

使用 jiffies 計(jì)數(shù)器

這個(gè)計(jì)數(shù)器和用來讀取它的工具函數(shù)包含在 <linux/jiffies.h>, 通常只需包含 <linux/sched.h>,它會(huì)自動(dòng)放入 jiffies.h 。 jiffies 和 jiffies_64 必須被當(dāng)作只讀變量。當(dāng)需要記錄當(dāng)前 jiffies 值(被聲明為 volatile 避免編譯器優(yōu)化內(nèi)存讀)時(shí),可以簡單地訪問這個(gè) unsigned long 變量,如:

#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 */

以下是一些簡單的工具宏及其定義:

#define time_after(a,b)???????? \
(typecheck(unsigned long, a) && \
???? typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
#define time_before(a,b)???? time_after(b,a)
#define time_after_eq(a,b)???? \
(typecheck(unsigned long, a) && \
???? typecheck(unsigned long, b) && \
((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b)???? time_after_eq(b,a)

用戶空間的時(shí)間表述法(struct timeval 和 struct timespec )與內(nèi)核表述法的轉(zhuǎn)換函數(shù):

#include <linux/time.h> /* #i nclude <linux/jiffies.h> --> \kernel\time.c*/
struct timespec {
time_t???? tv_sec; /* seconds */
long???? tv_nsec; /* nanoseconds */
};
#endif
struct timeval {
time_t???????? tv_sec; /* seconds */
???? suseconds_t???? tv_usec; /* microseconds */
};
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);

訪問jiffies_64 對于 32-位 處理器不是原子的,這意味著如果這個(gè)變量在你正在讀取它們時(shí)被更新你可能讀到錯(cuò)誤的值。若需要訪問jiffies_64,內(nèi)核有一個(gè)特別的輔助函數(shù),為你完成適當(dāng)?shù)逆i定:

#include <linux/jiffies.h>
u64 get_jiffies_64(void);

處理器特定的寄存器

若需測量非常短時(shí)間間隔或需非常高的精度,可以借助平臺依賴的資源。許多現(xiàn)代處理器包含一個(gè)隨時(shí)鐘周期不斷遞增的計(jì)數(shù)寄存器,他是進(jìn)行高精度的時(shí)間管理任務(wù)唯一可靠的方法。最有名的計(jì)數(shù)器寄存器是 TSC ( timestamp counter), 在 x86 的 Pentium 處理器開始引入并在之后所有的 CPU 中出現(xiàn)(包括 x86_64 平臺)。它是一個(gè) 64-位 寄存器,計(jì)數(shù) CPU 的時(shí)鐘周期,可從內(nèi)核和用戶空間讀取。在包含了 <asm/msr.h> (一個(gè) x86-特定的頭文件, 它的名子代表"machine-specific registers")的代碼中可使用這些宏:

rdtsc(low32,high32);/*原子地讀取 64-位TSC 值到 2 個(gè) 32-位 變量*/
rdtscl(low32);/*讀取TSC的低32位到一個(gè) 32-位 變量*/
rdtscll(var64);/*讀 64-位TSC 值到一個(gè) long long 變量*/
/*下面的代碼行測量了指令自身的執(zhí)行時(shí)間:*/
unsigned long ini, end;
rdtscl(ini); rdtscl(end);
printk("time lapse: %li\n", end - ini);

一些其他的平臺提供相似的功能, 并且內(nèi)核頭文件提供一個(gè)體系無關(guān)的功能用來代替 rdtsc,稱 get_cycles(定義在 <asm/timex.h>( 由 <linux/timex.h> 包含)),原型如下:

#include <linux/timex.h>
cycles_t get_cycles(void);
/*這個(gè)函數(shù)在每個(gè)平臺都有定義, 但在沒有時(shí)鐘周期計(jì)數(shù)器的平臺上返回 0 */
/*由于s3c2410系列處理器上沒有時(shí)鐘周期計(jì)數(shù)器所以get_cycles定義如下:*/
typedef unsigned long cycles_t;
static inline cycles_t get_cycles (void)
{
return 0;
}


獲取當(dāng)前時(shí)間

驅(qū)動(dòng)一般無需知道時(shí)鐘時(shí)間(用年月日、小時(shí)、分鐘、秒來表達(dá)的時(shí)間),只對用戶程序才需要,如 cron 和 syslogd。 內(nèi)核提供了一個(gè)將時(shí)鐘時(shí)間轉(zhuǎn)變?yōu)槊霐?shù)值的函數(shù):

unsigned long
mktime(const unsigned int year0, const unsigned int mon0,
const unsigned int day, const unsigned int hour,
const unsigned int min, const unsigned int sec)
{
unsigned int mon = mon0, year = year0;
/* 1..12 -> 11,12,1..10 */
if (0 >= (int) (mon -= 2)) {
???????? mon += 12; /* Puts Feb last since it has leap day */
???????? year -= 1;
}
return ((((unsigned long)
(year/4 - year/100 + year/400 + 367*mon/12 + day) +
???????? year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
/*這個(gè)函數(shù)將時(shí)間轉(zhuǎn)換成從1970年1月1日0小時(shí)0分0秒到你輸入的時(shí)間所經(jīng)過的秒數(shù),溢出時(shí)間為2106-02-07 06:28:16。本人認(rèn)為這個(gè)函數(shù)的使用應(yīng)這樣:若你要計(jì)算2000-02-07 06:28:16 到2000-02-09 06:28:16 所經(jīng)過的秒數(shù):unsigned long time1 = mktime(2000,2,7,6,28,16)-mktime(2000,2,9,6,28,16); 若還要轉(zhuǎn)成jiffies,就再加上:unsigned long time2 = time1*HZ. 注意溢出的情況!*/

為了處理絕對時(shí)間, <linux/time.h> 導(dǎo)出了 do_gettimeofday 函數(shù),它填充一個(gè)指向 struct timeval 的指針變量。絕對時(shí)間也可來自 xtime 變量,一個(gè) struct timespec 值,為了原子地訪問它,內(nèi)核提供了函數(shù) current_kernel_time。它們的精確度由硬件決定,原型是:

#include <linux/time.h>
void do_gettimeofday(struct timeval *tv);
struct timespec current_kernel_time(void);

/*得到的數(shù)據(jù)都表示當(dāng)前時(shí)間距UNIX時(shí)間基準(zhǔn)1970-01-01 00:00:00的相對時(shí)間*/

以上兩個(gè)函數(shù)在ARM平臺都是通過 xtime 變量得到數(shù)據(jù)的。


全局變量xtime:它是一個(gè)timeval結(jié)構(gòu)類型的變量,用來表示當(dāng)前時(shí)間距UNIX時(shí)間基準(zhǔn)1970-01-01 00:00:00的相對秒數(shù)值。

結(jié)構(gòu)timeval是Linux內(nèi)核表示時(shí)間的一種格式(Linux內(nèi)核對時(shí)間的表示有多種格式,每種格式都有不同的時(shí)間精度),其時(shí)間精度是微秒。該結(jié)構(gòu)是內(nèi)核表示時(shí)間時(shí)最常用的一種格式,它定義在頭文件include/linux/time.h中,如下所示:

struct timeval {

time_t tv_sec; /* seconds */

suseconds_t tv_usec; /* microseconds */

};

其中,成員tv_sec表示當(dāng)前時(shí)間距UNIX時(shí)間基準(zhǔn)的秒數(shù)值,而成員tv_usec則表示一秒之內(nèi)的微秒值,且1000000>tv_usec>=0。

Linux內(nèi)核通過timeval結(jié)構(gòu)類型的全局變量xtime來維持當(dāng)前時(shí)間,該變量定義在kernel/timer.c文件中,如下所示:

/* The current time */

volatile struct timeval xtime __attribute__ ((aligned (16)));

但是,全局變量xtime所維持的當(dāng)前時(shí)間通常是供用戶來檢索和設(shè)置的,而其他內(nèi)核模塊通常很少使用它(其他內(nèi)核模塊用得最多的是jiffies),因此對xtime的更新并不是一項(xiàng)緊迫的任務(wù),所以這一工作通常被延遲到時(shí)鐘中斷的底半部(bottom half)中來進(jìn)行。由于bottom half的執(zhí)行時(shí)間帶有不確定性,因此為了記住內(nèi)核上一次更新xtime是什么時(shí)候,Linux內(nèi)核定義了一個(gè)類似于jiffies的全局變量wall_jiffies,來保存內(nèi)核上一次更新xtime時(shí)的jiffies值。時(shí)鐘中斷的底半部分每一次更新xtime的時(shí)侯都會(huì)將wall_jiffies更新為當(dāng)時(shí)的jiffies值。全局變量wall_jiffies定義在kernel/timer.c文件中:

/* jiffies at the most recent update of wall time */

unsigned long wall_jiffies;

總結(jié)

以上是生活随笔為你收集整理的内核中的HZ 及延迟等的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。