六、IDT的初始化
1.两次初始化
运行模式初始值使用者
第一次实模式空处理程序BIOS例程
第二次保护模式有意义的中断处理程序或异常处理程序Linux系统
2.在IDT表的初始化完成之初,每个中断处理队列都是空的,此时即使打开中断并且某个外设中断真的发生了,也得不到实际的服务,因为没有执行具体的中断处理程序。
真正的中断服务要到具体设备的初始化程序将其中断处理程序ISR挂入某个中断请求队列后才会发生
3.在允许发生中断以前,必须适当地初始化IDT
七、激活中断或异常(以下内容都是由硬件自动完成)
1.确定与中断或异常相关的中断向量号
中断:硬件设备控制器通过IRQ向CPU发出信号,中断管制器把接受到的信号转换为中断向量号i
异常:对于软件指令发出或产生的异常,CPU会差别归类错误的类别,这个类别号就是中断向量
2.IDT第i项 ----- 段选择符 -----段描述符 ----- 段基址
3.IDT第i项 ----- 偏移量
4.段基址 + 偏移量 ----- 中断处理程序第一条指令的地址
5.在栈中保存EFLAGS、CS、EIP的内容
6.如果异常产生了一个出错码,把它保存在栈中
7.装载CS、EIP,其值分别是2-段选择符和4-偏移量,由这两个寄存器可得到中断或异常处理程序第一条指令的地址
八、找到中断或异常处理程序的第一条指令后,跳转这到这一指令的过程
1.中断
(1)在当前进程的内核堆栈中保存IRQ的值,为什么与系统调用号区分,保存的是-n
(2)在当前进程的内核堆栈中保存寄存器的值:SAVE_ALL
EFLAGS、CS、EIP、SS、ESP不包括在内,因为它们由控制单元自动保存(见七-7)
(3)把栈顶的地址存放到EAX中
(4)把用户段的选择符装到DS和ES中
(5)调用do_IRQ(),地址保存在CS、EIP中(见七-7)
(6)为正在给IRQ线服务的PIC(中断控制器)一个应答,这将允许PIC进一步发出中断
(7)执行共享这个IRQ的所有设备的ISR(总服务程序称为IRQ,某个设备的具体的服务程序称为ISR)
(8)跳到ret_from_intr()的地址后终止
(6)(7)(8)都是在(5)中被调用的,见十
2.异常
(1)如果异常发生时,控制单元没有自己把一个出错码压出栈中(见七-6),则压入一个空值。
这个凑数的出错码不在正常的出错码应该在的位置,以下-步是为了把它调整到它应该在的位置
(3)把异常处理程序的地址压入栈中
(4)把异常处理程度可能用到的寄存器保存到栈中
(5)把栈中位于ESP+36处的硬件出错码拷贝到EDX中,给栈中这一位置存上-1
(6)把保存在栈中ESP+32位置的异常处理程序的地址装入EDI中,给栈中的这一位置写入ES的值
(7)把栈当栈顶拷贝到EAX中
(8)把用户段的选择符装到DS和ES中
(9)调用地址在EDI中的异常处理程序
九、从中断或异常处理程序返回的过程
1.跳转到用于返回的代码的入口点
(1)中断ret_from_intr()
(2)异常:ret_from_exception()
2.把当前线程描述符的地址装载到EBP
3.根据栈中的CS和EFLAGS确定要返回到用户态还是内核态
4.如果有进程调度请求则调度
5.通过执行iret指令结束控制,被中断的程序重新开始执行
十、总的中服务务程序IRQ
1.为正在给IRQ线服务的PIC(中断控制器)一个应答,这将允许PIC进一步发出中断
2.发生以下任何一种情况,则返回
(1)相应的IRQ线被禁止
(2)另一个CPU正常处理这类中断
(3)没有相关的ISR
3.执行共享这个IRQ的所有设备的ISR
4.检查是否有可延迟函数在等待执行,如果有do_softirq()
5.ret_from_intr()
十一、中断服务例程
一个中断服务例程(ISR)实现一种特定设备的操作。当中断处理程序必须执行ISR时,它就调用hand_IRQ_event()函数。
十二、IRQ线的动态分配
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/fuwuqi/)在激活一个准备利用IRQ线的设备之前,其相应的驱动程序调用request_irq()。这个函数建立一个新的irqaction函数,并用参数值初始化它。然后调用setup_irq()函数把这个描述符插入到适合的IRQ链表。如果setup_irq()返回一个出错码,设备驱动程序中止操作,这意味着IRQ线已有另一个设备所使用,而这个设备不允许中断共享。当设备操作结束时,驱动程序调用free_irq()函数从IRQ链表中删除这个描述符,并释放相应的内存区。
request_irq()
free_irq()
1.Linux把紧随中断要执行的操作分为三类
特点处理方法举例
第一类紧急的在禁止可屏蔽中断的情况下立即执行修改设备和处理器同时访问的数据结构
第二类非紧急的在开中断的情况下立即执行修改那些只有处理器才会访问的数据结构(例如,按下一个键后读扫描码)
第三类非紧急可延迟的由独立的函数来执行把缓冲区的内核拷贝到某个进程的地址空间
2.把可延迟中断从中断处理程序中抽出来,由独立的函数来执行,有助于使内核保持较短的响应时间
3.Linux2.6使用可延迟函数和工作队列来处理可延迟中断,这两个都是内核函数。
二、可延迟函数
1.可延迟函数包括软中断和tasklet,tasklet是在软中断之上实现的
tasklet是I/O驱动程序中实现可延迟函数的首选方法。
tasklet建立在HI_SOFTIRQ和TASKLET_SOFTIRQ这两个软中断之上
几个tasklet可以与同一软中断相关联,每个tasklet执行自己的函数
分配方式并发性可重入性
软中断编译时静态分配可以并发地在多个CPU上运行必须是可重入的,并明确地使用自旋锁保护其数据结构
tasklet运行时动态分配相同类型的tasklet总是被串行地执行。不同类型的tasklet可以在几个CPU上并发地执行不必是可重入的
2.由给定CPU激活的一个可延迟函数必须在同一个CPU上执行
可延迟函数执行时不允许内核抢占,因为从一个CPU移到另一个CPU的过程需要将进程挂起
3.中断与软中断所使用的数据结构比较
中断软中断
中断向量号描述符irq_desc[中断向量号]softirq_vec[软中断号]
中断请求寄存器中断请求寄存器__softirq_active
中断屏蔽寄存器中断屏蔽寄存器__soft_mask
中断处理程序do_handler_namebh[]:32个
中断处理程序描述项irqactionsoftirq_action
中断机制的初始化trap_init():异常初始化
init_IRQ():中断初始化softirq_action
中断请求队列初始化init_ISA_irqs()open_softirq
中断处理程序与中断请求队列相关联request_irq()init_bh()
执行中断do_IRQ()do_softirq
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/fuwuqi/)4.激活可延迟函数
(1)激活软中断
A.把软中断置为把挂起状态,并周期性地检查处于挂起状态的软中断。如果有,就调用do_softirq()
B.一般是在以下几个点来检查挂起的软中断的a.调用local_bh_enable()激活本地软中断时b.do_IRQ()完成I/O中断的处理即将退出时c.用于周期性检查挂起状态软中断的内核线程ksoftirqd被唤醒时d.else,我觉得不太重要
(2)激活tasklet
A.把自己定义的描述符加入到tasklet_vec指向的链表中即可。调用tasklet_action()时,依次处理队列中的每个tasklet描述符,然后清空tasklet_vec指向的链表。
B.tasklet的每次激活至多触发tasklet函数的一次执行,除非tasklet函数重新激活自己
三、工作队列
1.它们允许内核函数被激活,并稍后由一种叫做工作者线程的特殊内核线程来执行
如果系统有n个CPU,就会创建n个工作者,但是只会创建一个工作队列
2.工作队列与可延迟函数的区别:
可延迟函数运行中断上下文中,而工作队列运行在进程中下文中。
可延迟函数不能阻塞,工作队列可执行可阻塞函数
可延迟函数被执行时不可能有任何正在运行的进程,工作队列中的函数由内核线程来执行,不访问用户态地址空间
3.工作队列的激活
把要执行的函数的描述符插入到工作队列中。
工作者等待函数被插入队列,被唤醒后把函数描述符取下并执行。
由于工作队列函数可以阻塞,工作者线程可以睡觉,或移动另一个CPU
总而言之:
当发生一个中断时,