linux核心定時器的實現

2021-08-30 07:06:21 字數 3699 閱讀 7529

由於linux還不是乙個實時的作業系統,因此如果需要更高精度,或者更精確的定時的話,可能就需要打一些實時的補丁,或者用商用版的實時linux,.

這裡內的定時器最小間隔也就是1個tick.

這裡還有乙個要注意的,我這裡的分析並沒有分析核心新的hrt 定時器.這個定時器是monta vista加入到核心的乙個高精度的定時器的實現.

先來看幾個相關的資料結構.

///這個是乙個最主要的資料結構,表示乙個完整的定時器級聯表

struct tvec_base  ____cacheline_aligned;

下面來看tvec和tvec_root的結構:

struct tvec ;

struct tvec_root ;

可以看到這兩個結構也就是hash鍊錶.每次通過超時jiffies來計算slot,然後插入到鍊錶.這裡鍊錶是fifo的.這裡除了tv5外其他幾個都是簡單的與tvr_mask按位與計算.

struct timer_list ;

///定義了乙個per cpu變數.這裡要知道定時器的註冊和觸發執行一定是在相同的cpu上的.

struct tvec_base boot_tvec_bases;

static define_per_cpu(struct tvec_base *, tvec_bases) = &boot_tvec_bases;

核心註冊定時器最終都會通過呼叫internal_add_timer來實現.具體的工作方式是這樣的:

1 如果定時器在接下來的0~255個jiffies中到期,則將定時器新增到tv1.

2 如果定時器是在接下來的256*64個jiffies中到期,則將定時器新增到tv2.

3 如果定時器是在接下來的256*64*64個jiffies中到期,則將定時器新增到tv3.

4 如果定時器是在接下來的256*64*64*64個jiffies中到期,則將定時器新增到tv4.

5 如果更大的超時,則利用0xffffffff來計算hash,然後插入到tv5(這個只會出現在64的系統).

看下面的圖就能比較清晰了:

[img]

接下來看原始碼:

這裡要知道核心中的軟定時器是用軟中斷來實現的,軟中斷的註冊以及實現可以看我前面的blog,這裡就不介紹了.我們來看timer模組的初始化:

void __init init_timers(void)

ok,接下來我們就來看timer_cpu_notify這個函式,其實這個函式還是定時器註冊的cpu的notify chain的action:

static struct notifier_block __cpuinitdata timers_nb = ;

static int __cpuinit timer_cpu_notify(struct notifier_block *self,

unsigned long action, void *hcpu)

其他的部分我們忽略,我們就發現定時器模組會呼叫init_timers_cpu來初始化.我們來分析這個函式.

這個函式最主要的功能就是初始化boot_tvec_bases,也就是全域性的定時器表:

static int __cpuinit init_timers_cpu(int cpu)

///由於在per cpu的變數中型別為tvec_bases的,只有boot_tvec_bases,因此,也就是將base這個指標付給boot_tvec_bases.

per_cpu(tvec_bases, cpu) = base;

} else

tvec_base_done[cpu] = 1;

} else

///開始初始化

spin_lock_init(&base->lock);

///開始初始化5個定時器表

for (j = 0; j < tvn_size; j++)

for (j = 0; j < tvr_size; j++)

init_list_head(base->tv1.vec + j);

///預設值為初始化時的jiffes

base->timer_jiffies = jiffies;

return 0;

}

通過上面的定時器初始化函式我們知道定時器軟中斷所對應的action是run_timer_softirq,也就是當時鐘中斷到來,軟中斷啟動時,就會呼叫這個函式,因此我們來看這個函式:

這個函式功能很簡單,它的最關鍵就是呼叫__run_timers,這個函式才是真正處理定時器的函式.

static void run_timer_softirq(struct softirq_action *h)

__run_timers這個函式的主要功能是執行所有超時的定時器:

1

static inline void __run_timers(struct tvec_base *base)

spin_lock_irq(&base->lock);}}

///修改base->running_timer為空

set_running_timer(base, null);

spin_unlock_irq(&base->lock);

}

ok我們接下來來看下定時器超時的機制,關鍵在這段**:

if (!index &&

(!cascade(base, &base->tv2, index(0))) &&

(!cascade(base, &base->tv3, index(1))) &&

!cascade(base, &base->tv4, index(2)))

cascade(base, &base->tv5, index(3));

index為0就說明當前要處理的定時器不在base->tv1 中.因此我們需要cascade來進行調解.

///得到在n級(也就是tv2,tv3...)的定時器表中的slot.這裡可以對照我們前面的internal_add_timer加入定時器的情況.

#define index(n) ((base->timer_jiffies >> (tvr_bits + (n) * tvn_bits)) & tvn_mask)

static int cascade(struct tvec_base *base, struct tvec *tv, int index)

///然後返回index,這裡可以看到如果index為空則說明這個級別的定時器也已經都處理過了,因此我們需要再處理下乙個級別.

return index;

}

可以看到定時器處理始終都是在處理tv1,如果tv1已經處理完了,則將tv2新增到tv1,以此類推.

而定時器軟中斷如何觸發呢,是用update_process_times來觸發的,這個函式比較簡單,主要是呼叫run_local_timers來觸發軟中斷:

void run_local_timers(void)

linux 核心 核心定時器

一.時鐘中斷概念 1.時鐘中斷由系統的定時硬體以週期性的時間間隔產生,這個間隔 即頻率 由核心根據hz來確定,hz是乙個與體系結構無關的常數,可配置 50 1200 在x86平台上預設值是1000 2.每當時鐘中斷發生的時候,全域性變數jiffies unsigned long 就加1,所以jiff...

linux核心定時器

度量時間差 時鐘中斷由系統的定時硬體以週期性的時間間隔產生,這個間隔 頻率 由核心根據hz來確定,hz是乙個與體系結構無關的常數,可配置 50 1200 在x86平台,預設值為1000.每秒鐘產生1000次時鐘中斷 每當時鐘中斷發生時,全域性變數jiffies就加1,因此其記錄了自linux啟動後時...

Linux 核心定時器

include include include include struct timer list my timer void func unsigned long data printk time out n data ld,pid ld n data,current pid mod timer ...