• Linux x86_64内核中断初始化


    中断分类

    Linux系统中,中断分为:

    • 硬中断:由外部设备或者执行异常产生的需要快速处理的中断。如缺页中断、定时器硬件中断。

      根据内部产生还是外部产生,分为:

      1. 异常:异常是内部产生的中断,不可屏蔽。
      2. 外部中断:外部中断是由外部设备产生的,可以屏蔽。
    • 软中断:

      ​ 软中断是Linux系统中断处理的底半处理部分,是Linux模拟的中断。为了加快硬件中断的处理,防止数据的丢失,Linux对中断处理分为顶半处理和底半处理两部分,顶半处理程序快速处理硬件事件,把不是那么紧急的逻辑放到底半处理程序中,可以简单的认为硬终端处理程序为顶半处理程序,软中断处理程序为底半处理程序。软中断一般在硬中断处理程序执行后才会执行。但是当硬中断嵌套的时候,软中断会在所有的硬中断处理完毕后才会处理,当软中断太多,会放到ksoftirqd线程中处理。

    内核初始化-中断

    intel处理器有256个硬中断号。其中前32个中断号为异常使用,在内核初始化的时候进行初始化。内核初始化的代码流程如下:
    可以看到首先初始化异常处理,再初始化部分外部中断,再初始化一部分软中断处理。

    
    asmlinkage void __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,&divide_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);
    #ifdef CONFIG_X86_MCE
    	set_intr_gate_ist(18,&machine_check, MCE_STACK); 
    #endif
    	set_intr_gate(19,&simd_coprocessor_error);
    
    #ifdef CONFIG_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
    2 NMI中断 Nmi
    3 单字节,int 3 Int3
    4 溢出 Overflow
    5 边界监测中断 Bounds
    6 无效操作码 Invalid_op
    7 设备不可用 Device_not_available
    8 双重故障 Double_fault
    9 协处理器段溢出 Coprocessor_segment_overrun
    10 无效TSS Incalid_tss
    11 缺段中断 Segment_not_present
    12 堆栈异常 Stack_segment
    13 一般保护异常 General_protection
    14 页异常 Page_fault
    15 Spurious_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_error do_divide_error 发送SIGFPE信号
    1 调试异常 Debug do_debug 发送SIGTRAP信号
    2 NMI中断 Nmi do_nmi
    3 单字节,int 3 Int3 do_int3 发送SIGTRAP信号
    4 溢出 Overflow do_overflow 发送SIGSEGV信号
    5 边界监测中断 Bounds do_bounds 发送SIGSEGV信号
    6 无效操作码 Invalid_op do_invalid_op 发送SIGILL信号
    7 设备不可用 Device_not_available math_state_restore 发送SIGSEGV信号
    8 双重故障 Double_fault do_double_fault
    9 协处理器段溢出 Coprocessor_segment_overrun do_coprocessor_segment_overrun 发送SIGFPE信号
    10 无效TSS Invalid_tss do_invalid_TSS 发送SIGSEGV信号
    11 缺段中断 Segment_not_present do_segment_not_present 发送SIGBUS信号
    12 堆栈异常 Stack_segment do_stack_segment
    13 一般保护异常 General_protection do_general_protection
    14 页异常 Page_fault do_page_fault 处理缺页中断
    15 Spurious_interrupt_bug do_spurious_interrupt_bug
    16 协处理器出错 Coprocessor_error do_coprocessor_error 发送SIGFPE信号
    17 对齐检查中断 Alignment_check do_alignment_check 发送SIGBUS信号
    0x80 系统调用 ia32_syscall
    0xf9 内核调试 call_debug do_call_debug

    外部中断初始化

    中断控制器硬件APIC分为两种:本地APIC和全局APIC。本地APIC集成在CPU内部,每个CPU都有一个,用于处理本地中断请求,CPU可以通过APIC向其他CPU发送中断,现在主要用于CPU之间的通信(IPI)。全局APIC主要是连接外部设备,用于外部设备的中断。在内核中断初始化的时候,会初始化三个与IPI相关中断。
    
    
    void __init init_IRQ(void)
    {
    	int i;
    	/**
    	* 该函数主要是初始化硬件
    	* 1. 初始化本地APIC控制芯片
    	* 2. 初始化8259A芯片
    	/
    	init_ISA_irqs();
    	/*
    	 * 清空32以后的中断向量表。(除了系统调用和内核调试用的中断号)
    	 */
    	for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
    		int vector = FIRST_EXTERNAL_VECTOR + i;
    		if (i >= NR_IRQS)
    			break;
    		if (vector != IA32_SYSCALL_VECTOR && vector != KDB_VECTOR) { 
    			set_intr_gate(vector, interrupt[i]);
    	}
    	}
    // 多处理器通信中断
    #ifdef CONFIG_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中断
    #ifdef CONFIG_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函数 处理结果
    0xfc RESCHEDULE_VECTOR 处理器间中断, 用于cpu之间同学,其他cpu要求重新调度 reschedule_interrupt smp_reschedule_interrupt 将线程调度标志置为需要重新调度。之后内核检查标志的时候会重新调度线程
    0xfd INVALIDATE_TLB_VECTOR 处理器间中断, 用于cpu之间通信,其他cpu要求TLB缓存失效 invalidate_interrupt smp_invalidate_interrupt cpu刷新TLB
    0xfa CALL_FUNCTION_VECTOR 处理器间中断, 用于cpu之间通信,让另外的cpu调用某个函数 call_function_interrupt smp_call_function_interrupt 函数数据通过call_data_struct传送,cpu会调用该函数
    0xef LOCAL_TIMER_VECTOR APIC定期器中断 apic_timer_interrupt smp_apic_timer_interrupt 触发定时器的软中断
    0xff SPURIOUS_APIC_VECTOR 伪中断 spurious_interrupt smp_spurious_interrupt 忽略
    0xfe ERROR_APIC_VECTOR APIC错误 error_interrupt smp_error_interrupt 打印错误

    0xfa中断说明:
    当cpu需要另一个cpu执行某个函数时,只需要初始化

    struct call_data_struct {
    	void (*func) (void *info);
    	void *info;
    	atomic_t started;
    	atomic_t finished;
    	int wait;
    };
    

    的结构体,然后发出一个0xfa中断即可。

    软中断初始化

    软中断初始化分为两部分:

    1. 初始化定时器时,会打开TIMER_SOFTIRQ的软中断,并设置中断处理函数为run_timer_softirq。
    2. softirq_init函数执行,会打开TASKLET_SOFTIRQ和HI_SOFTIRQ,处理函数分别为 tasklet_action和 tasklet_hi_action。

    软中断的线程处理机制就不说了。

  • 相关阅读:
    五种提高 SQL 性能的方法
    join 使用详解方式
    关于MagicAjax的用法
    收藏几段SQL Server语句和存储过程
    ubuntu nfs配置 以及mount.nfs:access denied by server while mounting问题解决
    Hisi开发板上 SQLite3.3.8移植
    父进程非阻塞回收子进程(适用LINUX下C语言的clientserver模型)
    busybox asm/page.h: No such find.
    ubuntu11.10 samba服务器配置
    errno定义
  • 原文地址:https://www.cnblogs.com/stonehat/p/8681639.html
Copyright © 2020-2023  润新知