?? 中斷.txt
字號:
"call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
"addl $8,%esp\n\t" \
"cli\n\t" \
UNBLK_##chip(mask) \
"decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
"incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
"jmp ret_from_sys_call\n");
其中nr為中斷請求類型,取值0~15。在irq.c中通過語句BUILD_TIMER_IRQ(first, 0,
0x01)調(diào)用該宏,在執(zhí)行宏的過程中處理時鐘中斷響應(yīng)程序do_irq()。
函數(shù)do_irq()的第一個參數(shù)是中斷請求隊列序號,時鐘中斷請求傳進(jìn)來的該參數(shù)是0。于是程序根據(jù)參數(shù)0找到請求隊列irq_action[0],逐個處理該隊列上handler所指的時鐘中斷請求的服務(wù)函數(shù)。由于已經(jīng)指定時鐘中斷請求的服務(wù)函數(shù)是timer_interrupt,在函數(shù)timer_interrupt中,立即調(diào)用do_timer()函數(shù)。
函數(shù)do_timer()把jiffies和lost_ticks加1,接著就執(zhí)行mark_bh(TIMER_BH)函數(shù),把bottom_half中時鐘隊列對應(yīng)的位置位,表示該隊列處于激活狀態(tài)。在做完這些動作后,程序從函數(shù)do_irq()中返回,繼續(xù)執(zhí)行以后的匯編代碼。于是,程序在執(zhí)行語句jmp
ret_from_sys_call后,跳到指定的位置處繼續(xù)執(zhí)行。
代碼段jmp ret_from_sys_call及其相關(guān)的代碼段如下:
ALIGN
.globl ret_from_sys_call
ret_from_sys_call:
cmpl $0,SYMBOL_NAME(intr_count)
jne 2f
9: movl SYMBOL_NAME(bh_mask),%eax
andl SYMBOL_NAME(bh_active),%eax
jne handle_bottom_half
#ifdef __SMP__
cmpb $(NO_PROC_ID), SYMBOL_NAME(saved_active_kernel_processor)
jne 2f
#endif
movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are
testl $(VM_MASK),%eax # different then
jne 1f
cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ?
je 2f
1: sti
orl $(IF_MASK),%eax # these just try to make sure
andl $~NT_MASK,%eax # the program doesn't do anything
movl %eax,EFLAGS(%esp) # stupid
cmpl $0,SYMBOL_NAME(need_resched)
jne reschedule
#ifdef __SMP__
GET_PROCESSOR_OFFSET(%eax)
movl SYMBOL_NAME(current_set)(,%eax), %eax
#else
movl SYMBOL_NAME(current_set),%eax
#endif
cmpl SYMBOL_NAME(task),%eax # task[0] cannot have signals
je 2f
movl blocked(%eax),%ecx
movl %ecx,%ebx # save blocked in %ebx for signal
handling
notl %ecx
andl signal(%eax),%ecx
jne signal_return
2: RESTORE_ALL
ALIGN
signal_return:
movl %esp,%ecx
pushl %ecx
testl $(VM_MASK),EFLAGS(%ecx)
jne v86_signal_return
pushl %ebx
call SYMBOL_NAME(do_signal)
popl %ebx
popl %ebx
RESTORE_ALL
ALIGN
v86_signal_return:
call SYMBOL_NAME(save_v86_state)
movl %eax,%esp
pushl %eax
pushl %ebx
call SYMBOL_NAME(do_signal)
popl %ebx
popl %ebx
RESTORE_ALL
handle_bottom_half:
incl SYMBOL_NAME(intr_count)
call SYMBOL_NAME(do_bottom_half)
decl SYMBOL_NAME(intr_count)
jmp 9f
ALIGN
reschedule:
pushl $ret_from_sys_call
jmp SYMBOL_NAME(schedule) # test
另外,一些與時鐘中斷及bottom half機(jī)制有關(guān)的數(shù)據(jù)結(jié)構(gòu)介紹如下:
#define HZ 100
unsigned long volatile jiffies=0;
系統(tǒng)每隔10ms自動把它加1,它是核心系統(tǒng)計時的單位。
enum {
TIMER_BH = 0,
CONSOLE_BH,
TQUEUE_BH,
DIGI_BH,
SERIAL_BH,
RISCOM8_BH,
SPECIALIX_BH,
BAYCOM_BH,
NET_BH,
IMMEDIATE_BH,
KEYBOARD_BH,
CYCLADES_BH,
CM206_BH
};
現(xiàn)在只定義了13個bottom half隊列,將來可擴(kuò)充到32個隊列。
unsigned long intr_count = 0;
相當(dāng)于信號量的作用。只有其等于0,才可以do_bottom_half。
int bh_mask_count[32];
用來計算bottom half隊列被屏蔽的次數(shù)。只有某隊列的bh_mask_count數(shù)為0,才能enable該隊列。
unsigned long bh_active = 0;
bh_active是32位長整數(shù),每一位表示一個bottom
half隊列,該位置1,表示該隊列處于激活狀態(tài),隨時準(zhǔn)備在CPU認(rèn)為合適的時候執(zhí)行該隊列的服務(wù),置0則相反。
unsigned long bh_mask = 0;
bh_mask也是32位長整數(shù),每一位對應(yīng)一個bottom half隊列,該位置1,表示該隊列可用,并把處理函數(shù)的入口地址賦給bh_base,置0則相反。
void (*bh_base[32])(void);
bottom half服務(wù)函數(shù)入口地址數(shù)組。定時器處理函數(shù)擁有最高的優(yōu)先級,它的地址存放在bh_base[0],總是最先執(zhí)行它所指向的函數(shù)。
我們注意到,在IRQ#_interrupt和fast_IRQ#_interrupt中斷函數(shù)處理返回前,都通過語句jmp
ret_from_sys_call,跳到系統(tǒng)調(diào)用的返回處(見irq.h),如果bottom half隊列不為空,則在那里做類似:
if (bh_active & bh_mask) {
intr_count = 1;
do_bottom_half();
intr_count = 0;
}(該判斷的匯編代碼見Entry.S)
的判斷,調(diào)用do_bottom_half()函數(shù)。
在CPU調(diào)度時,通過schedule函數(shù)執(zhí)行上述的判斷,再調(diào)用do_bottom_half()函數(shù)。
總而言之,在下列三種時機(jī):
CPU調(diào)度時
系統(tǒng)調(diào)用返回前
中斷處理返回前
都會作判斷調(diào)用do_bottom_half函數(shù)。Do_bottom_half函數(shù)依次掃描32個隊列,找出需要服務(wù)的隊列,執(zhí)行服務(wù)后把對應(yīng)該隊列的bh_active的相應(yīng)位置0。由于bh_active標(biāo)志中TIMER_BH對應(yīng)的bit為1,因而系統(tǒng)根據(jù)服務(wù)函數(shù)入口地址數(shù)組bh_base找到函數(shù)timer_bh()的入口地址,并馬上執(zhí)行該函數(shù),在函數(shù)timer_bh中,調(diào)用函數(shù)run_timer_list()和函數(shù)run_old_timers()函數(shù),定時執(zhí)行服務(wù)。
TVECS結(jié)構(gòu)及其實現(xiàn)
有關(guān)TVECS結(jié)構(gòu)的一些數(shù)據(jù)結(jié)構(gòu)定義如下:
#define TVN_BITS 6
#define TVR_BITS 8
#define TVN_SIZE (1 << TVN_BITS)
#define TVR_SIZE (1 << TVR_BITS)
#define TVN_MASK (TVN_SIZE - 1)
#define TVR_MASK (TVR_SIZE - 1)
#define SLOW_BUT_DEBUGGING_TIMERS 0
struct timer_vec {
int index;
struct timer_list *vec[TVN_SIZE];
};
struct timer_vec_root {
int index;
struct timer_list *vec[TVR_SIZE];
};
static struct timer_vec tv5 = { 0 };
static struct timer_vec tv4 = { 0 };
static struct timer_vec tv3 = { 0 };
static struct timer_vec tv2 = { 0 };
static struct timer_vec_root tv1 = { 0 };
static struct timer_vec * const tvecs[] = {
(struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5
};
#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0]))
static unsigned long timer_jiffies = 0;
TVECS結(jié)構(gòu)是一個元素個數(shù)為5的數(shù)組,分別指向tv1,tv2,tv3,tv4,tv5的地址。其中,tv1是結(jié)構(gòu)timer_vec_root的變量,它有一個index域和有256個元素的指針數(shù)組,該數(shù)組的每個元素都是一條類型為timer_list的鏈表。其余四個元素都是結(jié)構(gòu)timer_vec的變量,它們各有一個index域和64個元素的指針數(shù)組,這些數(shù)組的每個元素也都是一條鏈表。
函數(shù)internal_add_timer(struct timer_list *timer)
函數(shù)代碼如下:
static inline void internal_add_timer(struct timer_list *timer)
{
/*
* must be cli-ed when calling this
*/
unsigned long expires = timer->expires;
unsigned long idx = expires - timer_jiffies;
if (idx < TVR_SIZE) {
int i = expires & TVR_MASK;
insert_timer(timer, tv1.vec, i);
} else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
int i = (expires >> TVR_BITS) & TVN_MASK;
insert_timer(timer, tv2.vec, i);
} else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
insert_timer(timer, tv3.vec, i);
} else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
insert_timer(timer, tv4.vec, i);
} else if (expires < timer_jiffies) {
/* can happen if you add a timer with expires == jiffies,
* or you set a timer to go off in the past
*/
insert_timer(timer, tv1.vec, tv1.index);
} else if (idx < 0xffffffffUL) {
int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
insert_timer(timer, tv5.vec, i);
} else {
/* Can only get here on architectures with 64-bit jiffies */
timer->next = timer->prev = timer;
}
}
expires
在調(diào)用該函數(shù)之前,必須關(guān)中。對該函數(shù)的說明如下:
取出要加進(jìn)TVECS的timer的激發(fā)時間(expires),算出expires與timer_jiffies的差值idx,用來決定該插到哪個隊列中去。
若idx小于2^8,則取expires的第0位到第7位的值I,把timer加到tv1.vec中第I個鏈表的第一個表項之前。
若idx小于2^14,則取expires的第8位到第13位的值I,把timer加到tv2.vec中第I個鏈表的第一個表項之前。
若idx小于2^20,則取expires的第14位到第19位的值I,把timer加到tv3.vec中第I個鏈表的第一個表項之前。
若idx小于2^26,則取expires的第20位到第25位的值I,把timer加到tv4.vec中第I個鏈表的第一個表項之前。
若expires小于timer_jiffies,即idx小于0,則表明該timer到期,應(yīng)該把timer放入tv1.vec中tv1.index指定的鏈表的第一個表項之前。
若idx小于2^32,則取expires的第26位到第32位的值I,把timer加到tv5.vec中第I個鏈表的第一個表項之前。
若idx大等于2^32,該情況只有在64位的機(jī)器上才有可能發(fā)生,在這種情況下,不把timer加入TVECS結(jié)構(gòu)。
函數(shù)cascade_timers(struct timer_vec *tv)
該函數(shù)只是把tv->index指定的那條鏈表上的所有timer調(diào)用internal_add_timer()函數(shù)進(jìn)行重新調(diào)整,這些timer將放入TVECS結(jié)構(gòu)中比原來位置往前移一級,比如說,tv4上的timer將放到tv3上去,tv2上的timer將放到tv1上。這種前移是由run_timer_list函數(shù)里調(diào)用cascade_timers函數(shù)的時機(jī)來保證的。然后把該條鏈表置空,tv->index加1,若tv->index等于64,則重新置為0。
函數(shù)run_timer_list()
函數(shù)代碼如下:
static inline void run_timer_list(void)
{
cli();
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -