小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

hrtimer clockevent Timekeeping...

 黃浦江中的一條魚 2009-07-15
hrtimer + clockevent + Timekeeping
    kernel-2.6.22中的arm arch加入了對dynticks, clocksource/event支持. imx31的BSP在clock這里有一些改動. 找了些kernel clock及timer子系統(tǒng)近來的變化, 總結(jié)一下.
    一般來說Soft-Timer (timer wheel / hrtimer) 都是由Hardware-Timer(時鐘中斷之類)以及相關(guān)的clock source(e.g GPT in Soc)驅(qū)動, 所以我打算先從clock這層開始介紹, 接著是soft-timer, kernel timekeeping, 最后來看一些應(yīng)用.

Clock Source

clock source定義了一個clock device的基本屬性及行為, 這些clock device一般都有計數(shù), 定時, 產(chǎn)生中斷能力, 比如GPT. 結(jié)構(gòu)定義如下:

struct clocksource {
    char *name;
    struct list_head list;
    int rating;
    cycle_t (*read)(void);
    cycle_t mask;
    u32 mult; /* cycle -> xtime interval, maybe two clock cycle trigger one interrupt (one xtime interval) */
    u32 shift;
    unsigned long flags;
    cycle_t (*vread)(void);
    void (*resume)(void);

    /* timekeeping specific data, ignore */
    cycle_t cycle_interval; /* just the rate of GPT count per OS HZ */
    u64    xtime_interval; /* xtime_interval = cycle_interval * mult. */
    cycle_t cycle_last ____cacheline_aligned_in_smp; /* last cycle in rate count */
    u64 xtime_nsec; /* cycle count, remain from xtime.tv_nsec
                     * now nsec rate count offset = xtime_nsec +                              * xtime.tv_nsec << shift */

    s64 error;
};


最重要的成員是read(), cycle_last和cycle_interval. 分別定義了讀取clock device count 寄存器當前計數(shù)值接口, 保存上一次周期計數(shù)值和每個tick周期間隔值. 這個結(jié)構(gòu)內(nèi)的值, 無論是cycle_t, 還是u64類型(實際cycle_t就是u64)都是計數(shù)值(cycle), 而不是nsec, sec和jiffies. read()是整個kernel讀取精確的單調(diào)時間計數(shù)的接口, kernel會用它來計算其他時間, 比如:jiffies, xtime.
clocksource的引入, 解決了之前kernel各個arch都有自己的clock device的管理方式, 基本都隱藏在MSL層, kernel core 及driver很難訪問的問題. 它導(dǎo)出了以下接口:
1) clocksource_register() 注冊clocksource
2) clocksource_get_next() 獲取當前clocksource設(shè)備
3) clocksource_read() 讀取clock, 實際跑到clocksource->read()
當driver處理的時間精度比較高的時, 可以通過上面的接口, 直接拿clock device來讀.
當然目前ticker時鐘中斷源也會以clocksource的形式存在.

Clock Event

Clock event的主要作用是分發(fā)clock事件及設(shè)置下一次觸發(fā)條件. 在沒有clock event之前, 時鐘中斷都是周期性地產(chǎn)生, 也就是熟知的jiffies和HZ.
Clock Event device主要的結(jié)構(gòu):

struct clock_event_device {
    const char        *name;
    unsigned int        features;
    unsigned long        max_delta_ns;
    unsigned long        min_delta_ns;
    unsigned long        mult;
    int            shift;
    int            rating;
    int            irq;
    cpumask_t        cpumask;
    int            (*set_next_event)(unsigned long evt,
                         struct clock_event_device *);
    void            (*set_mode)(enum clock_event_mode mode,
                     struct clock_event_device *);
    void            (*event_handler)(struct clock_event_device *);
    void            (*broadcast)(cpumask_t mask);
    struct list_head    list;
    enum clock_event_mode    mode;
    ktime_t            next_event;
};

最重要的是set_next_event(), event_handler(). 前者是設(shè)置下一個clock事件的觸發(fā)條件, 一般就是往clock device里重設(shè)一下定時器. 后者是event handler, 事件處理函數(shù). 該處理函數(shù)會在時鐘中斷ISR里被調(diào)用. 如果這個clock用來做為ticker時鐘, 那么handler的執(zhí)行和之前kernel的時鐘中斷ISR基本相同, 類似timer_tick(). 事件處理函數(shù)可以在運行時動態(tài)替換, 這就給kernel一個改變整個時鐘中斷處理方式的機會, 也就給highres tick及dynamic tick一個動態(tài)掛載的機會. 目前kernel內(nèi)部有periodic/highres/dynamic tick三種時鐘中斷處理方式. 后面會介紹.

hrtimer & timer wheel

首先說一下timer wheel. 它就是kernel一直采用的基于jiffies的timer機制, 接口包括init_timer(), mod_timer(), del_timer()等, 很熟悉把.
hrtimer 的出現(xiàn), 并沒有拋棄老的timer wheel機制(也不太可能拋棄:)). hrtimer做為kernel里的timer定時器, 而timer wheel則主要用來做timeout定時器. 分工比較明確. hrtimers采用紅黑樹來組織timers, 而timer wheel采用鏈表和桶.
hrtimer精度由原來的timer wheel的jiffies提高到nanosecond. 主要用于向應(yīng)用層提供nanosleep, posix-timers和itimer接口, 當然驅(qū)動和其他子系統(tǒng)也會需要high resolution的timer.
kernel 里原先每秒周期性地產(chǎn)生HZ個ticker(中斷), 被在下一個過期的hrtimer的時間點上產(chǎn)生中斷代替. 也就是說時鐘中斷不再是周期性的, 而是由timer來驅(qū)動(靠clockevent的set_next_event接口設(shè)置下一個事件中斷), 只要沒有hrtimer加載, 就沒有中斷. 但是為了保證系統(tǒng)時間(進程時間統(tǒng)計, jiffies的維護)更新, 每個tick_period(NSEC_PER_SEC/HZ, 再次強調(diào)hrtimer精度是nsec)都會有一個叫做tick_sched_timer的hrtimer加載.
接下來對比一下, hrtimer引入之前及之后, kernel里時鐘中斷的處理的不同. (這里都是基于arm arch的source去分析)

1)no hrtimer

kernel 起來, setup_arch()之后的time_init()會去初始化相應(yīng)machine結(jié)構(gòu)下的timer. 初始化timer函數(shù)都在各個machine的體系結(jié)構(gòu)代碼中, 初始化完硬件時鐘, 注冊中斷服務(wù)函數(shù), 使能時鐘中斷. 中斷服務(wù)程序會清中斷, 調(diào)用timer_tick(), 它執(zhí)行:
1. profile_tick(); /* kernel profile, 不是很了解 */
2. do_timer(1); /* 更新jiffies */
3. update_process_times(); /* 計算進程耗時, 喚起TIMER_SOFTIRQ(timer wheel), 重新計算調(diào)度時間片等等 */
最后中斷服務(wù)程序設(shè)置定時器, 使其在下一個tick產(chǎn)生中斷.

這樣的框架, 使得high-res的timer很難加入. 所有中斷處理code都在體系結(jié)構(gòu)代碼里被寫死, 并且代碼重用率很低, 畢竟大多的arch都會寫同樣的中斷處理函數(shù).

2)hrtimer

kernel 里有了clockevent/source的引入, 就把clocksource的中斷以一種事件的方式被抽象出來. 事件本身的處理交給event handler. handler可以在kernel里做替換從而改變時鐘中斷的行為. 時鐘中斷ISR會看上去象這樣:

static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
    /* clear timer interrupt flag */
    .....
    /* call clock event handler */
    arch_clockevent.event_handler(
&arch_clockevent);
    ....
    return IRQ_HANDLED;
}


event_handler 在注冊clockevent device時, 會被默認設(shè)置成tick_handle_periodic(). 所以kernel剛起來的時候, 時鐘處理機制仍然是periodic的, ticker中斷周期性的產(chǎn)生. tick_handle_periodic()會做和timer_tick差不多的事情, 然后調(diào)用clockevents_program_event() => arch_clockevent.set_next_event()去設(shè)置下一個周期的定時器. tick-common.c里把原來kernel時鐘的處理方式在clockevent框架下實現(xiàn)了, 這就是periodic tick的時鐘機制.

