Linux x86_64 内核中断初始化
中断分类
Linux 系统中, 中断分为:
硬中断: 由外部设备或者执行异常产生的需要快速处理的中断如缺页中断定时器硬件中断
根据内部产生还是外部产生, 分为:
异常: 异常是内部产生的中断, 不可屏蔽
外部中断: 外部中断是由外部设备产生的, 可以屏蔽
软中断:
软中断是 Linux 系统中断处理的底半处理部分, 是 Linux 模拟的中断为了加快硬件中断的处理, 防止数据的丢失, Linux 对中断处理分为顶半处理和底半处理两部分, 顶半处理程序快速处理硬件事件, 把不是那么紧急的逻辑放到底半处理程序中, 可以简单的认为硬终端处理程序为顶半处理程序, 软中断处理程序为底半处理程序软中断一般在硬中断处理程序执行后才会执行但是当硬中断嵌套的时候, 软中断会在所有的硬中断处理完毕后才会处理, 当软中断太多, 会放到 ksoftirqd 线程中处理
内核初始化 - 中断
intel 处理器有 256 个硬中断号其中前 32 个中断号为异常使用, 在内核初始化的时候进行初始化内核初始化的代码流程如下:
可以看到首先初始化异常处理, 再初始化部分外部中断, 再初始化一部分软中断处理asmlinkagevoid__init start_kernel(void)
{
lock_kernel();
...
// 初始化调度模块
sched_init();
...
sort_main_extable();
// 初始化异常处理
trap_init();
...
// 初始化外部中断
init_IRQ();
...
// 初始化定时器模块, 同时, 会注册定时器的软中断处理函数
init_timers();
// 初始化软中断)
softirq_init();
time_init();
...
// 初始化
acpi_early_init();
}
异常中断初始化
异常中断在内核中称为 trap, 异常中断初始化代码为// 门初始化初始化中断向量表系统有固定的 256 个硬件中断向量
void__init trap_init(void)
{
set_intr_gate(0,÷_error);
set_intr_gate_ist(1,&debug,DEBUG_STACK);
set_intr_gate_ist(2,&nmi,NMI_STACK);
set_intr_gate(3,&int3);
set_system_gate(4,&overflow);/* int4-5 can be called from all */
set_system_gate(5,&bounds);
set_intr_gate(6,&invalid_op);
set_intr_gate(7,&device_not_available);
set_intr_gate_ist(8,&double_fault,DOUBLEFAULT_STACK);
set_intr_gate(9,&coprocessor_segment_overrun);
set_intr_gate(10,&invalid_TSS);
set_intr_gate(11,&segment_not_present);
set_intr_gate_ist(12,&stack_segment,STACKFAULT_STACK);
set_intr_gate(13,&general_protection);
set_intr_gate(14,&page_fault);
set_intr_gate(15,&spurious_interrupt_bug);
set_intr_gate(16,&coprocessor_error);
set_intr_gate(17,&alignment_check);
#ifdefCONFIG_X86_MCE
set_intr_gate_ist(18,&machine_check,MCE_STACK);
#endif
set_intr_gate(19,&simd_coprocessor_error);
#ifdefCONFIG_IA32_EMULATION
set_system_gate(IA32_SYSCALL_VECTOR,ia32_syscall);
#endif
set_intr_gate(KDB_VECTOR,call_debug);
/*
* Should be a barrier for any external CPU state.
*/
cpu_init();
}
总结如下:中断向量号异常事件Linux 的处理程序0除法错误Divide_error
1调试异常Debug
2NMI 中断Nmi
3单字节,int 3Int3
4溢出Overflow
5边界监测中断Bounds
6无效操作码Invalid_op
7设备不可用Device_not_available
8双重故障Double_fault
9协处理器段溢出Coprocessor_segment_overrun
10无效 TSSIncalid_tss
11缺段中断Segment_not_present
12堆栈异常Stack_segment
13一般保护异常General_protection
14页异常Page_fault
15Spurious_interrupt_bug
16协处理器出错Coprocessor_error
17对齐检查中断Alignment_check
0x80系统调用ia32_syscall
0xf9内核调试call_debug
上述中断处理函数都是汇编语言编写一部分汇编直接处理完毕, 一部分通过调用 C 函数帮助处理
汇编代码在
linux/arch/x86_64/entry.S
中, 大部分都是调用 C 函数
do_中断处理函数名
处理
整理如下:中断向量号异常事件Linux 汇编调用 c 函数处理结果0除法错误Divide_errordo_divide_error发送 SIGFPE 信号
1调试异常Debugdo_debug发送 SIGTRAP 信号
2NMI 中断Nmido_nmi
3单字节,int 3Int3do_int3发送 SIGTRAP 信号
4溢出Overflowdo_overflow发送 SIGSEGV 信号
5边界监测中断Boundsdo_bounds发送 SIGSEGV 信号
6无效操作码Invalid_opdo_invalid_op发送 SIGILL 信号
7设备不可用Device_not_availablemath_state_restore发送 SIGSEGV 信号
8双重故障Double_faultdo_double_fault
9协处理器段溢出Coprocessor_segment_overrundo_coprocessor_segment_overrun发送 SIGFPE 信号
10无效 TSSInvalid_tssdo_invalid_TSS发送 SIGSEGV 信号
11缺段中断Segment_not_presentdo_segment_not_present发送 SIGBUS 信号
12堆栈异常Stack_segmentdo_stack_segment
13一般保护异常General_protectiondo_general_protection
14页异常Page_faultdo_page_fault处理缺页中断
15Spurious_interrupt_bugdo_spurious_interrupt_bug
16协处理器出错Coprocessor_errordo_coprocessor_error发送 SIGFPE 信号
17对齐检查中断Alignment_checkdo_alignment_check发送 SIGBUS 信号
0x80系统调用ia32_syscall
0xf9内核调试call_debugdo_call_debug
外部中断初始化
中断控制器硬件 APIC 分为两种: 本地 APIC 和全局 APIC 本地 APIC 集成在 CPU 内部, 每个 CPU 都有一个, 用于处理本地中断请求, CPU 可以通过 APIC 向其他 CPU 发送中断, 现在主要用于 CPU 之间的通信 (IPI) 全局 APIC 主要是连接外部设备, 用于外部设备的中断在内核中断初始化的时候, 会初始化三个与 IPI 相关中断void__init init_IRQ(void)
{
inti;
/**
* 该函数主要是初始化硬件
* 1. 初始化本地 APIC 控制芯片
* 2. 初始化 8259A 芯片
/
init_ISA_irqs();
/*
* 清空 32 以后的中断向量表(除了系统调用和内核调试用的中断号)
*/
for(i=0;i
intvector=FIRST_EXTERNAL_VECTOR+i;
if(i>=NR_IRQS)
break;
if(vector!=IA32_SYSCALL_VECTOR&&vector!=KDB_VECTOR){
set_intr_gate(vector,interrupt[i]);
}
}
// 多处理器通信中断
#ifdefCONFIG_SMP
set_intr_gate(FIRST_DEVICE_VECTOR,interrupt[0]);
set_intr_gate(RESCHEDULE_VECTOR,reschedule_interrupt);
set_intr_gate(INVALIDATE_TLB_VECTOR,invalidate_interrupt);
set_intr_gate(CALL_FUNCTION_VECTOR,call_function_interrupt);
#endif
// 本地 APIC 中断
#ifdefCONFIG_X86_LOCAL_APIC
/* self generated IPI for local APIC timer */
set_intr_gate(LOCAL_TIMER_VECTOR,apic_timer_interrupt);
set_intr_gate(SPURIOUS_APIC_VECTOR,spurious_interrupt);
set_intr_gate(ERROR_APIC_VECTOR,error_interrupt);
#endif
setup_timer();
if(!acpi_ioapic)
setup_irq(2,&irq2);
}
总结如下:中断向量号中断名异常事件中断处理函数调用 c 函数处理结果0xfcRESCHEDULE_VECTOR处理器间中断, 用于 cpu 之间同学,其他 cpu 要求重新调度reschedule_interruptsmp_reschedule_interrupt将线程调度标志置为需要重新调度。之后内核检查标志的时候会重新调度线程
0xfdINVALIDATE_TLB_VECTOR处理器间中断, 用于 cpu 之间通信,其他 cpu 要求 TLB 缓存失效invalidate_interruptsmp_invalidate_interruptcpu 刷新 TLB
0xfaCALL_FUNCTION_VECTOR处理器间中断, 用于 cpu 之间通信,让另外的 cpu 调用某个函数call_function_interruptsmp_call_function_interrupt函数数据通过 call_data_struct 传送,cpu 会调用该函数
0xefLOCAL_TIMER_VECTORAPIC 定期器中断apic_timer_interruptsmp_apic_timer_interrupt触发定时器的软中断
0xffSPURIOUS_APIC_VECTOR伪中断spurious_interruptsmp_spurious_interrupt忽略
0xfeERROR_APIC_VECTORAPIC 错误error_interruptsmp_error_interrupt打印错误
0xfa 中断说明:
当 cpu 需要另一个 cpu 执行某个函数时, 只需要初始化structcall_data_struct{
void(*func)(void*info);
void*info;
atomic_tstarted;
atomic_tfinished;
intwait;
};
的结构体, 然后发出一个 0xfa 中断即可
软中断初始化
软中断初始化分为两部分:
初始化定时器时, 会打开 TIMER_SOFTIRQ 的软中断, 并设置中断处理函数为 run_timer_softirq
softirq_init 函数执行, 会打开 TASKLET_SOFTIRQ 和 HI_SOFTIRQ, 处理函数分别为 tasklet_action 和 tasklet_hi_action
软中断的线程处理机制就不说了
来源: https://www.cnblogs.com/stonehat/p/8681639.html