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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

linux内核之时间子系统

發(fā)布時間:2023/12/3 综合教程 40 生活家
生活随笔 收集整理的這篇文章主要介紹了 linux内核之时间子系统 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、概要

???????? 時間管理在內核中占有非常重要的地位,內核中有大量的函數(shù)都是基于時間驅動的。有些函數(shù)需要周期執(zhí)行,而有些需要等待一個相對時間后才運行,此外內核還必須管理系統(tǒng)的運行時間以及當前日期和時間。內核必須在硬件的幫助下才能計算和管理時間。

???????? 硬件為內核提供了一個系統(tǒng)定時器用以計算流逝的時間,該時鐘在內核中可看成是一個電子時間資源。系統(tǒng)定時器以某種頻率自行觸發(fā)或射中時鐘中斷,當時鐘中斷發(fā)生時內核就通過一種特殊的中斷處理程序對其進行處理。該頻率可以通過編程預定(即節(jié)拍率),連續(xù)兩次時鐘中斷的間隔時間稱為節(jié)拍(tick)。利用時間中斷周期執(zhí)行的主要工作:

A.更新系統(tǒng)運行時間

B.更新墻上時間

C.在smp系統(tǒng)上,均衡調度程序中各處理器上的運行隊里

D.檢查當前進程是否用盡了自己的時間片

E.運行超時的動態(tài)定時器

F.更新資源消耗和處理器時間的統(tǒng)計值

?

1、節(jié)拍率:HZ

???????? 系統(tǒng)定時器頻率是通過靜態(tài)預處理定義的,就是HZ,在系統(tǒng)啟動時按照HZ值對硬件進行設置。不同體系結構,HZ不同,但大部分都是100即一個tick是10ms。

???????? 一個合適的HZ是相當重要的,下面分析了高HZ的優(yōu)勢和劣勢:

優(yōu)勢:

A.內核定時器能夠以更高的頻度和更高的準確度運行

B.依賴定時值的系統(tǒng)調用(poll和select)能夠以更高的精度運行

C.對諸如資源消耗和系統(tǒng)運行時間等的測量會有更精細的解析度

D.提高進程搶占的準確度

劣勢:

A.中斷頻率越高,切換頻繁,系統(tǒng)負擔越重

B.中斷處理程序占用的CPU時間越多

C.更頻繁的打亂處理器高速緩存并增加耗電

2、jiffies

???????? 全局變量jiffies用來記錄自系統(tǒng)啟動以來產生的節(jié)拍的總數(shù)。內核給jiffies賦了一個特殊的初值,引起盡早溢出捕捉bug,每次時鐘中斷處理程序就會增加該變量的值。

???????? 在include/linux/jiffies.h文件中定義了該變量:

extern unsigned long volatile __jiffy_datajiffies;

jiffies為無符號長整形,在32位體系結構上是32位,在64位體系結構上是64位。

???????? 當jiffies變量的值超過它的最大存放范圍后就會發(fā)生溢出,回繞到0。因此,內核提供了四個宏來幫助比較節(jié)拍計數(shù),能正確地處理節(jié)拍計數(shù)回繞情況:

time_after(a, b) time_before(a, b)time_after_eq(a, b) time_before_eq(a, b)

3、硬件時鐘和定時器

???????? 體系結構提供了兩種設備進行計時:實時時鐘和系統(tǒng)定時器。

實時時鐘:

???????? 用來持久存放系統(tǒng)時間的設備,即便系統(tǒng)關閉后也可以靠主板上的微型電池提供的電力保持系統(tǒng)的計時。當系統(tǒng)啟動時,內核通過讀取RTC來初始化墻上時間,該時間存放在xtime變量中,然后定期同步兩者時間保持一致,重啟時可以得到一個相對準確的時間。

系統(tǒng)定時器:

???????? 內核定時機制中最為重要的角色,提供一種周期性觸發(fā)中斷機制。相應的中斷處理程序主要完成以下工作:

A.更新jiffies

B.更新墻上時間

C.更新資源消耗的統(tǒng)計值,比如當前進程所消耗的系統(tǒng)時間和用戶時間

D.執(zhí)行到期的動態(tài)定時器

E.執(zhí)行scheduler_tick()函數(shù)

F.計算平均負載值

4、實際時間

???????? 當前實際時間定義在文件kernel/time/timekeeping.c中:

static struct timekeeper timekeeper

struct timekeeper {

u64????????xtime_sec;

u64????????xtime_nsec;

}

xtime_sec以秒為單位,存放著自1970年1月1日(UTC)以來經過的時間,該時間點被稱為紀元,多數(shù)Unix系統(tǒng)的墻上時間都是基于該紀元而言的。xtime_nsec上一秒開始經過的ns數(shù)。

???????? 通過gettimeofday和settimeofday來獲取和設置當前時間,設置時間需要具有CAP_SYS_TIME權能。

5、動態(tài)定時器

???????? 它是管理內核流逝的時間的基礎,使用簡單。只需要執(zhí)行一些初始化工作,設置一個超時時間,指定超時發(fā)生后執(zhí)行的函數(shù),然后激活定時器就可以了。

???????? 定時器由結構timer_list表示,定義在文件<include/linux/timer.h >

struct timer_list {??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???/*???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

????* All fields that change during normal runtime grouped to the???????????????????????????????????????????????????????????????????????????????

????* same cacheline????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

????*/??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???struct list_head entry;? ?//定時器鏈表入口????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???unsigned long expires;? ?//以jiffies為單位的定時值?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???struct tvec_base *base;? //定時器內部值,用戶不使用? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

???void (*function)(unsigned long); //定時器處理函數(shù)????????????????????????????????????????????????????????????????????????????????????????????????????????????

???unsigned long data;????????? //傳遞給處理函數(shù)的長整型參數(shù)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

intslack;????????

???????? …

}

???????? 使用方法如下:

創(chuàng)建定時器:structtimer_list my_timer

初始化定時器:init_timer(&my_timer)

定時值初始化:

??????????????????????????? my_timer.expires= jiffies + delay

??????????????????????????? my_timer.data= 0

??????????????????????????? my_timer.function= my_function( void my_function(unsigned long data))

激活定時器:add_timer(&my_timer)

修改已經激活的定時器超時時間:mod_timer(&my_timer,jiffies+new_delay)

在定時器超市前停止定時器:del_timer(&my_timer)或 del_timer_sync(&my_timer)

? ?????? 定時器作為軟中斷在下半部上下文中執(zhí)行,時鐘中斷處理程序會通過raise_softirq(TIMER_SOFTIRQ)喚醒定時器軟中斷,從而在當前處理器上運行所有的超時定時器。雖然所有定時器都以鏈表形式存放在一起,但是為了提高搜索效率,內核將定時器按照他們的超時時間劃分成五組,當定時器超時時間接近時,定時器將隨組一起下移。

6、延遲執(zhí)行

???????? 內核代碼(尤其是驅動程序)除了使用定時器或下半部機制以外,還需要其他方法來推遲執(zhí)行任務。內核提供了許多延遲方法處理各種延遲要求:

A.忙等待

???????? 該方法想要延遲的時間是節(jié)拍的整數(shù)倍,或者精確率要求不高時使用

unsinged long timeout = jiffies + 10

while (time_before(jiffies, timeout))

???????? ;

或者

while(time_before(jiffies, delay))

???????? cond_resched();

B.短延遲:

???????? voidudelay(unsigned long usecs)

???????? voidndelay(unsigned long nsecs)

???????? voidmdelay(unsigned long msecs)

/proc/cpuinfo的BogoMIPS主要被udelay()和mdelay()函數(shù)使用,記錄處理器在給定時間內忙循環(huán)執(zhí)行的次數(shù)。

C.schedule_timeout()

???????? 該方法會讓需要延遲執(zhí)行的任務睡眠到指定的延遲時間耗盡后再重新運行。唯一的參數(shù)是延遲的相對時間,單位是jiffies。需要注意一點:

該函數(shù)需要調用調度程序,所以調用它的代碼必須保證能夠睡眠。換句話說,調用代碼必須處于進程上下文中,并且不能持有鎖。

二、linux時間框架

1、框架

隨著技術發(fā)展,出現(xiàn)了下面兩種新的需求:

(1)嵌入式設備需要較好的電源管理策略。傳統(tǒng)的linux會有一個周期性的時鐘,即便是系統(tǒng)無事可做的時候也要醒來,這樣導致系統(tǒng)不斷的從低功耗(idle)狀態(tài)進入高功耗的狀態(tài)。這樣的設計不符合電源管理的需求。

(2)多媒體的應用程序需要非常精確的timer,例如為了避免視頻的跳幀、音頻回放中的跳動,這些需要系統(tǒng)提供足夠精度的timer。

???????? 因此,內核時間子系統(tǒng)也進行了框架調整,引進了高精度timer。和低精度timer不同,高精度timer使用了人類的最直觀的時間單位ns(低精度timer使用的tick是和內核配置相關,不夠直接)。本質上linuxkernel提供了高精度timer之后,其實不必提供低精度timer了,不過由于低精度timer存在了很長的歷史,并且滲入到內核各個部分,如果去掉容易引起linuxkernel穩(wěn)定性和健壯性的問題,因此kernel保持兩種并存。其示意圖如下:

???????? 在smp情況下,driver硬件定時器劃分成了兩部分:一個提供給clocksource模塊使用,一個提供給clockevent事件使用。而clockevent又簡單化為兩個小部分:一個是每個CPU自己的定時器,一個是全局定時器;每個CPU定時器管理本CPU的任務運行情況、資源統(tǒng)計等,第一個啟動CPU(一般是CPU0)還會更新tick和墻上時鐘,而globaltimer則主要用于低功耗模式下CPU進入睡眠時喚醒所有CPU。

???????? Kernel抽象了底層驅動,劃分為clockevent和clocksource連個模塊,驅動加載時會調用兩個模塊接口進行注冊。clocksource的精度就是定時器時鐘頻率的精度(ns級別),可以認為是一個timeline,而clockevent則是這個timeline上的特定時間點,產生中斷后調用相應的callback函數(shù)進行事件處理。

???????? tickdevice layer 基于clockevent設備進行工作:每個CPU都有自己唯一的tickdevice,管理自己的調度,進程統(tǒng)計等。Tickdevice可以工作在兩種模式:periodic 和 one shot mode。有多少CPU就有多少個tickdevice稱之為local tickdevice,在所有的local tickdevice中會有一個被選為globaltick device(一般是CPU0的tick device),該device負責維護整個系統(tǒng)的jiffies,更新wall clock,計算全局負荷什么的。

???????? 高精度hrtimer需要高精度的clockevent,工作在one shotmode的tick device提供高精度的clockevent。雖然有了高精度hrtimer的出現(xiàn),內核并沒有拋棄老的低精度timer機制。當系統(tǒng)處于高精度timer的時候,系統(tǒng)會setup一個特別的高精度hrtimer(sched_timer),該高精度timer會周期性的觸發(fā),從而模擬傳統(tǒng)的periodictick,推動傳統(tǒng)低精度timer的運轉(代碼沒有弄明白sched_timer?)。

2、內核配置

(1)GENERIC_CLOCKEVENTS和GENERIC_CLOCKEVENTS_BUILD:代表使用新的時間子系統(tǒng)架構,默認就是配置好的

(2)新時間子系統(tǒng)框架下,Timerssubsystem配置選項主要和tick已經是否支持高精度hrtimer有關。Tick有2種配置(二選一):

CONFIG_HZ_PERIODIC:無論何時都啟用周期性的tick,即便是在系統(tǒng)idle的時候。

CONFIG_NO_HZ_IDLE:該選項默認會打開 CONFIG_TICK_ONESHOT 和CONFIG_NO_HZ_COMMON,在系統(tǒng)idle的時候,停掉周期性tick。

CONFIG_HIGH_RES_TIMERS:支持高精度hrtimer