hres tick機制在第一個TIMER SOFTIRQ里會替換掉periodic tick, 當然要符合一定條件, 比如command line里沒有把hres(highres=off)禁止掉, clocksource/event支持hres和oneshot的能力. 這里的切換做的比較ugly, 作者的comments也提到了, 每次timer softirq被調(diào)度, 都要調(diào)用hrtimer_run_queues()檢查一遍hres是否active, 如果能在timer_init()里就把clocksource/event的條件check過, 直接切換到hres就最好了, 不知道是不是有什么限制條件. TIMER SOFTIRQ代碼如下:

static void run_timer_softirq(struct softirq_action *h)
{
    tvec_base_t *base = __get_cpu_var(tvec_bases);

    hrtimer_run_queues(); /* 有機會就切換到hres或者nohz */

    if (time_after_eq(jiffies, base->timer_jiffies))
        __run_timers(base); /* timer wheel */
}


切換的過程比較簡單, 用hrtimer_interrupt()替換當前clockevent hander, 加載一個hrtimer: tick_sched_timer在下一個tick_period過期, retrigger下一次事件.
hrtimer_interrupt() 將過期的hrtimers從紅黑樹上摘下來, 放到相應(yīng)clock_base->cpu_base->cb_pending列表里, 這些過期timers會在HRTIMER_SOFTIRQ里執(zhí)行. 然后根據(jù)剩余的最早過期的timer來retrigger下一個event, 再調(diào)度HRTIMER_SOFTIRQ. hrtimer softirq執(zhí)行那些再cb_pending上的過期定時器函數(shù). tick_sched_timer這個hrtimer在每個tick_period都會過期, 執(zhí)行過程和timer_tick()差不多, 只是在最后調(diào)用hrtimer_forward將自己加載到下一個周期里去, 保證每個tick_period都能正確更新kernel內(nèi)部時間統(tǒng)計.

Timekeeping

Timekeeping子系統(tǒng)負責(zé)更新xtime, 調(diào)整誤差, 及提供get/settimeofday接口. 為了便于理解, 首先介紹一些概念:

Times in Kernel

kernel的time基本類型:
1) system time
A monotonically increasing value that represents the amount of time the system has been running. 單調(diào)增長的系統(tǒng)運行時間, 可以通過time source, xtimewall_to_monotonic計算出來.
2) wall time
A value representing the the human time of day, as seen on a wrist-watch. Realtime時間: xtime.
3) time source
A representation of a free running counter running at a known frequency, usually in hardware, e.g GPT. 可以通過clocksource->read()得到counter值
4) tick
A periodic interrupt generated by a hardware-timer, typically with a fixed interval
defined by HZ: jiffies

這些time之間互相關(guān)聯(lián), 互相可以轉(zhuǎn)換.
system_time = xtime + cyc2ns(clock->read() - clock->cycle_last) + wall_to_monotonic;
real_time = xtime + cyc2ns(clock->read() - clock->cycle_last)
也就是說real time是從1970年開始到現(xiàn)在的nanosecond, 而system time是系統(tǒng)啟動到現(xiàn)在的nanosecond.
這兩個是最重要的時間, 由此hrtimer可以基于這兩個time來設(shè)置過期時間. 所以引入兩個clock base.

Clock Base

CLOCK_REALTIME: base在實際的wall time
CLOCK_MONOTONIC: base在系統(tǒng)運行system time
hrtimer可以選擇其中之一, 來設(shè)置expire time, 可以是實際的時間, 也可以是相對系統(tǒng)的時間.
他們提供get_time()接口:
CLOCK_REALTIME 調(diào)用ktime_get_real()來獲得真實時間, 該函數(shù)用上面提到的等式計算出realtime.
CLOCK_MONOTONIC 調(diào)用ktime_get(), 用system_time的等式獲得monotonic time.


