当前位置: 首页 > news >正文

Linux中断概述

linux中断流程

本文简单概述学习一下Linux(Linux3.10,x86)的中断注册处理相关的流程,并讨论一下再中断情况下的调度机制。

中断向量表的注册

在调用了start_kernel启动内核之后,会调用trap_init函数来注册。

void __init trap_init(void)
{
	int i;

#ifdef CONFIG_EISA
	void __iomem *p = early_ioremap(0x0FFFD9, 4);

	if (readl(p) == 'E' + ('I'<<8) + ('S'<<16) + ('A'<<24))
		EISA_bus = 1;
	early_iounmap(p, 4);
#endif

	set_intr_gate(X86_TRAP_DE, &divide_error);
	set_intr_gate_ist(X86_TRAP_NMI, &nmi, NMI_STACK);
	/* int4 can be called from all */
	set_system_intr_gate(X86_TRAP_OF, &overflow);
	set_intr_gate(X86_TRAP_BR, &bounds);
	set_intr_gate(X86_TRAP_UD, &invalid_op);
	set_intr_gate(X86_TRAP_NM, &device_not_available);
#ifdef CONFIG_X86_32
	set_task_gate(X86_TRAP_DF, GDT_ENTRY_DOUBLEFAULT_TSS);
#else
	set_intr_gate_ist(X86_TRAP_DF, &double_fault, DOUBLEFAULT_STACK);
#endif
	set_intr_gate(X86_TRAP_OLD_MF, &coprocessor_segment_overrun);
	set_intr_gate(X86_TRAP_TS, &invalid_TSS);
	set_intr_gate(X86_TRAP_NP, &segment_not_present);
	set_intr_gate_ist(X86_TRAP_SS, &stack_segment, STACKFAULT_STACK);
	set_intr_gate(X86_TRAP_GP, &general_protection);
	set_intr_gate(X86_TRAP_SPURIOUS, &spurious_interrupt_bug);
	set_intr_gate(X86_TRAP_MF, &coprocessor_error);
	set_intr_gate(X86_TRAP_AC, &alignment_check);
#ifdef CONFIG_X86_MCE
	set_intr_gate_ist(X86_TRAP_MC, &machine_check, MCE_STACK);
#endif
	set_intr_gate(X86_TRAP_XF, &simd_coprocessor_error);

	/* Reserve all the builtin and the syscall vector: */
	for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++)
		set_bit(i, used_vectors);

#ifdef CONFIG_IA32_EMULATION
	set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall);
	set_bit(IA32_SYSCALL_VECTOR, used_vectors);
#endif

#ifdef CONFIG_X86_32
	set_system_trap_gate(SYSCALL_VECTOR, &system_call);
	set_bit(SYSCALL_VECTOR, used_vectors);
#endif

	/*
	 * Set the IDT descriptor to a fixed read-only location, so that the
	 * "sidt" instruction will not leak the location of the kernel, and
	 * to defend the IDT against arbitrary memory write vulnerabilities.
	 * It will be reloaded in cpu_init() */
	__set_fixmap(FIX_RO_IDT, __pa_symbol(idt_table), PAGE_KERNEL_RO);
	idt_descr.address = fix_to_virt(FIX_RO_IDT);

	/*
	 * Should be a barrier for any external CPU state:
	 */
	cpu_init();

	x86_init.irqs.trap_init();

#ifdef CONFIG_X86_64
	memcpy(&nmi_idt_table, &idt_table, IDT_ENTRIES * 16);
	set_nmi_gate(X86_TRAP_DB, &debug);
	set_nmi_gate(X86_TRAP_BP, &int3);
#endif
}

通过调用set_intr_gate函数来写入idt_entry表,将初始的信号和处理的事件写入,通过写入中断处理号和中断处理函数来进行中断源的注册。

初始化中断

在上一步将中断和中断源进行注册之后,就将中断进行初始化,即init_IRQ()函数。

void __init init_IRQ(void)
{
	int i;

	/*
	 * We probably need a better place for this, but it works for
	 * now ...
	 */
	x86_add_irq_domains();

	/*
	 * On cpu 0, Assign IRQ0_VECTOR..IRQ15_VECTOR's to IRQ 0..15.
	 * If these IRQ's are handled by legacy interrupt-controllers like PIC,
	 * then this configuration will likely be static after the boot. If
	 * these IRQ's are handled by more mordern controllers like IO-APIC,
	 * then this vector space can be freed and re-used dynamically as the
	 * irq's migrate etc.
	 */
	for (i = 0; i < legacy_pic->nr_legacy_irqs; i++)
		per_cpu(vector_irq, 0)[IRQ0_VECTOR + i] = i; // 将cpu0的中断号全部注册进去,当前只注册了cpu0的相关中断

	x86_init.irqs.intr_init();   // 初始中断
}

X86_init.irqs初始化的函数就是native_init_IRQ。

void __init native_init_IRQ(void)
{
	int i;

	/* Execute any quirks before the call gates are initialised: */
	x86_init.irqs.pre_vector_init();

	apic_intr_init();  // 注册apic相关的中断函数

	/*
	 * Cover the whole vector space, no vector can escape
	 * us. (some of these will be overridden and become
	 * 'special' SMP interrupts)
	 */
	i = FIRST_EXTERNAL_VECTOR;
	for_each_clear_bit_from(i, used_vectors, NR_VECTORS) {
		/* IA32_SYSCALL_VECTOR could be used in trap_init already. */
		set_intr_gate(i, interrupt[i - FIRST_EXTERNAL_VECTOR]);
	}

	if (!acpi_ioapic && !of_ioapic)
		setup_irq(2, &irq2);

#ifdef CONFIG_X86_32
	irq_ctx_init(smp_processor_id());
#endif
}

设置的每个中断的处理函数都是指向了interrupt这个数组的位置。在x86/kernel/entry_64.S文件中。

	.section .init.rodata,"a"
ENTRY(interrupt)     //指向中断
	.section .entry.text
	.p2align 5
	.p2align CONFIG_X86_L1_CACHE_SHIFT
ENTRY(irq_entries_start)
	INTR_FRAME
vector=FIRST_EXTERNAL_VECTOR
.rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7   // 获取数据的参数
	.balign 32
  .rept	7
    .if vector < NR_VECTORS
      .if vector <> FIRST_EXTERNAL_VECTOR
	CFI_ADJUST_CFA_OFFSET -8
      .endif
1:	pushq_cfi $(~vector+0x80)	/* Note: always in signed byte range */
      .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6
	jmp 2f
      .endif
      .previous
	.quad 1b
      .section .entry.text
vector=vector+1
    .endif
  .endr
2:	jmp common_interrupt  // 调到common_interrput处理
.endr
	CFI_ENDPROC
END(irq_entries_start)

通过跳到common_interrupt来进行中断的处理。

common_interrupt:
	XCPT_FRAME
	ASM_CLAC
	addq $-0x80,(%rsp)		/* Adjust vector to [-256,-1] range */
	interrupt do_IRQ      // 调用do_IRQ()处理中断
	/* 0(%rsp): old_rsp-ARGOFFSET */
ret_from_intr:
	DISABLE_INTERRUPTS(CLBR_NONE)
	TRACE_IRQS_OFF
	decl PER_CPU_VAR(irq_count)

	/* Restore saved previous stack */
	popq %rsi
	CFI_DEF_CFA rsi,SS+8-RBP	/* reg/off reset after def_cfa_expr */
	leaq ARGOFFSET-RBP(%rsi), %rsp
	CFI_DEF_CFA_REGISTER	rsp
	CFI_ADJUST_CFA_OFFSET	RBP-ARGOFFSET

exit_intr:
	GET_THREAD_INFO(%rcx)
	testl $3,CS-ARGOFFSET(%rsp)
	je retint_kernel

	/* Interrupt came from user space */
	/*
	 * Has a correct top of stack, but a partial stack frame
	 * %rcx: thread info. Interrupts off.
	 */
retint_with_reschedule:
	movl $_TIF_WORK_MASK,%edi
retint_check:
	LOCKDEP_SYS_EXIT_IRQ
	movl TI_flags(%rcx),%edx
	andl %edi,%edx
	CFI_REMEMBER_STATE
	jnz  retint_careful

retint_swapgs:		/* return to user-space */
	/*
	 * The iretq could re-enable interrupts:
	 */
	DISABLE_INTERRUPTS(CLBR_ANY)
	TRACE_IRQS_IRETQ
	SWAPGS
	jmp restore_args

retint_restore_args:	/* return to kernel space */
	DISABLE_INTERRUPTS(CLBR_ANY)
	/*
	 * The iretq could re-enable interrupts:
	 */
	TRACE_IRQS_IRETQ
restore_args:
	RESTORE_ARGS 1,8,1

irq_return:
	INTERRUPT_RETURN
	_ASM_EXTABLE(irq_return, bad_iret)

主要的处理函数位于do_IRQ函数,在中断处理完成之后,无论是返回用户空间还是内核态都会检查一下是否需要进程需求调度。

unsigned int __irq_entry do_IRQ(struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);  // 获取旧的中断

	/* high bit used in ret_from_ code  */
	unsigned vector = ~regs->orig_ax;
	unsigned irq;

	irq_enter();   // irq进入中断 禁止中断等
	exit_idle();

	irq = __this_cpu_read(vector_irq[vector]);  // 读取中断的irq

	if (!handle_irq(irq, regs)) {   // 处理中断
		ack_APIC_irq();    // 中断回复 

		if (printk_ratelimit())
			pr_emerg("%s: %d.%d No irq handler for vector (irq %d)\n",
				__func__, smp_processor_id(), vector, irq);
	}

	irq_exit();    // irq离开

	set_irq_regs(old_regs);   // 恢复旧的现场
	return 1;
}

Handle_irq处理流程如下。

bool handle_irq(unsigned irq, struct pt_regs *regs)
{
	struct irq_desc *desc;

	stack_overflow_check(regs);

	desc = irq_to_desc(irq);
	if (unlikely(!desc))
		return false;

	generic_handle_irq_desc(irq, desc);
	return true;
}

...
  
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
	desc->handle_irq(irq, desc);  // 调用注册的handle去处理
}

至此,中断的处理流程就是这样调用handle处理。

总结

本文只是简单的查看了硬件中断的处理与注册流程,大致框架流程就是如上所示。由于本人才疏学浅,如有错误请批评指正。

相关文章:

  • 【统计学习|书籍阅读】第一章 统计学习方法概论 p1-p24
  • 操作系统——计算机系统概述补充
  • pytorch 实现一个最简单的 GAN:用mnist数据集生成新图像
  • 七雄争霸武将技能搭配
  • 利用Python进行数据分析-Numpy入门基础知识
  • QML的Popup遇到的坑
  • 解数独 视频讲解 c++
  • kubernetes 网络
  • 运维流程化和标准化
  • LeetCode104. 二叉树的最大深度和N叉树的最大深度
  • Games104 引擎工具链笔记
  • 如何梳理当天的事情?
  • 【历年NeurIPS论文下载】一文了解NeurIPS国际顶会(含NeurIPS2022)
  • 《JVM学习笔记》字节码基础
  • Java 学习 --SpringBoot 常用注解详解
  • 【391天】每日项目总结系列128(2018.03.03)
  • Django 博客开发教程 8 - 博客文章详情页
  • fetch 从初识到应用
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • JS数组方法汇总
  • Mysql5.6主从复制
  • passportjs 源码分析
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 嵌入式文件系统
  • 思考 CSS 架构
  • 微信端页面使用-webkit-box和绝对定位时,元素上移的问题
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • 携程小程序初体验
  • 运行时添加log4j2的appender
  • 《码出高效》学习笔记与书中错误记录
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • linux 淘宝开源监控工具tsar
  • 第二十章:异步和文件I/O.(二十三)
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • ​iOS实时查看App运行日志
  • #if 1...#endif
  • #NOIP 2014# day.2 T2 寻找道路
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (vue)页面文件上传获取:action地址
  • (二)【Jmeter】专栏实战项目靶场drupal部署
  • (四)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)shell中括号的特殊用法 linux if多条件判断
  • (转)Sublime Text3配置Lua运行环境
  • (转)大道至简,职场上做人做事做管理
  • (转)视频码率,帧率和分辨率的联系与区别
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • .net core webapi 部署iis_一键部署VS插件:让.NET开发者更幸福
  • .Net Core 中间件验签
  • .net mvc 获取url中controller和action
  • .NET 解决重复提交问题
  • .net 验证控件和javaScript的冲突问题