配置了高精度hrtimer或NO_HZ_COMMOM就一定配置CONFIG_TICK_ONESHOT,表示系統(tǒng)支持one-shot類型的tick_device。

???????? 因此,在新時間子系統(tǒng)下有4種配置:

A.低精度timer和周期tick

B.低精度timer和dynamic tick(tickless idle)(當前系統(tǒng)使用情況)

C.高精度hrtimer和周期tick

D.高精度hrtimer和dynamic tick(tickless idle)

3、四種配置(轉載自附錄網(wǎng)站)

(1)低精度timer + 周期tick

我們首先看周期性tick的實現(xiàn)。起始點一定是底層的clocksource chip driver,該driver會調用接口clockevents_register_device向clock event注冊。一旦增加了一個clockevent device,需要通知上層的tickdevice layer,有可能新注冊的這個device更好、更適合某個tick device。要是這個clock eventdevice被某個tickdevice收留了(要么該tickdevice之前沒有匹配的clockevent device,要么新的clockevent device更適合該tickdevice),那么就啟動對該tickdevice的配置(參考tick_setup_device)。根據(jù)當前系統(tǒng)的配置情況(周期性tick),會調用tick_setup_periodic函數(shù),這時候,該tick device對應的clock event device的clock event handler被設置為tick_handle_periodic。底層硬件會周期性的產生中斷,從而會周期性的調用tick_handle_periodic從而驅動整個系統(tǒng)的運轉。需要注意的是:即便是配置了CONFIG_NO_HZ和CONFIG_TICK_ONESHOT,系統(tǒng)中沒有提供one shot的clock event device,這種情況下,整個系統(tǒng)仍然是運行在周期tick的模式下。

下面來到低精度timer模塊了,其實即便沒有使能高精度timer,內核也會把高精度timer模塊的代碼編譯進kernel的image中,這一點可以從Makefile文件中看出。在這種構架下,各個內核模塊也可以調用linuxkernel中的高精度timer模塊的接口函數(shù)來實現(xiàn)高精度timer,但是,這時候高精度timer模塊是運行在低精度的模式,也就是說這些hrtimer雖然是按照高精度timer的紅黑樹進行組織,但是系統(tǒng)只是在每一周期性tick到來的時候調用hrtimer_run_queues函數(shù),來檢查是否有expire的hrtimer。毫無疑問,這里的高精度timer也就是沒有意義了。

由于存在周期性tick,低精度timer的運作毫無壓力,和過去一樣。

(2)低精度timer + Dynamic Tick

系統(tǒng)開始的時候并不是直接進入Dynamictick mode的,而是經歷一個切換過程。開始的時候,系統(tǒng)運行在周期tick的模式下,各個cpu對應的tick device的(clock event device的)event handler是tick_handle_periodic。在timer的軟中斷上下文中,會調用tick_check_oneshot_change進行是否切換到one shot模式的檢查,如果系統(tǒng)中有支持one-shot的clock event device,并且沒有配置高精度timer的話,那么就會發(fā)生tick mode的切換(調用tick_nohz_switch_to_nohz),這時候,tick device會切換到one shot模式,而event handler被設置為tick_nohz_handler。由于這時候的clock eventdevice工作在oneshot模式,因此當系統(tǒng)正常運行的時候,在eventhandler中每次都要reprogramclock event,以便正常產生tick。當cpu運行idle進程的時候,clock eventdevice不再reprogram產生下次的tick信號,這樣,整個系統(tǒng)的周期性的tick就停下來。

(3)高精度timer + Dynamic Tick

同樣的,系統(tǒng)開始的時候并不是直接進入Dynamictick mode的,而是經歷一個切換過程。系統(tǒng)開始的時候是運行在周期tick的模式下,event handler是tick_handle_periodic。在周期tick的軟中斷上下文中(參考run_timer_softirq),如果滿足條件,會調用hrtimer_switch_to_hres將hrtimer從低精度模式切換到高精度模式上。這時候,系統(tǒng)會有下面的動作:

A.Tickdevice的clockevent設備切換到oneshotmode(參考tick_init_highres函數(shù))

B.Tickdevice的clockevent設備的eventhandler會更新為hrtimer_interrupt(參考tick_init_highres函數(shù))

C.設定schedtimer(也就是模擬周期tick那個高精度timer,參考tick_setup_sched_timer函數(shù))

這樣,當下一次tick到來的時候,系統(tǒng)會調用hrtimer_interrupt來處理這個tick(該tick是通過sched timer產生的)。

在Dynamictick的模式下,各個cpu的tick device工作在one shot模式,該tick device對應的clock event設備也工作在one shot的模式,這時候,硬件Timer的中斷不會周期性的產生,但是linuxkernel中很多的模塊是依賴于周期性的tick的,因此,在這種情況下,系統(tǒng)使用hrtime模擬了一個周期性的tick。在切換到dynamic tick模式的時候會初始化這個高精度timer,該高精度timer的回調函數(shù)是tick_sched_timer。這個函數(shù)執(zhí)行的函數(shù)類似周期性tick中event handler執(zhí)行的內容。不過在最后會reprogram該高精度timer,以便可以周期性的產生clockevent。當系統(tǒng)進入idle的時候,就會stop這個高精度timer,這樣,當沒有用戶事件的時候,CPU可以持續(xù)在idle狀態(tài),從而減少功耗。

(4)高精度timer + 周期性Tick

這種配置不多見,多半是由于硬件無法支持oneshot的clockevent device,這種情況下,整個系統(tǒng)仍然是運行在周期tick的模式下。

三、用戶接口

1、系統(tǒng)時間相關服務

(1)秒級函數(shù):time和stime

#include <time.h>

time_t time(time_t *t); //獲取時間秒

int stime(time_t *t); ?//設置時間秒

對應的系統(tǒng)調用為:sys_time和sys_stime。time函數(shù)返回當前點到linuxepoch的秒數(shù),stime設定當前時間點到linuxepoch的秒數(shù)。

???????? 與上面函數(shù)配套的還有一系列時間點與linuxepoch轉換函數(shù):mktime,localtime_r。

(2)微秒級函數(shù):gettimeofday和settimeofday

#include <sys/time.h>

int gettimeofday(struct timeval *tv, structtimezone *tz);

int settimeofday(const struct timeval *tv,const struct timezone *tz);

struct timeval {

time_t????? tv_sec;???? /* seconds */

suseconds_ttv_usec;??? /* microseconds */

};

struct timezone {

inttz_minuteswest;???? /* minutes west ofGreenwich */

inttz_dsttime;????? ???/* type of DST correction */

};

對應的系統(tǒng)調用:sys_gettimeofday和sys_settimeday。Gettimeofday獲取linux epoch到當前時間點的秒數(shù)以及微秒數(shù);settimeofday則設定從linuxepoch到當前時間點的秒數(shù)以及微秒數(shù)。

值得一提的是:這些系統(tǒng)調用在新的POSIX標準中接口被clock_gettime和clock_settime取代。

(3)納秒級別的時間函數(shù):clock_gettime和clock_settime

#include <time.h>

int clock_getres(clockid_t clk_id, structtimespec *res); //獲取clock_id的系統(tǒng)時鐘精度

int clock_gettime(clockid_t clk_id, structtimespec *tp);

int clock_settime(clockid_t clk_id, conststruct timespec *tp);

struct timespec {

time_t?? tv_sec;??????? /* seconds */

long???? tv_nsec;?????? /* nanoseconds */

};

clk_id識別systemclock的ID,定義如下:

CLOCK_REALTIME:真實的墻上時鐘,前面函數(shù)就是從獲取該ID的值

CLOCK_MONOTONIC:該時鐘是單調遞增的,也是真實的墻上時鐘只是起始點不一定是linuxepoch,一般會把系統(tǒng)啟動的時間點設定為基準點。除了NTP和adjtime對該時鐘進行調整外,其他任何接口不允許設定該時鐘,保證該時鐘的單調性。可以了解系統(tǒng)啟動時間。

CLOCK_MONOTONIC_RAW:具備CLOCK_MONOTONIC的特性,但它不受NTP和adjtime影響,是完全基于本地晶振的時鐘。一般程序員不大用。

CLOCK_BOOTTIME:類似CLOCK_MONOTONIC,但是系統(tǒng)suspend時依然增加。

CLOCK_PROCESS_CPUTIME_ID:每個CPU的高精度進程定時器,clock_getcpuclockid獲取進程的clock_id

CLOCK_THREAD_CPUTIME_ID:線程的CPU時間,pthread_getcpuclockid獲取線程的clock_id。

(4)系統(tǒng)時鐘調整

???????? 上面設定系統(tǒng)時間是一個比較粗暴的做法,一旦修改了系統(tǒng)時間,系統(tǒng)中很多以來絕對時間的進程會有各種奇怪的行為。所以系統(tǒng)提供了時間同步的接口函數(shù),可以讓外部的精準計時服務器不斷的修正系統(tǒng)時鐘。

A.adjtime

int adjtime(const struct timeval *delta,struct timeval *olddelta);

struct timeval {

time_t????? tv_sec;???? /* seconds */

suseconds_ttv_usec;?? ?/* microseconds */

};

???????? 該函數(shù)可以根據(jù)delta參數(shù)緩慢的修正系統(tǒng)時鐘(CLOCK_REALTIME那個)。olddelta返回上一次調整中尚未完成的delta。

B.adjtimex

#include <sys/timex.h>

int adjtimex(struct timex *buf);

struct timex {

intmodes;?????????? /* mode selector */

longoffset;???????? /* time offset (usec) */

longfreq;?????????? /* frequency offset(scaled ppm) */

longmaxerror;?????? /* maximum error (usec)*/

longesterror;?????? /* estimated error (usec)*/

intstatus;????????? /* clock command/status*/

longconstant;?????? /* pll time constant */

longprecision;????? /* clock precision (usec)(read-only) */

longtolerance;????? /* clock frequencytolerance (ppm)

??????????????????????????????????????(read-only) */

structtimeval time; /* current time (read-only) */

longtick;?????????? /* usecs between clockticks */

};

???????? 該函數(shù)用來顯示或這修改linux內核的時間變量的工具,提供了對與內核時間變量直接訪問功能,可以實現(xiàn)對于系統(tǒng)時間的飄逸進行修正。任何用戶都可以使用它查看,但是只有root用戶才可以更改這些參數(shù)。

2、進程睡眠

(1)秒級函數(shù):sleep

#include <unistd.h>

unsigned int sleep(unsigned int seconds);

???????? 該函數(shù)會導致當前進程sleepseconds之后(基于CLOCK_REALTIME)返回繼續(xù)執(zhí)行程序。返回值說明了進程沒有進入睡眠的時間。

(2)微秒級別函數(shù):usleep

#include <unistd.h>

int usleep(useconds_t usec);

???????? 該函數(shù)功能和上面一樣,不過返回值定義不同。0:表示執(zhí)行成功,-1:執(zhí)行失敗,錯誤碼在errno中。

(3)納秒級別函數(shù):nanosleep

#include <time.h>

int nanosleep(const struct timespec *req,struct timespec *rem);

struct timespec {

time_ttv_sec;??????? /* seconds */

long?? tv_nsec;?????? /* nanoseconds */

};

該函數(shù)取代了usleep函數(shù),req中設定你要sleep的秒以及納秒值,rem表示還有多少時間沒睡完。返回0表示成功,返回-1說明失敗。

sleep/usleep/nanosleep的系統(tǒng)都是通過kernel的sys_nanosleep系統(tǒng)調用實現(xiàn)(底層基于hrtimer)。

(4)更高級的sleep函數(shù):clock_nanosleep

#include <time.h>

int clock_nanosleep(clockid_t clock_id, intflags,

?????????????????????????? const structtimespec *request,

?????????????????????????? struct timespec*remain);

clock_id說明該函數(shù)不僅能基于real_timeclock睡眠,還可以基于其他的系統(tǒng)時鐘睡眠。flag等0或1,分別指明request參數(shù)設定的時間值是相對時間還是絕對時間。

3、timer相關的服務

(1)alarm函數(shù)

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

???????? 該函數(shù)在指定秒數(shù)(基于CLOCK_REALTIME)的時間過去后,向該進程發(fā)送SIGALRM信號。調用該接口的程序需要設定signalhandler。

四、代碼解析

第二章節(jié)講述內核配置有兩種主要模式:periodic和 one-shot,下面代碼分析主要根據(jù)實際使用的低精度tick和dynamic tick (tickles tick)即使用one-shot配置進行講解(Linux-3.10.y)。

1、數(shù)據(jù)結構

(1)clocksource

內核使用structclocksource數(shù)據(jù)結構記錄時鐘源所有信息,主要作為系統(tǒng)時間的基準,當有多個時鐘源時選擇最優(yōu)那個,沒有時鐘源時默認使用基于jiffies的時鐘clocksource_jiffies。內核通過一個鏈表clocksource_list管理所有注冊的時鐘源,每個時鐘源定義了一個單調增加的計數(shù)器并以ns為單位。

???????? structclocksource結構體詳細如下(include/linux/clocksource.h):

struct clocksource {

???cycle_t (*read)(struct clocksource *cs);?//讀取指定CS的cycle值(定時器當前計數(shù)值)

???cycle_t cycle_last;?? //保存最近一次read的cycle值(其中一個重要作用翻轉)

cycle_tmask; //counter是32位還是64位

//公式:ns =(cycles/F) * NSEC_PER_SEC = (cycle* mult) >> shift

???u32 mult; //cycle轉化為ns的乘數(shù)

???u32 shift; //cycle轉化為ns的除數(shù),采用移位的方式

???u64 max_idle_ns; //該時鐘允許的最大空閑時間(沒搞明白如何用)

???u32 maxadj;???? //最大調整值與mult相關

#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA //未用

???struct arch_clocksource_data archdata;

#endif

???const char *name; //時鐘源名字

???struct list_head list; //注冊時鐘源鏈表頭

intrating; //時鐘源精度值,

1--99: 不適合于用作實際的時鐘源,只用于啟動過程或用于測試;

100--199:基本可用,可用作真實的時鐘源,但不推薦;

200--299:精度較好,可用作真實的時鐘源;

300--399:很好,精確的時鐘源;

400--499:理想的時鐘源,如有可能就必須選擇它作為時鐘源;

??? int(*enable)(struct clocksource *cs); //使能時鐘源

???void (*disable)(struct clocksource *cs); //禁止時鐘源

???unsigned long flags; //時鐘源屬性,CLOCK_SOURCE_IS_CONTINUOUS連續(xù)時鐘

???void (*suspend)(struct clocksource *cs); //掛起時鐘源

???void (*resume)(struct clocksource *cs); //恢復時鐘源

#ifdef CONFIG_CLOCKSOURCE_WATCHDOG?? //未用

???/* Watchdog related data, used by the framework */

???struct list_head wd_list;

???cycle_t cs_last;

???cycle_t wd_last;

#endif

}____cacheline_aligned;

(2)clockevent

內核使用struct clock_event_device數(shù)據(jù)結構記錄時鐘的事件信息,包括硬件時鐘中斷發(fā)生時要執(zhí)行的那些操作。提供了對周期性事件和單觸發(fā)事件的支持。還提供了高精度定時器和動態(tài)定時器的支持。內核通過一個clockevent_devices管理所有注冊的clock event設備。

???????? 該結構體在頭文件include/linux/clockchips.h中定義,詳細定義如下:

struct clock_event_device {

???void ???????? (*event_handler)(structclock_event_device *); //事件處理函數(shù),主要有三種:peridioc\one-shot\broadcast

???int??? (*set_next_event)(unsignedlong evt, struct clock_event_device *); //設置下次觸發(fā)事件基于clocksource的cycles

???int??? (*set_next_ktime)(ktime_texpires, struct clock_event_device *); //設置下次觸發(fā)事件基于ktime(用得比較少)

???ktime_t??? next_event;

???u64? max_delta_ns; //可設置的最大時間差

???u64? min_delta_ns; //可設置的最小時間差

???u32? mult; //和clocksource一樣

???u32? shift; //和clocksource一樣

???enum clock_event_mode?? mode; //clockevent工作模式,見下面

???unsigned int??????? features; //clockevent設備特征,見下面

?? ?unsigned long?????? retries;

???void? (*broadcast)(const structcpumask *mask); //廣播所有CPU函數(shù)

???void? (*set_mode)(enumclock_event_mode mode, struct clock_event_device *); //設置模式

???void? (*suspend)(structclock_event_device *);

???void? (*resume)(struct clock_event_device*);

???unsigned long? min_delta_ticks;

???unsigned long? max_delta_ticks;

???const char??????? *name;

???int??? rating;

???int??? irq;

???const struct cpumask????? *cpumask;//CPU掩碼,判斷是否屬于某一個CPU,或廣播支持的CPU

???struct list_head??????? list;

} ____cacheline_aligned;

???????? clockevent設備工作模式:

enum clock_event_mode {

???CLOCK_EVT_MODE_UNUSED = 0,??????????

???CLOCK_EVT_MODE_SHUTDOWN,?????????? //關閉模式

???CLOCK_EVT_MODE_PERIODIC,?????????????? //周期性模式

???CLOCK_EVT_MODE_ONESHOT,????? //單次模式

???CLOCK_EVT_MODE_RESUME,??????? //恢復模式

};

???????? clockevent設備特征:

#define CLOCK_EVT_FEAT_PERIODIC???? 0x000001 ???????? //可以產生周期觸發(fā)事件特征

#define CLOCK_EVT_FEAT_ONESHOT???? 0x000002????????? //可以產生單觸發(fā)事件特征

#define CLOCK_EVT_FEAT_KTIME?????????? 0x000004 ?????? //產生事件的事件基準ktime

//X86下使用,進入省電情況

#define CLOCK_EVT_FEAT_C3STOP???????? 0x000008 ?????? //clocksource停止,需要廣播事件支持,本ARM平臺也使用了該選項

#define CLOCK_EVT_FEAT_DUMMY?????? 0x000010????????? // Local APIC timer使用該選項

(3)tick_device

???????? struct tick_device只是對struct clock_event_device的一個封裝,加入了運行模式變量,支持PERIODIC和ONESHOT兩種模式。

struct tick_device {

???struct clock_event_device *evtdev;

???enum tick_device_mode mode;

};

enum tick_device_mode {

???TICKDEV_MODE_PERIODIC,

???TICKDEV_MODE_ONESHOT,

};

2、內核初始化

???????? 在內核啟動函數(shù)start_kernel里對時間系統(tǒng)進行了初始化

(1)tick_init

???????? 該函數(shù)初始化tick控制。向clockevents_chain通知鏈中添加一個tick通知分發(fā)器tick_notifier(分發(fā)回調函數(shù):tick_notify)。在底層驅動注冊設備時,CLOCK_EVT_NOTIFY_ADD消息就是添加了一個新的clockevent設備。

初始化tick broadcast掩碼,如果配置了CONFIG_TICK_ONESHOT相關掩碼也要初始化。

(2)init_timers

初始化本CPU上的低精度定時器相關的數(shù)據(jù)結構,將通知分發(fā)器timers_nb添加到cpu_chain通知鏈;初始化定時器軟中斷open_softirq(TIMER_SOFTIRQ, run_timer_softirq);

(3)hrtimers_init

初始化本CPU上的高精度精度定時器相關的數(shù)據(jù)結構,將通知分發(fā)器hrtimers_nb添加到cpu_chain通知鏈;如果開啟高精度定時器宏,則初始化高精度定時器軟中斷open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq)(本平臺為使用)。

(4)timekeeping_init

初始化時鐘源clocksource及timekeeping模塊時間初始值,如果平臺沒有更好的時鐘源,系統(tǒng)使用jiffies作為時鐘源。

clock = clocksource_default_clock();

struct clocksource * __init __weakclocksource_default_clock(void)

{

???return &clocksource_jiffies;

}

struct clocksource clocksource_jiffies = {

???.name?????? = "jiffies",

???.rating???? = 1, /* lowest validrating*/

???.read?????? = jiffies_read,

???.mask?????? = 0xffffffff,/*32bits*/

???.mult?????? = NSEC_PER_JIFFY<< JIFFIES_SHIFT, /* details above */

???.shift????? = JIFFIES_SHIFT,

};

(5)time_init

前面函數(shù)時內核通用架構,該函數(shù)為硬件時鐘初始化平臺相關,一般由各個平臺自己實現(xiàn),細節(jié)見下節(jié)。

void __init time_init(void)

{??

???if (machine_desc->init_time)

? ??????machine_desc->init_time(); (=hi3536_timer_init)

???else

???????clocksource_of_init();

???sched_clock_postinit();

}

?

3、硬件時鐘初始化

(1)平臺注冊

MACHINE_START(HI3536, "hi3536")

.atag_offset? = 0x100,

???.map_io?????? = hi3536_map_io,

???.init_early?? = hi3536_init_early,

???.init_irq???? =hi3536_gic_init_irq,

#ifdef CONFIG_HI3536_SYSCNT

???.init_time??? = arch_timer_init,

#else

??? .init_time??? =hi3536_timer_init,

#endif?

???.init_machine = hi3536_init,

???.smp????????? =smp_ops(hi3536_smp_ops),

???.reserve????? = hi3536_reserve,

???.restart????? = hi3536_restart,

???MACHINE_END

???????? 上一節(jié)machine_desc的具體實現(xiàn)即為該宏定義,init_time就是hi3536_timer_init,其具體內容見下面。

?

(2)平臺定時器初始化

void __init hi3536_timer_init(void)

{

/* 設置所有定時器的工作時鐘(未初始化,默認3MHZ)

???????? Hi3536有time0~910個時鐘

???????? 根據(jù)配置,所有定時器都配置成了總線時鐘125Mhz(8ns)

*/

???writel(readl((const volatile void *)IO_ADDRESS(REG_BASE_SCTL)) |

???????(1 << 16) | (1 << 18),

???????(volatile void *)IO_ADDRESS(REG_BASE_SCTL));???

#ifdef CONFIG_SP804_LOCAL_TIMER

???hi3536_local_timer_init(); //每個CPU使用一個定時器作為local timer,該平臺有4核CPU0~CPU3分別對應:timer4~timer7,注冊為clockevent設備

#endif

???hi3536_clocksource_init((void *)TIMER(0)->addr,

???????TIMER(0)->name); //timer0作為clocksource設備

???sp804_clockevents_init((void *)TIMER(1)->addr,

???????TIMER(1)->irq.irq, TIMER(1)->name); //timer1作為clockevent設備的globaltimer

}

???????? 從上面看,hi3536總共支持10個timer,實際使用了6個,timer0/timer1,timer4~timer7,其他保留。其注冊的順序如下:timer0(clocksource)—> timer1(clockevent global timer) —> timer4(clockevent cpu0 local timer)—> time5~7(clockevent cpu1~3 local timer)。每類定時器的初始化,見下面細節(jié)分析。

(3)clocksource初始化

???????? 初始化函數(shù)源碼如下:

static void __inithi3536_clocksource_init(void __iomem *base, const char *name)?????????????????????????????????????????????????????????????????

{????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???long rate = sp804_get_clock_rate(name); ?//獲取定時器時鐘62.5MHz? ????

???struct clocksource *clksrc = &hi3536_clocksource.clksrc;???

???

???if (rate < 0)????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???????return;??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???? ???

???clksrc->name?? = name;? //name=timer0 ???????

???clksrc->rating = 200; //時鐘源精度值????????????

???clksrc->read?? =hi3536_clocksource_read;? ?//獲取計數(shù)值,系統(tǒng)主要調用該接口轉化為系統(tǒng)時間? ???????

???clksrc->mask?? =CLOCKSOURCE_MASK(32),? //計數(shù)值32位

???clksrc->flags? = CLOCK_SOURCE_IS_CONTINUOUS,?//持續(xù)的時鐘源

???clksrc->resume = hi3536_clocksource_resume,? ????

???hi3536_clocksource.base = base;? ?????

???hi3536_clocksource_start(base);? //初始化寄存器

??? clocksource_register_hz(clksrc, rate); ?//計算出mult和shift,為系統(tǒng)選擇更好的時鐘源??

???setup_sched_clock(hi3536_sched_clock_read, 32, rate); //通用sched_clock模塊,這個模塊主要是提供一個sched_clock的接口函數(shù),獲取當前時間點和系統(tǒng)啟動之間的納秒值。

}

(4)per CPU定時器初始化

???????? 每CPU定時器初始化函數(shù):

static void __inithi3536_local_timer_init(void)

{??

? ??unsigned int cpu = 0;

???unsigned int ncores = num_possible_cpus(); //獲取CPU個數(shù)

? ? local_timer_rate = sp804_get_clock_rate("sp804"); //獲取定時器時鐘

? ? for (cpu = 0; cpu < ncores; cpu++) { //為每個CPU分配各自的定時器

???????struct hi_timer_t *cpu_timer = GET_SMP_TIMER(cpu);

? ? ? ? cpu_timer->irq.handler = sp804_timer_isr; //中斷處理函數(shù)

???????cpu_timer->irq.dev_id = (void *)cpu_timer; //定時器分別是timer0~3

???????setup_irq(cpu_timer->irq.irq, &cpu_timer->irq); //注冊中斷號

???????disable_irq(cpu_timer->irq.irq); //關閉中斷

?? ?}

local_timer_register(&hi3536_timer_tick_ops);//注冊定時器操作函數(shù)

?/* 以上只是為每個CPU分配了定時器,并沒有注冊每個cpu的clockevent,啟動CPU(CPU0)在稍后注冊,而其他次CPU則直到kernel_init啟動它們后才會注冊 */

}

//定時器注冊clockevent操作函數(shù)

static struct local_timer_opshi3536_timer_tick_ops __cpuinitdata = {

???.setup? =hi3536_local_timer_setup,

???.stop?? = hi3536_local_timer_stop,

};

static int __cpuinithi3536_local_timer_setup(struct clock_event_device *evt)

{

???unsigned int cpu = smp_processor_id();

???struct hi_timer_t *timer = GET_SMP_TIMER(cpu);

???struct irqaction *irq = &timer->irq;

? ? evt->name = timer->name;

???evt->irq? = irq->irq;

???evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT

????????????????????????? |CLOCK_EVT_FEAT_C3STOP;

? ? evt->set_mode = sp804_set_mode;

???evt->set_next_event = sp804_set_next_event;

???evt->rating = 350;

? ? timer->priv = (void *)evt;

? ? clockevents_config_and_register(evt, local_timer_rate, 0xf, 0xffffffff);//注冊clockevent

???irq_set_affinity(evt->irq, evt->cpumask);

???enable_irq(evt->irq);

? ? return 0;

}

//中斷處理函數(shù)

static irqreturn_t sp804_timer_isr(int irq,void *dev_id)

{

???struct hi_timer_t *timer = (struct hi_timer_t *)dev_id;

???unsigned int clkevt_base = timer->addr;

???struct clock_event_device *evt

???????= (struct clock_event_device *)timer->priv;

? ? /* clear the interrupt */

???writel(1, IOMEM(clkevt_base + TIMER_INTCLR));

? ? evt->event_handler(evt);? //periodic 和 one-shot模式處理函數(shù)不一樣,見4小節(jié)細節(jié)。

? ? return IRQ_HANDLED;

}

(5)per CPU注冊clockevent

???????? 上面只是初始化為每個CPU分配一個定時器,并沒有將定時器注冊到clockevent。而注冊則是在各個CPU啟動后,主CPU和次CPU注冊是分開的,具體如下:

A.主CPU(cpu0)

???????? 注冊過程:kernel_init—> kernel_init_freeable —> smp_prepare_cpus —> percpu_timer_setup :

static void __cpuinitpercpu_timer_setup(void)

{

???unsigned int cpu = smp_processor_id();

???struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu);

? ?evt->cpumask = cpumask_of(cpu);

? ?if (!lt_ops || lt_ops->setup(evt)) //這里的setup就是前面初始化的:hi3536_local_timer_setup,將CPU0的timer注冊到clockevent設備。

???????broadcast_timer_setup(evt);

}

B.次CPU(cpu1~cpu3)

???????? 注冊過程:kernel_init—> kernel_init_freeable —> smp_init:

void __init smp_init(void)

{??

?? …

???/* FIXME: This should be done in userspace --RR */

???for_each_present_cpu(cpu) {

???????if (num_online_cpus() >= setup_max_cpus)

???????????break;

???? ???if (!cpu_online(cpu)) //啟動所有未啟動的CPU

???????????cpu_up(cpu);

??? }

}

???????? cpu_up—> _cpu_up —> __cpu_up —> boot_secondary —> smp_ops.smp_boot_secondary—> hi3536_boot_secondary —> hi3536_secondary_startup(匯編) —> secondary_startup(匯編) —> __secondary_switched(匯編) —> secondary_start_kernel —>percpu_timer_setup該函數(shù)就是上面主CPU注冊本地定時器的函數(shù)。

(6)clockevent初始化

???????? 這里主要是注冊clockevent的global timer,在periodic模式下不起作用,在one-shot模式下才會作用。注冊過程過程如下:

???????? sp804_clockevents_init—> __sp804_clockevents_init —> clockevents_config_and_register —> clockevents_register_device—> clockevents_do_notify —> tick_notify(tick_init初始化的通知分發(fā)器) —>tick_check_new_device

?

4、periodic和one-shot模式

???????? 針對clockevent有periodic和one-shot兩種模式,主要的不同點:event_handler事件處理函數(shù)不同,細節(jié)見下列分析。

(1)periodic模式

???????? 根據(jù)上一節(jié)的注冊初始化,其先后順序如下:timer1(clockeventglobal timer) —> timer4(clockevent cpu0 local timer) —> time5~7(clockeventcpu1~3 local timer)。

???????? 注冊timer1時運行在CPU0上,次CPU1~3還沒有啟動,這時tick_check_new_device接口會把timer1注冊為CPU0的local timer,此刻timer1的event_handler= tick_handle_periodic。當CPU0調用smp_prepare_cpus注冊自己的local timer時會用timer4替換timer1,此刻timer4的event_handler= tick_handle_periodic,而timer1的event_handler在tick_setup_device中被修改為clockevents_handle_noop(空實現(xiàn)),并在接口tick_check_broadcast_device中被注冊為globaltimer設備(在周期模式下不起任何作用)。

???????? CPU1~3啟動時調用secondary_start_kernel注冊local timer時只有一個定時器分別是timer5~7,它們的event_handler= tick_handle_periodic。

???????? clockevents_handle_noop:空實現(xiàn)

???????? tick_handle_periodic主要完成如下工作:

A.如果是CPU0,則調用do_timer完成tick更新和墻上時鐘更新

B.update_process_times完成:啟動本地軟中斷;更新本CPU的運行隊列;調度任務;SMP下觸發(fā)運行隊列均衡。

?

(2)one-shot模式

???????? 設備啟動時一開始是periodic模式,過程和上面一模一樣。各個CPU觸發(fā)本地軟中斷后發(fā)生切換,run_timer_softirq—> hrtimer_run_pending —> tick_check_oneshot_change —> tick_nohz_switch_to_nohz—> tick_switch_to_oneshot:

將CPU0~3的event_handler切換為:tick_nohz_handler

將timer1的event_handler切換為:tick_handle_oneshot_broadcast

切換只會發(fā)生一次,切換好后hrtimer_run_pending接口就會直接返回。

tick_nohz_handler:

???????? 和tick_handle_periodic做的事情大致一樣。

tick_handle_oneshot_broadcast主要完成:喚醒各個CPU,補償tick的偏差。

???????? one-shot模式下event_handler處理函數(shù)每次都要重新設置next_event。該模式的好處就是可以節(jié)省CPU功耗。

5、系統(tǒng)調用例子講解

(1)gettimeofday

???????? 該函數(shù)主要用于獲取微妙級別的時間。其對應的系統(tǒng)調用為sys_gettimeofday,實際起作用的是do_gettimeofday:

void do_gettimeofday(struct timeval*tv)?????????????????????????????????????????????????????????????????????????????????????????????????????????

{????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???struct timespec now;?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

? ?getnstimeofday(&now); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

???tv->tv_sec = now.tv_sec;?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???tv->tv_usec = now.tv_nsec/1000;??????????????????????????????????????????????????????????????????????????????????????????????????????????????

}

int __getnstimeofday(structtimespec *ts) ????????????????????????????????????????????????????????????????????????????????????????????????????????

{??

???struct timekeeper *tk = &timekeeper;

???unsigned long seq;???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

???s64 nsecs = 0;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??

???do {

???????seq = read_seqcount_begin(&timekeeper_seq);? //使用順序鎖進行數(shù)據(jù)同步訪問

? ts->tv_sec = tk->xtime_sec;??? //獲取秒數(shù),xtime_sec更新由update_all_time進行??????????????????????????????????????????????????????????????????????????????????????????????????????????????

???????nsecs = timekeeping_get_ns(tk); //調用clocksource的read函數(shù)(即上面的hi3536_clocksource_read),將計數(shù)轉化為相應的ns數(shù)

??? }while (read_seqcount_retry(&timekeeper_seq, seq));

?

???ts->tv_nsec = 0;

???timespec_add_ns(ts, nsecs); //調整ns數(shù),傳遞給用戶

??????…..

}

?

(2)nanosleep

???????? 這是一個ns級別的睡眠函數(shù),對應的系統(tǒng)調用sys_nanosleep(kernel/hrtimer.c),實際起作用的是hrtimer_nanosleep—> do_nanosleep:

static int __sched do_nanosleep(structhrtimer_sleeper *t, enum hrtimer_mode mode)

{??

???????? //初始化一個新的hrtimer

???hrtimer_init_sleeper(t, current);

???????

???do {

???????set_current_state(TASK_INTERRUPTIBLE); //設置當前任務睡眠

???????hrtimer_start_expires(&t->timer, mode);//將新的hrtimer加入到timer_list

???????if (!hrtimer_active(&t->timer)) //如果沒有激活hrtimer則直接退出

???????????t->task = NULL;

???????????

???????if (likely(t->task)) //如果激活了,就開始調度

???????????freezable_schedule();

???

???????hrtimer_cancel(&t->timer); //運行到這里,說明定時器到期,那么取消定時器。

???????mode = HRTIMER_MODE_ABS;

???

??? }while (t->task && !signal_pending(current));

?

???__set_current_state(TASK_RUNNING); //設置進程狀態(tài)可執(zhí)行

?

???return t->task == NULL;

}

???????? 不管內核有沒有配置HIGH_RES_TIMERS,內核都編譯httimer.c接口。如果沒有配置hrtimer則使用低精度的tick方案,這時定時器是相當不準確;如果進行了配置,則使用高精度方案。兩種方案下,定時器中斷處理方法不同:

低精度下的hrtimer:

???????? update_process_times—> run_local_timers —> hrtimer_run_queues

高精度下的hrtimer:

run_hrtimer_softirq—>hrtimer_peek_ahead_timers —> __hrtimer_peek_ahead_timers —> hrtimer_interrupt

?

?

附錄A

參考資料

http://www.wowotech.net/timer_subsystem/time-subsyste-architecture.html

http://www.wowotech.net/timer_subsystem/timer_subsystem_userspace.html

?

?

?

總結

以上是生活随笔為你收集整理的linux内核之时间子系统的全部內容,希望文章能夠幫你解決所遇到的問題。

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