淩晨1點正是看代碼的好時間,自勉。
核心代碼閱讀(17) 中分析了如何借助softirq機制實作Top Half和Bottom Half的。時鐘中斷不僅是核心的心跳,而且很好的诠釋了softirq機制是如何把始終中斷分成上下兩個部分。
時鐘中斷機制
時鐘中斷很重要,是核心的心跳。
進入核心态的途徑:中斷發生,異常,系統調用。
程序的排程隻能在核心态運作,如果使用者态的程序進入了死循環,而此時一直都沒有中斷,異常,系統調用,CPU豈不是一直進入不了核心執行?
時鐘中斷就是惟一可以預測的中斷源。
時鐘中斷的初始化
asmlinkage void __init start_kernel(void)
{
char * command_line;
unsigned long mempages;
extern char saved_command_line[];
lock_kernel();
printk(linux_banner);
setup_arch(&command_line);
printk("Kernel command line: %s\n", saved_command_line);
parse_options(command_line);
trap_init();
init_IRQ();
sched_init();
time_init();
softirq_init();
}
1) time_init 就是時鐘中斷初始化函數。
time_init
void __init time_init(void)
{
extern int x86_udelay_tsc;
xtime.tv_sec = get_cmos_time();
xtime.tv_usec = 0;
dodgy_tsc();
if (cpu_has_tsc) {
unsigned long tsc_quotient = calibrate_tsc();
if (tsc_quotient) {
fast_gettimeoffset_quotient = tsc_quotient;
use_tsc = 1;
x86_udelay_tsc = 1;
#ifndef do_gettimeoffset
do_gettimeoffset = do_fast_gettimeoffset;
#endif
do_get_fast_time = do_gettimeofday;
/* report CPU clock rate in Hz.
* The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
* clock/second. Our precision is about 100 ppm.
*/
{ unsigned long eax=0, edx=1000;
__asm__("divl %2"
:"=a" (cpu_khz), "=d" (edx)
:"r" (tsc_quotient),
"0" (eax), "1" (edx));
printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000);
}
}
}
setup_irq(0, &irq0);
}
1) xtime.tv_sec = get_cmos_time();
xtime.tv_usec = 0;
系統時鐘涉及到兩個全局變量:
a) struct time_val xtime;
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
xtime紀錄了從曆史上某個時刻開始的絕對時間,數值來自一個CMOS晶片,精确到秒。get_cmos_time擷取秒并初始化全局變量xtime。
b) jiffies
記錄了從開機以來時鐘中斷發生的次數。
2) setup_irq(0, &irq0);
設定中斷向量号0的服務子程式為irq0.
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
是以,時鐘中斷的服務程式是 timer_interrupt。
時鐘中斷服務子程式timer_interrupt
static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
int count;
write_lock(&xtime_lock);
do_timer_interrupt(irq, NULL, regs);
write_unlock(&xtime_lock);
}
1) timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
調用中斷服務子程式的第一個參數是裝置的id,第二個參數就是通過SAVE_ALL或者 jmp error_code 構造的寄存器現場。
2) write_lock(&xtime_lock);
全局變量xtime需要上鎖。
do_timer_interrupt
static inline void do_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
do_timer(regs);
if (!user_mode(regs))
x86_do_profile(regs->eip);
}
1) x86_do_profile(regs->eip);
統計資訊。
時鐘中斷上半段do_timer
void do_timer(struct pt_regs *regs)
{
(*(unsigned long *)&jiffies)++;
mark_bh(TIMER_BH);
if (TQ_ACTIVE(tq_timer))
mark_bh(TQUEUE_BH);
}
1) (*(unsigned long *)&jiffies)++;
jiffies加1.
2) mark_bh(TIMER_BH);
到此時鐘中斷‘上半部’執行完畢,激活時鐘中斷的Bottom Half。
時鐘中斷下半段 timer_bh
在sched_init中初始化了始終中斷的下半段:
init_bh(TIMER_BH, timer_bh);
init_bh(TQUEUE_BH, tqueue_bh);
init_bh(IMMEDIATE_BH, immediate_bh);
void timer_bh(void)
{
update_times();
run_timer_list();
}
update_times
static inline void update_times(void)
{
unsigned long ticks;
write_lock_irq(&xtime_lock);
ticks = jiffies - wall_jiffies;
if (ticks) {
wall_jiffies += ticks;
update_wall_time(ticks);
}
write_unlock_irq(&xtime_lock);
}
1) ticks = jiffies - wall_jiffies;
wall_jiffies是牆上時間已經更新到了哪裡了。
內插補點ticks是需要更新的心跳數。
2) update_wall_time(ticks);
校準更新時鐘xtime
3) calc_load(ticks);
計算load
update_wall_time
static void update_wall_time(unsigned long ticks)
{
do {
ticks--;
update_wall_time_one_tick();
} while (ticks);
if (xtime.tv_usec >= 1000000) {
xtime.tv_usec -= 1000000;
xtime.tv_sec++;
second_overflow();
}
}
1) update_wall_time_one_tick
校準更新時鐘xtime
calc_load
unsigned long avenrun[3];
static inline void calc_load(unsigned long ticks)
{
unsigned long active_tasks;
static int count = LOAD_FREQ;
count -= ticks;
if (count < 0) {
count += LOAD_FREQ;
active_tasks = count_active_tasks();
CALC_LOAD(avenrun[0], EXP_1, active_tasks);
CALC_LOAD(avenrun[1], EXP_5, active_tasks);
CALC_LOAD(avenrun[2], EXP_15, active_tasks);
}
}
rum_timer_list
static inline void run_timer_list(void)
{
spin_lock_irq(&timerlist_lock);
while ((long)(jiffies - timer_jiffies) >= 0) {
struct list_head *head, *curr;
if (!tv1.index) {
int n = 1;
do {
cascade_timers(tvecs[n]);
} while (tvecs[n]->index == 1 && ++n < NOOF_TVECS);
}
repeat:
head = tv1.vec + tv1.index;
curr = head->next;
if (curr != head) {
struct timer_list *timer;
void (*fn)(unsigned long);
unsigned long data;
timer = list_entry(curr, struct timer_list, list);
fn = timer->function;
data= timer->data;
detach_timer(timer);
timer->list.next = timer->list.prev = NULL;
timer_enter(timer);
spin_unlock_irq(&timerlist_lock);
fn(data);
spin_lock_irq(&timerlist_lock);
timer_exit();
goto repeat;
}
++timer_jiffies;
tv1.index = (tv1.index + 1) & TVR_MASK;
}
spin_unlock_irq(&timerlist_lock);
}
1) run_timer_list(void)
完成系統中的定時器任務。
2) struct timer_list {
struct list_head list;
unsigned long expires;
unsigned long data;
void (*function)(unsigned long);
};
3) ++timer_jiffies;
一個tick最多執行一個timer。
4) spin_unlock_irq(&timerlist_lock);
fn(data);
spin_lock_irq(&timerlist_lock);
執行一個timer過程解鎖timerlist_lock。