中断处理程序是如何获取中断向量号的
CPU控制单元在最后会跳到中断IDT+n的地方,也就是中断源对应的IDT表的位置
ENTRY(irq_entries_start)
RING0_INT_FRAME
vector=0
.rept NR_IRQS
ALIGN
.if vector
CFI_ADJUST_CFA_OFFSET -4
.endif
1: pushl $~(vector) //中断向量压栈
CFI_ADJUST_CFA_OFFSET 4
jmp common_interrupt
.previous
.long 1b
.text
vector=vector+1 //展开后每个IDT加1,用于计算中断向量用于do_IRQ进一步处理
.endr
END(irq_entries_start)
压栈的寄存器对应结构体如下
对应
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int xds;
int xes;
int xfs;
/* int xgs; */
long orig_eax; //恰好是压栈的中断向量号
long eip;
int xcs;
long eflags;
long esp;
int xss;
};
用户态程序运行时出现中断,那么特权级发生变化,栈要切换,切换的流程如下
__switch_to
=>load_esp0(tss, next);
=>native_load_esp0(tss, thread);
=>tss->x86_tss.esp0 = thread->esp0;
esp0的定义如下:
struct thread_struct {
/* cached TLS descriptors. */
struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
unsigned long esp0;
unsigned long sysenter_cs;
unsigned long eip;
unsigned long esp;
unsigned long fs;
unsigned long gs;
/* Hardware debugging registers */
unsigned long debugreg[8]; /* %%db0-7 debug registers */
/* fault info */
unsigned long cr2, trap_no, error_code;
/* floating point info */
union i387_union i387;
/* virtual 86 mode info */
struct vm86_struct __user * vm86_info;
unsigned long screen_bitmap;
unsigned long v86flags, v86mask, saved_esp0;
unsigned int saved_fs, saved_gs;
/* IO permissions */
unsigned long *io_bitmap_ptr;
unsigned long iopl;
/* max allowed port in the bitmap, in bytes: */
unsigned long io_bitmap_max;
};
esp0初始化位置
#define INIT_THREAD { \
.esp0 = sizeof(init_stack) + (long)&init_stack, \ //来源于thread_info的内核栈
.vm86_info = NULL, \
.sysenter_cs = __KERNEL_CS, \
.io_bitmap_ptr = NULL, \
.fs = __KERNEL_PERCPU, \
}
异常返回的定义
DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip)
#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
fastcall void do_##name(struct pt_regs * regs, long error_code) \
{ \
siginfo_t info; \
info.si_signo = signr; \
info.si_errno = 0; \
info.si_code = sicode; \
info.si_addr = (void __user *)siaddr; \
if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
== NOTIFY_STOP) \
return; \
do_trap(trapnr, signr, str, 1, regs, error_code, &info); \
=>do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, long error_code, siginfo_t *info)
tsk->thread.error_code = error_code;
tsk->thread.trap_no = trapnr;
if (info)//用户态发信号
force_sig_info(signr, info, tsk);
else
force_sig(signr, tsk);
}
设置和调用
set_trap_gate(0,÷_error);
ENTRY(divide_error)
RING0_INT_FRAME
pushl $0 # no error code
CFI_ADJUST_CFA_OFFSET 4
pushl $do_divide_error
CFI_ADJUST_CFA_OFFSET 4
jmp error_code
CFI_ENDPROC
END(divide_error)
void __init init_IRQ(void)
x86_init.irqs.intr_init();//X86_init.c (arch\x86\kernel): .intr_init = native_init_IRQ,
=>void __init native_init_IRQ(void)
x86_init.irqs.pre_vector_init();//X86_init.c (arch\x86\kernel): .pre_vector_init = init_ISA_irqs,
=>void __init init_ISA_irqs(void)
init_bsp_APIC();
legacy_pic->init(0);
/*
* 16 old-style INTA-cycle interrupts:
*/
for (i = 0; i < legacy_pic->nr_legacy_irqs; i++) {
struct irq_desc *desc = irq_to_desc(i);
desc->status = IRQ_DISABLED;
desc->action = NULL;
desc->depth = 1;
set_irq_chip_and_handler_name(i, &i8259A_chip,
handle_level_irq, "XT");
}
apic_intr_init();
void __init native_init_IRQ(void)
for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) {
/* IA32_SYSCALL_VECTOR could be used in trap_init already. */
if (!test_bit(i, used_vectors))
set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]);//将中断处理函数挂接到中断门上
}
struct legacy_pic default_legacy_pic = {
.nr_legacy_irqs = NR_IRQS_LEGACY,
.chip = &i8259A_chip,
.mask_all = mask_8259A,
.restore_mask = unmask_8259A,
.init = init_8259A,
.irq_pending = i8259A_irq_pending,
.make_irq = make_8259A_irq,
=>static void make_8259A_irq(unsigned int irq)
disable_irq_nosync(irq);
io_apic_irqs &= ~(1<<irq);
set_irq_chip_and_handler_name(irq, &i8259A_chip, handle_level_irq,
"XT");
enable_irq(irq);
};
参考资料
i8259A中断控制器分析 二
http://blog.sina.com.cn/s/blog_70dd169101019wzw.html
内核随记(一)——理解中断
http://blog.sina.com.cn/s/blog_5d0e8d0d01019cds.html
8259A工作原理描述
https://blog.csdn.net/aaaaatiger/article/details/2357395
8259a pic 原理收集
https://blog.csdn.net/github_30220885/article/details/47139671
x86保护模式的几点思考——IRQ、中断号和中断向量
http://blog.51cto.com/snower/557678
理解Linux中断 (1)
https://blog.csdn.net/tommy_wxie/article/details/7425685
理解Linux中断 (2)
https://blog.csdn.net/tommy_wxie/article/details/7425692
x86 kernel 中断机制分析一——IDT 文章不错
https://blog.csdn.net/yin262/article/details/53928178
linux对TSS(任务状态描述符)的使用---Linux内核笔记
https://blog.csdn.net/shinesi/article/details/1933851
x86体系下linux中的任务切换与TSS
https://blog.csdn.net/dog250/article/details/6203529
linux下X86架构IDT解析
http://blog.chinaunix.net/uid-27717694-id-3942170.html
linux内核中断、异常、系统调用的分析以及实践
https://blog.csdn.net/icyfire0105/article/details/1898523
LINUX中断描述符初始化 经典文章
http://www.cnblogs.com/icanth/archive/2012/06/04/2535251.html
linux不可屏蔽中断异常处理函数定义
https://blog.csdn.net/wyfwx/article/details/6740478
linux中断源码分析 - 概述(一) 文章不错
https://www.cnblogs.com/tolimit/p/4390724.html
linux中断源码分析 - 概述(一) 好文
https://www.cnblogs.com/tolimit/p/4390724.html
linux源码entry_32.S中interrupt数组的分析
https://blog.csdn.net/jinhongzhou/article/details/6015551
细说Linux内核中断机制
https://blog.csdn.net/yiqiaoxihui/article/details/81133950
kernel 3.10内核源码分析--中断--中断和异常返回流程
http://zzjlzx.blog.chinaunix.net/uid-14528823-id-4761421.html