timekeeping提供兩個接口do_gettimeofday()/do_settimeofday(), 都是針對realtime操作. 用戶空間對gettimeofday的syscall也會最終跑到這里來.
do_gettimeofday()會調(diào)用__get_realtime_clock_ts()獲得時間, 然后轉(zhuǎn)成timeval.
do_settimeofday(), 將用戶設(shè)置的時間更新到xtime, 重新計算xtime到monotonic的轉(zhuǎn)換值, 最后通知hrtimers子系統(tǒng)時間變更.

int do_settimeofday(struct timespec *tv)
{
    unsigned long flags;
    time_t wtm_sec, sec = tv->tv_sec;
    long wtm_nsec, nsec = tv->tv_nsec;

    if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
        return -EINVAL;

    write_seqlock_irqsave(&xtime_lock, flags);

    nsec -= __get_nsec_offset();

    wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
    wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);

    set_normalized_timespec(&xtime, sec, nsec); /* 重新計算xtime: 用戶設(shè)置的時間減去上一個周期到現(xiàn)在的nsec */
    set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); /* 重新調(diào)整wall_to_monotonic */

    clock->error = 0;
    ntp_clear();

    update_vsyscall(&xtime, clock);
    write_sequnlock_irqrestore(&xtime_lock, flags);
    /* signal hrtimers about time change */
    clock_was_set();

    return 0;
}



Userspace Application

hrtimer的引入, 對用戶最有用的接口如下:

Clock API
clock_gettime(clockid_t, struct timespec *)
獲取對應(yīng)clock的時間
clock_settime(clockid_t, const struct timespec *)
設(shè)置對應(yīng)clock時間
clock_nanosleep(clockid_t, int, const struct timespec *, struct timespec *)
進程nano sleep
clock_getres(clockid_t, struct timespec *)
獲取時間精度, 一般是nanosec

clockid_t 定義了四種clock:
CLOCK_REALTIME
System-wide realtime clock. Setting this clock requires appropriate privileges.
CLOCK_MONOTONIC
Clock that cannot be set and represents monotonic time since some unspecified starting point.
CLOCK_PROCESS_CPUTIME_ID
High-resolution per-process timer from the CPU.
CLOCK_THREAD_CPUTIME_ID
Thread-specific CPU-time clock.
前兩者前面提到了, 后兩個是和進程/線程統(tǒng)計時間有關(guān)系, 還沒有仔細研究過, 是utime/stime之類的時間. 應(yīng)用層可以利用這四種clock, 提高靈活性及精度.

Timer API

Timer 可以建立進程定時器,單次或者周期性定時。

int timer_create(clockid_t clockid, struct sigevent *restrict evp, timer_t *restrict timerid);
創(chuàng)建定時器。
clockid 指定在哪個clock base下創(chuàng)建定時器。
evp (sigevent) 可以指定定時器到期后內(nèi)核發(fā)送哪個信號給進程,以及信號所帶參數(shù);默認為SIGALRM。
timerid 返回所建timer的id號。
在 signal處理函數(shù)里,可以通過siginfo_t.si_timerid 獲得當前的信號是由哪個timer過期觸發(fā)的。 試驗了一下,最多可創(chuàng)建的timer數(shù)目和ulimit里的pending signals的有關(guān)系,不能超過pending signals的數(shù)量。

int timer_gettime(timer_t timerid, struct itimerspec *value); 
獲得timer的下次過期的時間。

int timer_settime(timer_t timerid, int flags, const struct itimerspec *restrict value, struct itimerspec *restrict ovalue);

設(shè)置定時器的過期時間及間隔周期。

int timer_delete(timer_t timerid);
刪除定時器。

這些系統(tǒng)調(diào)用都會建立一個posix_timer的hrtimer,在過期的時候發(fā)送信號給進程。

總結(jié)

hrtimer 及clockevent/source的引入對于kernel的實時性的提高有很大貢獻,也將clock的處理從體系結(jié)構(gòu)的代碼中抽象了出來,增強了代碼 的可重用性。并且對于posix的time/timer標準有了強有力的支持,提高了用戶空間的應(yīng)用程序的時間處理精度及靈活性。如果應(yīng)用層在使用這些 syscall時有任何不解之處,直接看看hrtimer的code,對于處理問題,理解OS的行為都有很大幫助。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多