第11章 定時(shí)器和時(shí)間管理

系統(tǒng)定時(shí)器是一種可編程硬件芯片,能以固定頻率產(chǎn)生中斷,也就是定時(shí)器中斷,其對應(yīng)的中斷處理程序負(fù)責(zé)更新系統(tǒng)時(shí)間,也負(fù)責(zé)執(zhí)行需要周期性運(yùn)行的任務(wù)。

系統(tǒng)定時(shí)器和時(shí)鐘中斷處理程序是Linux系統(tǒng)內(nèi)核管理機(jī)制的中樞。動態(tài)定時(shí)器是一種推遲執(zhí)行程序的工具。

一、內(nèi)核中的時(shí)間概念

內(nèi)核必須在硬件的幫助下才能計(jì)算和管理時(shí)間。硬件為內(nèi)核提供一個(gè)系統(tǒng)定時(shí)器用以計(jì)算流逝的時(shí)間。系統(tǒng)定時(shí)器以某種頻率自行觸發(fā)(擊中hitting或射中popping)時(shí)鐘中斷,該頻率可以通過編程預(yù)定,稱節(jié)拍率(tick rate)。當(dāng)時(shí)鐘中斷發(fā)生時(shí),內(nèi)核通過特殊的中斷處理程序進(jìn)行處理。節(jié)拍率對內(nèi)核是可知的,所以內(nèi)核知道連續(xù)兩次時(shí)間中斷的間隔時(shí)間,也就是節(jié)拍(tick),等于1/節(jié)拍率(秒)。

  • 墻上時(shí)間:實(shí)際時(shí)間
  • 系統(tǒng)運(yùn)行時(shí)間:自系統(tǒng)啟動開始所經(jīng)過的時(shí)間

二、節(jié)拍率:HZ

節(jié)拍率是通過靜態(tài)預(yù)處理定義的,也就是HZ(赫茲),在系統(tǒng)啟動時(shí)按照HZ值對硬件進(jìn)行設(shè)置。大多數(shù)體系結(jié)構(gòu)的節(jié)拍率是可調(diào)的。

提高節(jié)拍率的好處:

  • 更高的時(shí)鐘中斷解析度可以提高時(shí)間驅(qū)動事件的解析度
  • 提高時(shí)間驅(qū)動事件的準(zhǔn)確度

高HZ的優(yōu)勢(也就是高時(shí)鐘中斷解析度和準(zhǔn)確度帶來的好處):

  • 內(nèi)核定時(shí)器能以更高的頻度和準(zhǔn)確度運(yùn)行
  • 依賴定時(shí)值執(zhí)行的系統(tǒng)調(diào)用以更高的精度運(yùn)行,提高性能
  • 對如資源消耗和系統(tǒng)運(yùn)行時(shí)間等的測量會有更精細(xì)的解析度
  • 提高進(jìn)程搶占的準(zhǔn)確度

高HZ的劣勢:HZ越高,時(shí)鐘中斷頻率越高,系統(tǒng)負(fù)擔(dān)越重。

三、jiffies

全局變量jiffies用于記錄自系統(tǒng)啟動以來的節(jié)拍的總數(shù)。

jiffies的定義:

extern unsigned long volatile jiffies;
//關(guān)鍵詞volatile指示編譯器在每次訪問變量時(shí)都重新從主內(nèi)存中獲得,而不是通過寄存器中的變量別名來訪問。

在32位體系結(jié)構(gòu)上unsigned long只有32位,很容易會溢出。因此增加了一個(gè)64位的變量:

extern u64 jiffies_64;  // 可通過get_jiffies_64()函數(shù)訪問

ld腳本用于連接主內(nèi)核映像,然后用jiffies_64變量覆蓋jiffies。因此,對于32位系統(tǒng),jiffies取jiffies_64的低32位,因?yàn)榇蠖鄶?shù)代碼使用jiffies存放流失的時(shí)間,因此只關(guān)心低32位;時(shí)間管理代碼使用整個(gè)64位jiffies_64,避免溢出。對于64位系統(tǒng),jiffies和jiffies_64是同一個(gè)變量。

因?yàn)?2位體系結(jié)構(gòu)不能原子地一次訪問64位變量中的兩個(gè)32位數(shù)值,因此在讀取jiffies時(shí)需要用xtime_lock鎖對jiffies變量進(jìn)行鎖定。

當(dāng)jiffies變量的值發(fā)生溢出,如果節(jié)拍計(jì)數(shù)還要繼續(xù)增加,jiffies的值就會回繞到0。內(nèi)核提供了宏定義來處理:

#define time_after(unkonwn, known) ((long)(known) - (long)(unknown) < 0)
#define time_before(unkonwn, known) ((long)(unknown) - (long)(known) < 0)
#define time_after_eq(unkonwn, known) ((long)(unknown) - (long)(known) >= 0)
#define time_before(unkonwn, known) ((long)(known) - (long)(unknown) >= 0)

內(nèi)核定義了USER_HZ來代表用戶空間看到的HZ值。

jiffies_to_clock_t(unsigned long)//將HZ表示的節(jié)拍計(jì)數(shù)轉(zhuǎn)化稱USER_HZ表示的節(jié)拍計(jì)數(shù)
jiffies_64_to_clock_t(unsigned long)

四、硬時(shí)鐘和定時(shí)器

實(shí)時(shí)時(shí)鐘(RTC):用于持久存放系統(tǒng)時(shí)間的設(shè)備,即便系統(tǒng)關(guān)閉后,也可以靠主板上的微型電池提供電力保持系統(tǒng)的計(jì)時(shí)。當(dāng)系統(tǒng)啟動時(shí),內(nèi)核通過讀取RTC來初始化墻上時(shí)間,存放在xtime變量中。

系統(tǒng)定時(shí)器提供一種周期性觸發(fā)中斷機(jī)制。

五、時(shí)鐘中斷處理程序

時(shí)鐘中斷處理程序可以劃分為兩部分:

  • 體系結(jié)構(gòu)相關(guān)部分
  • 體系結(jié)構(gòu)無關(guān)部分

與體系結(jié)構(gòu)相關(guān)的例程作為系統(tǒng)定時(shí)器的中斷處理程序注冊到內(nèi)核中,以便產(chǎn)生時(shí)鐘中斷時(shí)運(yùn)行。其執(zhí)行:

  • 獲得xtime_lock鎖,以便對訪問jiffies_64和xtime進(jìn)行保護(hù)
  • 需要時(shí)應(yīng)答或重設(shè)系統(tǒng)時(shí)鐘
  • 周期性使用墻上時(shí)間更新實(shí)時(shí)時(shí)鐘
  • 調(diào)用體系結(jié)構(gòu)無關(guān)的時(shí)鐘例程tick_periodic()
  • 釋放xtime_lock鎖

tick_periodic()執(zhí)行:

  • jiffies_64加1
  • 更新資源消耗的統(tǒng)計(jì)值
  • 執(zhí)行以及到期的動態(tài)定時(shí)器
  • 執(zhí)行sheduler_tick()函數(shù)
  • 更新墻上時(shí)間,存放在xtime變量中
  • 計(jì)算平均負(fù)載值

六、實(shí)際(墻上)時(shí)間

墻上時(shí)間保存在xtime變量中:

struct timespec {
  _kernel_time_t tv_sec;  //秒,19700101以來經(jīng)過的s數(shù)
  long tv_nsec; //ns,上一秒開始經(jīng)過的ns數(shù)
};

struct timespec xtime;

獲取墻上時(shí)間的系統(tǒng)調(diào)用為:

int gettimeofday(struct  timeval*tv,struct  timezone *tz )

七、定時(shí)器

定時(shí)器(也稱動態(tài)定時(shí)器、內(nèi)核定時(shí)器)是管理內(nèi)核流逝的時(shí)間的基礎(chǔ)。由結(jié)構(gòu)timer_list表示:

struct timer_list {
  struct list_head entry; 
  unsigned long expires; //以jiffies為單位的定時(shí)值
  void (*function) (unsigned long); //定時(shí)器處理函數(shù)
  unsigned long data; //定時(shí)器處理函數(shù)的參數(shù)
  struct tvec_t_base_s *base;
};

使用定時(shí)器:

//定義定時(shí)器
struct timer_list my_timer;

//初始化
init_timer(&my_timer);
//填充
my_timer.expires =
my_timer.function =
my_timer.data =

// 激活定時(shí)器
add_timer(&my_timer);

//修改定時(shí)器超時(shí)時(shí)間
mod_timer(&my_timer, jiffies+new_delay);

//停止定時(shí)器
del_timer(&my_timer);
del_timer_sync(&my_timer);

內(nèi)核在時(shí)鐘中斷發(fā)生后執(zhí)行定時(shí)器,定時(shí)器作為軟中斷在下半部上下文執(zhí)行。

八、延遲執(zhí)行

8.1 忙等待

忙等待是最簡單的延遲方法,僅僅在想要延遲的時(shí)間是節(jié)拍的整數(shù)倍,或精確率要求不高時(shí)才可以使用。

8.2 短延遲

有時(shí)內(nèi)核需要很短的延遲,而且還要求延遲的時(shí)間很準(zhǔn)確,就需要延遲函數(shù):

void udelay(unsigned long usecs); //us
void ndelay(unsigned long nsecs);//ns
void mdelay(unsigned long msecs);//ms

BogoMIPS值記錄處理器在給定時(shí)間內(nèi)忙循環(huán)執(zhí)行的次數(shù)。udelay函數(shù)根據(jù)指定的延遲時(shí)間在1s中的占比,就能決定需要進(jìn)行多少次循環(huán)就可以達(dá)到要求的推遲時(shí)間。

8.3 schedule_timeout()

schedule_timeout()會讓需要延遲的任務(wù)睡眠到指定的延遲時(shí)候耗盡再重新運(yùn)行。當(dāng)指定時(shí)間到期后,內(nèi)核喚醒被延遲的任務(wù)并將其重新放回到運(yùn)行隊(duì)列中。schedule_timeout()調(diào)用之前,必須將任務(wù)置成TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE狀態(tài),否則不會休眠。調(diào)用schedule_timeout的代碼必須處于進(jìn)程上下文,并且不能持有鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 目錄: 上篇從日常代碼出發(fā),著重討論了Java、MySQL等應(yīng)用層中日期時(shí)間的表示和存儲等操作、可能遇到的坑,及時(shí)...
    Wayne566閱讀 937評論 0 1
  • 定時(shí)器的實(shí)現(xiàn)原理 定時(shí)器的實(shí)現(xiàn)依賴的是CPU時(shí)鐘中斷,時(shí)鐘中斷的精度就決定定時(shí)器精度的極限。一個(gè)時(shí)鐘中斷源如何實(shí)現(xiàn)...
    linux大本營閱讀 946評論 0 0
  • 定時(shí)器的使用 1、定義定時(shí)器結(jié)構(gòu)體timer_list。 2、設(shè)置超時(shí)時(shí)間,定義定時(shí)器處理函數(shù)和傳參。 3、激活定...
    gbmaotai閱讀 909評論 0 0
  • 進(jìn)程 創(chuàng)建 創(chuàng)建進(jìn)程用fork()函數(shù)。fork()為子進(jìn)程創(chuàng)建新的地址空間并且拷貝頁表。子進(jìn)程的虛擬地址空間...
    梅花怒閱讀 2,079評論 0 7
  • 前言: Linux中如何對時(shí)間進(jìn)行管理?時(shí)鐘節(jié)拍的概念及延時(shí)函數(shù)的用法很多同學(xué)都用不好,下面我給大家總結(jié)一下。 一...
    華清遠(yuǎn)見2閱讀 610評論 0 0

友情鏈接更多精彩內(nèi)容