linux 内核 中断(内核中断与外核中断区别)
各位老铁们,大家好,今天由我来为大家分享linux 内核 中断,以及内核中断与外核中断区别的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!
详解linux内核-缺页中断处理
现代处理器大部分都配备MMU,除一些小型嵌入式设备。MMU实现虚拟地址与物理地址间的转换,利用它我们能利用更多内存空间,得益于程序的局部性原理,暂时不用的数据存放磁盘。当访问到时会触发缺页中断,将所需数据从磁盘加载至内存。由此,我们得以运行程序大小超过内存容量的程序和打开超过内存容量的文件。现代处理器通过分段分页机制完成虚拟地址至物理地址的转换,一般支持二级页表或四级页表。
缺页中断处理的流程如下:
1.硬件中断内核,保存程序计数器及其他指令状态信息至特殊CPU寄存器。
2.启动汇编程序,保存通用寄存器及其他易丢失信息,防止操作系统破坏。
3.操作系统发现缺页中断,识别需要的虚拟页面。通常硬件寄存器包含信息,若无则检索程序计数器,分析当前指令,确定操作。
4.确定缺页虚拟地址,检查地址有效性与权限一致性。若不一致,向进程发送信号或终止进程。若有效且无保护错误,系统检查空闲页框。若无,则执行页面置换算法淘汰页面。
5.若选中的页框脏,则将该页写回磁盘,进行上下文切换,挂起产生缺页中断的进程,直到写入磁盘完成。写入的页框标记为忙,避免其他进程占用。
6.一旦页框干净,操作系统获取页面在磁盘的地址,通过磁盘操作将其装入。此时,产生缺页中断的进程仍挂起,如果有其他可运行的用户进程,选择该进程运行。
7.当磁盘中断发生,表示页面已加载至内存,页表更新,页框标记为正常状态。
8.恢复缺页中断前的状态,程序计数器指向中断前指令。
9.调度产生缺页中断的进程,操作系统返回调用汇编例程。
10.汇编例程恢复寄存器状态,返回用户空间,仿佛缺页中断未发生。
Linux内核对缺页异常处理复杂,基本思想与上述流程相似。首先关注struct vm_area_struct,虚拟内存区域,如.text段、数据段,每个都对应vma,在加载二进制文件时创建这些vma。
页面分类为:
匿名页:数据段、堆、栈、mmap匿名映射的页。
文件页:可执行文件代码段映射的页、普通文件映射的页。
缺页异常分类:内核态缺页异常与用户态缺页异常。内核态异常包括vmalloc区异常,vmalloc出现异常易于处理,只需同步页表即可,内核引用用户空间地址引发的异常,如用户态地址非法或页面已交换至磁盘。内核bug也是内核态缺页异常之一。内核态缺页异常频率低,因为内核态数据不会交换至磁盘。用户态缺页异常频繁,因用户态数据频繁写入交换区与文件,且进程创建时伴随着大量缺页异常。
Linux基本处理流程如下:
内核处理缺页异常的主要函数为do_page_fault。do_page_fault流程图如下。
在do_page_fault内调用的关键函数为find_vma,查找缺页地址对应的vma,vma在进程创建时分配。内核通过链表与红黑树对vma排序。
在栈增长方向向下时,若缺页地址不在任何vma闭区间内,需要扩展栈所属vma起始地址,expand_stack函数如下。
当发生正常用户空间的缺页异常,需要调用handle_mm_fault处理页表。获取缺页地址对应的页表项指针后,进入handle_pte_fault,根据条件调用不同的页框交换函数,具体代码如下。
用户态缺页页框经过以上处理,将从磁盘加载至内存,重新建立映射。
内核态缺页异常处理涉及函数,内核态分为页表未更新至最新,需要进行页表同步,调用vmalloc_fault。内核态缺页异常还有一情况,内核访问用户空间地址,若用户空间页表对应的页框已被交换至磁盘,或为错误地址,则使用fixup_exception处理。内核编译时会预留异常表空间。i386链接脚本内存布局包含以下内容:
__start__ex_table和 __stop__ex_table指定__ex_table段的起始与结束地址。*(__ex_table)将所有输入目标文件的__ex_table段组合成一个__ex_table。在内核调用copy_from_user时发生缺页异常的处理过程如下。
经过以上分析,深入理解了Linux内核的缺页异常处理。当然,还有未分析的细枝末节,但整体处理逻辑已清晰。
Linux内核中断之中断调用流程
本文基于 RockPI 4A单板Linux4.4内核介绍中断调用流程。
ARMv8包括两种运行状态:AArch64和AArch32。
AArch64中不再使用AArch32中的7种特权模式,而是提出了Exception Levels的概念,包括:
1)EL0:用于用户态程序,权限最低
2)EL1:给内核使用,权限稍高
3)EL2:虚拟化相关,权限更高
4)EL3:安全相关,权限最高
Linux内核中一般只使用EL0和EL1。
AArch64异常向量表中的异常包括:
1)Synchronous exception(同步异常)
2)SError
3)IRQ
4)FIQ
注:SError、IRQ和FIQ属于异步异常。
在Linux内核中,在 arch/arm64/kernel/entry.S文件中定义了异常向量表,内容如下:
选取 el1_irq()函数介绍Linux内核中断的调用流程。
文件: arch/arm64/kernel/entry.S,调用流程如下:
1、handle_irq()初始化
在 DTS解析阶段完成 handle_irq()函数的初始化,流程如下:
gic_irq_domain_map()函数中完成了 handle_irq()函数的赋值,具体执行如下:
2、handle_irq()实现
以共享外设中断 SPI的中断处理函数 handle_fasteoi_irq()为例,继续跟踪中断的执行过程。
handle_irq_event_percpu()函数会调用已经注册的中断处理函数,同时唤醒 irq_thread线程。
3、中断处理线程
在使用 request_threaded_irq()函数申请中断时,会创建一个 irq_thread线程,调用流程如下:
irq_thread线程平时在睡眠状态,等待 handle_irq_event_percpu()函数唤醒,进一步执行已注册的中断处理线程函数。
使用 DRM框架中 HDMI中断验证中断调用流程。
文件: drivers\gpu\drm\bridge\synopsys\dw-hdmi.c
在中断处理函数 dw_hdmi_hardirq()和中断处理线程函数 dw_hdmi_irq中增加 dump_stack()调用(注:仅限于调试验证)。
插入 HDMI线,系统启动后,显示中断调用流程的日志如下:
和
Linux内核中断上半部处理程序
硬件中断--异步中断中断本质上是一种电信号,由硬件设备发出,用于通知处理器特定事件。不同设备对应不同中断,每个中断通过唯一的数字标识,称为中断请求(IRQ)线。处理器内部对其编号,也称为中断号。
异常--同步中断异常不同于中断,产生时必须考虑与处理器时钟同步。异常常称为同步中断,是处理器执行时由于编程失误而导致的错误指令(如除0),或者执行期间出现特殊情况(如缺页),必须靠内核来处理时,处理器就会产生一个异常。我们通常所说的是中断,是指由硬件产生的异步中断。
软中断工作方式类似于异步中断,区别是它是通过软件引起的中断,异步中断是由硬件引起的。
中断处理程序中断处理程序(interrupt handler)或中断服务例程(interrupt service routine,ISR)响应一个特定中断时执行的函数。设备产生的每个中断,都有一个中断处理程序关联。一个设备可能产生多种不同的中断,那么该设备就可以对应多个中断处理程序。相应地,设备驱动程序就需要准备多个这样的函数。
Linux中断处理程序特点 Linux中,中断处理程序看起来像普通C函数,按特定类型声明,以便内核能以标准的方式传递处理程序的信息。中断处理程序与其他内核函数的区别:中断处理程序是被内核调用来响应中断的,而它们运行于被称为中断上下文的特殊上下文中。由于中断随时可能产生,因此中断处理程序必须随时、尽快执行,这样才能尽快响应中断,同时恢复中断代码的执行。
上下半部的对比一般把中断处理切分为2个部分或两半:1)上半部(top half)中断处理程序是上半部,接收到一个中断,就立即开始执行,但只做严格时限的工作。如对接收中断进行应答或复位硬件,这些工作都是在所有中断被禁止的情况下完成的。2)下半部(bottom half)中断处理程序中,能被允许稍后完成的工作会推迟到下半部。Linux提供下半部的各种机制。
编写中断处理程序一个典型的中断处理程序声明:其签名必须与request_irq()中参数handler所要求函数类型一致。参数说明:返回值:返回值是一个特殊类型:irqerturn_t。实际是int类型,为了与早期内核兼容,因为2.6以前实际是void类型。可能返回2个特殊值:IRQ_NONE和IRQ_HANDLED。当中断处理程序检测到一个中断,但该中断并非注册处理函数时指定的产生源时,返回IRQ_NONE;当中断处理程序被正确调用,而且确实是它所对应的设备产生的中断时,返回IRQ_HANDLED。返回值也可以用宏IRQ_RETVAL(x):x为非0,宏返回IRQ_HANDLED;x为0,返回IRQ_NONE。定义说明:中断处理程序通常标记为static,因为从来不会在其他文件被直接调用。可重入性 Linux中中断处理程序无需重入。当一个给定的中断处理程序正在执行时,相应的中断线在所有处理器上都会被屏蔽,以防在同一个中断线上接收另一个新的中断。也就是说,同一个中断线,同一个中断处理程序,在执行完毕之前不可能同时在2个处理器上执行,加上中断处理程序不能休眠,因此无需考虑可重入性。
中断控制 Linux内核提供一组接口用于操作机器上的中断状态:能用于禁止当前处理器的中断系统,屏蔽整个机器的一条中断线。例程与体系结构相关,位于。
禁止内核抢占:preempt_disable、preempt_enable等;禁止和激活中断用于禁止当前处理器上的本地中断,随后由激活:。由于flags包含具体体系结构的数据,即中断系统的状态,因此,flags不能传递给另一个函数,必须驻留在同一栈帧中。基于此,local_irq_save和restore的调用必须在同一个函数中。
中断系统的状态如何了解中断系统的状态,如中断当前是禁止的还是激活的?或者当前是否正处于中断上下文的执行状态中?本小节提供这方面回答。中 irqs_disable()如果本地处理器上的中断系统被禁止,则返回非0;否则,返回0。中 in_interrupt()如果内核处于中断上下文,则返回非0;如果在进程上下文,返回0。in_irq()如果当前正在执行中断处理程序,则返回非0;否则,返回0.
中断处理机制的实现中断处理系统的实现依赖于体系结构。中断的入口点是:硬件产生了中断,处理器开始跳转到对应的一个唯一位置,在栈中保存这个号,并存放当前寄存器的值(被中断任务),开始处理中断。中断产生到内核处理中断的步骤:1)硬件产生中断。2)处理器跳转到中断向量表对应的中断处理程序,栈中保存中断号及打断任务保存线程。3)内核调用do_IRQ()。之后,大多数中断处理代码用C编写。do_IRQ()提取中断号,对应x86代码:计算出中断号后,do_IRQ()对所接收的中断进行应答,禁止这条线上的中断传递。这些操作通过调用mask_and_ack_8259A()来完成。4)处理中断如果中断线上已安装中断处理程序,就调用handle_IRQ_event()处理;如果没有,就直接调用ret_from_intr(),返回内核运行中断的代码。x86上,handle_IRQ_event():5)调用ret_from_intr()类似于初始入口代码,用汇编编写。用于检查重新调度释放正在挂起(意味着设置了need_resched)。如果重新调度正在挂起,并且内核正在返回用户空间(i.e.中断了用户进程),那么schedule()被调用。如果内核正在返回内核空间(i.e.中断了内核本身),只有preempt_count(抢占计数器)为0时,schedule()才会被调用(否则,抢占内核便是不安全的)。schedule()返回后,或者如果没有挂起的工作,那么原来的寄存器被恢复,内核恢复到曾经中断的点。
/proc/interrupts查看中断统计信息 procfs是一个虚拟文件系统,只存在于内核内存,一般安装于/proc目录下。在procfs中读写文件都要调用内核函数,这些函数模拟从真实文件中读或写。/proc/interrupts文件存放系统中与中断相关的统计信息。
中断处理程序实例一个来自RTC驱动程序的中断处理程序,可在drivers/char/rtc.c中找到。RTC用于设置系统时钟,提供alarm或周期性定时器。