📄 2.html
字号:
|SAVE_ALL ---<br> |do_IRQ | wrapper routines<br> |handle_IRQ_event ---<br> |handler() -> timer_interrupt // registered IRQ 0 handler<br> |do_timer_interrupt<br> |do_timer<br> |jiffies++;<br> |update_process_times<br> |if (--counter <= 0) { // if time slice ended then<br> |counter = 0; // reset counter<br> |need_resched = 1; // prepare to reschedule<br> |}<br> |do_softirq<br> |while (need_resched) { // if necessary<br> |schedule // reschedule<br> |handle_softirq<br> |}<br> |RESTORE_ALL<p>·IRQ0x00_interrupt, SAVE_ALL [include/asm/hw_irq.h]<br>·do_IRQ, handle_IRQ_event [arch/i386/kernel/irq.c]<br>·timer_interrupt, do_timer_interrupt [arch/i386/kernel/time.c]<br>·do_timer, update_process_times [kernel/timer.c]<br>·do_softirq [kernel/soft_irq.c]<br>·RESTORE_ALL, while loop [arch/i386/kernel/entry.S]<p> 系统启动核心时,调用start_kernal()继续各方面的初始化,在这之前,各种中断都被禁止,只有在完成必要的初始化后,直到执行完Kmalloc_init()后,才允许中断(init\main.c)。与时钟中断有关的部分初始化如下:<p> 调用trap_init()设置各种trap入口,如system_call、GDT entry、LDT entry、call gate等。其中0~17为各种错误入口,18~47保留。<p> 调用init_IRQ()函数设置核心系统的时钟周期为10ms,即100HZ,它是以后按照轮转法进行CPU调度时所依照的基准时钟周期。每10ms产生的时钟中断信号直接输入到第一块8259A的INT 0(即irq0)。初始化中断矢量表中从0x20起的17个中断矢量,用bad_IRQ#_interrupt函数的地址(#为中断号)填写。<p> 调用sched_init()函数,设置启动第一个进程init_task。设置用于管理bottom_half机制的数据结构bh_base[],规定三类事件的中断处理函数,即时钟TIMER_BH、设备TQUEUE_BH和IMMEDIATE_BH。<p> 调用time_init()函数,首先读取当时的CMOS时间,最后调用setup_x86_irq(0,&irq0)函数,把irq0挂到irq_action[0]队列的后面,并把中断矢量表中第0x20项,即timer中断对应的中断矢量改为IRQ0_interrupt函数的地址,在irq0中,指定时间中断服务程序是timer_interrupt,<br> static struct irqaction irq0 = { timer_interrupt, 0, 0, "timer", NULL, NULL}<br> 结构irqaction的定义如下:<br> struct irqaction {<br> void (*handler)(int, void *, struct pt_regs *); /* 中断服务函数入口 */<br> unsigned long flags; /* 服务允中与否标记 */<br> unsigned long mask;<br> const char *name;<br> void *dev_id;<br> struct irqaction *next;<br> };<br> 其中,若flag==SA_INTERRUPT,则中断矢量改为fast_IRQ#_interrupt,在执行中断服务的过程中不允许出现中断,若为其它标记,则中断矢量为IRQ#_interrupt,在执行中断服务的过程中,允许出现中断。<br>Irq_action的定义与初始化如下:<br> static void (*interrupt[17])(void) = {IRQ#_interrupt};<br> static void (*fast_interrupt[16])(void) = {fast_IRQ#_interrupt};<br> static void (*bad_interrupt[16])(void) = {bad_IRQ#_interrupt};(以上#为中断号)<br> static struct irqaction *irq_action[16] = {<br> NULL, NULL, NULL, NULL,<br> NULL, NULL, NULL, NULL,<br> NULL, NULL, NULL, NULL,<br> NULL, NULL, NULL, NULL<br> };<p> irq_action是一个全局数组,每个元素指向一个irq队列,共16个irq队列,时钟中断请求队列在第一个队列,即irq_action[0]。当每个中断请求到来时,都调用setup_x86_irq把该请求挂到相应的队列的后面。<p> 以后,系统每10ms产生一次时钟中断信号,该信号直接输入到第一块8259A的INT 0(即irq0)。CPU根据中断矢量表和中断源,找到中断矢量函数入口IRQ0_interrupt(程序运行过程中允许中断)或者fast_IRQ0_interrupt(程序运行过程中不允许中断)或者bad_IRQ0_interrupt(不执行任何动作,直接返回),这些函数由宏BUILD_TIMER_IRQ(chip, nr, mask)展开定义。<br>宏BUILD_TIMER_IRQ(chip, nr, mask)的定义如下:<br>#define BUILD_TIMER_IRQ(chip,nr,mask) \<br>asmlinkage void IRQ_NAME(nr); \<br>asmlinkage void FAST_IRQ_NAME(nr); \<br>asmlinkage void BAD_IRQ_NAME(nr); \<br>__asm__( \<br>"\n"__ALIGN_STR"\n" \<br>SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \<br>SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \<br>SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \<br> "pushl $-"#nr"-2\n\t" \<br> SAVE_ALL \<br> ENTER_KERNEL \<br> ACK_##chip(mask,(nr&7)) \<br> "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\ /* intr_count为进入临界区的同步信号量 */<br> "movl %esp,%ebx\n\t" \<br> "pushl %ebx\n\t" \<br> "pushl $" #nr "\n\t" \ /* 把do_irq函数参数压进堆栈 */<br> "call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \<br> "addl $8,%esp\n\t" \<br> "cli\n\t" \<br> UNBLK_##chip(mask) \<br> "decl "SYMBOL_NAME_STR(intr_count)"\n\t" \<br> "incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \<br> "jmp ret_from_sys_call\n");<p> 其中nr为中断请求类型,取值0~15。在irq.c中通过语句BUILD_TIMER_IRQ(first, 0, 0x01)调用该宏,在执行宏的过程中处理时钟中断响应程序do_irq()。<p> 函数do_irq()的第一个参数是中断请求队列序号,时钟中断请求传进来的该参数是0。于是程序根据参数0找到请求队列irq_action[0],逐个处理该队列上handler所指的时钟中断请求的服务函数。由于已经指定时钟中断请求的服务函数是timer_interrupt,在函数timer_interrupt中,立即调用do_timer()函数。<p> 函数do_timer()把jiffies和lost_ticks加1,接着就执行mark_bh(TIMER_BH)函数,把bottom_half中时钟队列对应的位置位,表示该队列处于激活状态。在做完这些动作后,程序从函数do_irq()中返回,继续执行以后的汇编代码。于是,程序在执行语句jmp ret_from_sys_call后,跳到指定的位置处继续执行。<p>代码段jmp ret_from_sys_call及其相关的代码段如下:<br> ALIGN<br> .globl ret_from_sys_call<br>ret_from_sys_call:<br> cmpl $0,SYMBOL_NAME(intr_count)<br> jne 2f<br>9: movl SYMBOL_NAME(bh_mask),%eax<br> andl SYMBOL_NAME(bh_active),%eax<br> jne handle_bottom_half<br>#ifdef __SMP__<br> cmpb $(NO_PROC_ID), SYMBOL_NAME(saved_active_kernel_processor)<br> jne 2f<br>#endif<br> movl EFLAGS(%esp),%eax # check VM86 flag: CS/SS are<br> testl $(VM_MASK),%eax # different then<br> jne 1f<br> cmpw $(KERNEL_CS),CS(%esp) # was old code segment supervisor ?<br> je 2f<br>1: sti<br> orl $(IF_MASK),%eax # these just try to make sure<br> andl $~NT_MASK,%eax # the program doesn't do anything<br> movl %eax,EFLAGS(%esp) # stupid<br> cmpl $0,SYMBOL_NAME(need_resched)<br> jne reschedule<br>#ifdef __SMP__<br> GET_PROCESSOR_OFFSET(%eax)<br> movl SYMBOL_NAME(current_set)(,%eax), %eax<br>#else<br> movl SYMBOL_NAME(current_set),%eax<br>#endif<br> cmpl SYMBOL_NAME(task),%eax # task[0] cannot have signals<br> je 2f<br> movl blocked(%eax),%ecx<br> movl %ecx,%ebx # save blocked in %ebx for signal handling<br> notl %ecx<br> andl signal(%eax),%ecx<br> jne signal_return<br>2: RESTORE_ALL<p>ALIGN<br>signal_return:<br> movl %esp,%ecx<br> pushl %ecx<br> testl $(VM_MASK),EFLAGS(%ecx)<br> jne v86_signal_return<br> pushl %ebx<br> call SYMBOL_NAME(do_signal)<br> popl %ebx<br> popl %ebx<br> RESTORE_ALL<p>ALIGN<br>v86_signal_return:<br> call SYMBOL_NAME(save_v86_state)<br> movl %eax,%esp<br> pushl %eax<br> pushl %ebx<br> call SYMBOL_NAME(do_signal)<br> popl %ebx<br> popl %ebx<br> RESTORE_ALL<p> handle_bottom_half:<br>incl SYMBOL_NAME(intr_count)<br>call SYMBOL_NAME(do_bottom_half)<br>decl SYMBOL_NAME(intr_count)<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -