中断子系统分析(一)

2024-06-03 16:32
文章标签 中断 系统分析

本文主要是介绍中断子系统分析(一),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自:

http://blog.chinaunix.net/uid-25047042-id-3647544.html

写的非常清楚。


中断子系统分析(一) 2013-05-03 20:28:25

分类: LINUX

中断子系统是linux内核非常重要的一个子系统,中断是系统对设备进行管理最常见也可以说是最有效的一种方式,在各种设备驱动中我们都可以看到设备向系统申请注册中断的地方。在我们平常使用中断的时候,大多都是根据设备的中断号向系统申请注册(request_irq)一个中断及中断处理函数,然后实现好中断处理函数就差不多了。我们也都知道,当中断产生时,系统就会去执行中断处理函数,从而实现对设备的有效管理。那么,系统是如何实现这些的呢?

异常向量表的创建:

    首先我们来了解当中断产生时,系统是如何响应中断的。

    中断是作为异常的一种被CPU响应处理的。系统在启动过程中作建立一个异常向量表,当异常产生的时候,CPU就会通过异常向量表获得相应的异常处理地址,从而对相应的异常进行处理。在ARM架构中,异常向量的基地址有两个:0x00000000和0xffff0000。这个地址是可以通过设置而确定的。在我使用的系统当中它是0xffff0000。当异常产生时,CPU就会跳转到异常向量的基地址处,那么这个地方有什么呢?

    在系统启动过程中,会定义一个异常向量表,并将表拷贝到0xffff0000这个地址。在traps.c中有void __init early_trap_init(void *vectors_base) {

unsigned long vectors = (unsigned long)vectors_base;

extern char __stubs_start[], __stubs_end[];

extern char __vectors_start[], __vectors_end[];

extern char __kuser_helper_start[], __kuser_helper_end[];

int kuser_sz = __kuser_helper_end - __kuser_helper_start;

vectors_page = vectors_base;

/*

 * Copy the vectors, stubs and kuser helpers (in entry-armv.S)

 * into the vector page, mapped at 0xffff0000, and ensure these

 * are visible to the instruction stream.

 */

memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

……..

};

    其中这个vectors_base就是异常向量基地址,它的值就是0xffff0000。其中最重要的两行代码就是:

memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);

        memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);

    它分别将__vectors_start__stubs_start的内容拷贝到vectorsvectors+200处,下面我们来看看__vectors_start__stubs_start,到底有什么。

在entry-armv.s中有:

__vectors_start:

 ARM( swi SYS_ERROR0 )

 THUMB( svc #0 )

 THUMB( nop )

W(b) vector_und + stubs_offset

W(ldr) pc, .LCvswi + stubs_offset

W(b) vector_pabt + stubs_offset

W(b) vector_dabt + stubs_offset

W(b) vector_addrexcptn + stubs_offset

W(b) vector_irq + stubs_offset

W(b) vector_fiq + stubs_offset

.globl __vectors_end

__vectors_end:



.globl __stubs_start

__stubs_start:

/*

 * Interrupt dispatcher

 */

vector_stub irq, IRQ_MODE, 4

.long __irq_usr @  0  (USR_26 / USR_32)

.long __irq_invalid @  1  (FIQ_26 / FIQ_32)

.long __irq_invalid @  2  (IRQ_26 / IRQ_32)

.long __irq_svc @  3  (SVC_26 / SVC_32)

.long __irq_invalid @  4

.long __irq_invalid @  5

.long __irq_invalid @  6

.long __irq_invalid @  7

.long __irq_invalid @  8

.long __irq_invalid @  9

.long __irq_invalid @  a

.long __irq_invalid @  b

.long __irq_invalid @  c

.long __irq_invalid @  d

.long __irq_invalid @  e

.long __irq_invalid @  f

…….

.globl __stubs_end

__stubs_end:

    __vectors_start:是定义的异常向量表,它被拷贝到0xffff0000处,当异常产生时,CPU跳转0xffff0000,也就是开始执行__vectors_start之后的代码。根据代码可知,不同的异常,它会跳转到不同的异常处理地址,如当中断产生时它会跳转到:

W(b) vector_irq + stubs_offset

    __stubs_start是处理跳转的部分。我们把vector_stub irq, IRQ_MODE, 4 宏展开有:

.macro vector_stub, name, mode, correction=0

.align 5

vector_\name:

.if \correction

sub lr, lr, #\correction

.endif

@

@ Save r0, lr_<exception> (parent PC) and spsr_<exception>

@ (parent CPSR)

@

stmia sp, {r0, lr} @ save r0, lr

mrs lr, spsr

str lr, [sp, #8] @ save spsr

@

@ Prepare for SVC32 mode.  IRQs remain disabled.

@

mrs r0, cpsr

eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)

msr spsr_cxsf, r0

@

@ the branch table must immediately follow this code

@

and lr, lr, #0x0f

 THUMB( adr r0, 1f )

 THUMB( ldr lr, [r0, lr, lsl #2] )

mov r0, sp

 ARM( ldr lr, [pc, lr, lsl #2] )

movs pc, lr @ branch to handler in SVC mode

ENDPROC(vector_\name)

    因此我们可以看到,_stubs_start中定义的vector_stub irq, IRQ_MODE, 4就是前面异常向量表中要使用到的vector_irq。它保存了一些寄存器的值,并根据CPU的状态跳转到__irq_usr 或者__irq_svn。

    这里又有一个问题,为什么它不直接跳转vector_irq而且是跳转到vector_irq + stubs_offset,我们来看一下stubs_offset的定义:

    .equ stubs_offset, __vectors_start + 0x200 - __stubs_start

    前面有说了,__vectors_start被拷贝到0xffff0000处,而__stubs_start被拷贝到0xffff0000+0x200处,画个图来看一下:

    如图所示,当中断异常产生时,CPU执行到W(b) vector_irq + stubs_offset时它会从当前位置(pc)跳转到vertor_irq处来处理中断异常,那么从pc到vertor_irq的距离offset是多少呢,就是0x200加上vertor_irq到__stub_start距离再减去vectors_start到pc的位置,也就是offset=0x200+vertor_irq-__stub_start-(pc-vertors_start)

=vertors_start+0x200-__stub_start+(vertor_irq-pc)

    CPU执行的当前地址是pc,跳转距离是offset,那么目标地址就是pc+offset了,那么目标地址也就是pc+(vertor_irq-pc)+vertors_start+0x200-__stub_start也就是vertor_irq+stubs_offset了。

跳转到vertor_irq之后根据CPU状态会去执行__irq_usr或者__irq_svc,分析__irq_svc:

__irq_svc:

svc_entry

irq_handler

……

这里重点分析irq_handler:

.macro irq_handler

#ifdef CONFIG_MULTI_IRQ_HANDLER

ldr r1, =handle_arch_irq

mov r0, sp

adr lr, BSYM(9997f)

ldr pc, [r1]

#else

arch_irq_handler_default

#endif

9997:

.endm

从这里我们看到了,当执行到irq_handler的时候它就会去执行handler_arch_irq(默认是执行arch_irq_handler_default,但在我的系统当中它有定义CONFIG_MULTI_IRQ_HANDLER)。

到这里汇编部分执行完了,开始步入C代码当中。

执行中断处理函数:

根据上面异常向量表的分析可知,当中断产生后系统会去执行handler_arch_irq。那么在handler_arch_irq会做些什么呢?

系统启动里,会执行main.c的start_kernel()来对系统进行初始化,在start_kernel的setup_arch(&command_line)中有:

……

#ifdef CONFIG_MULTI_IRQ_HANDLER

handle_arch_irq = mdesc->handle_irq;

#endif

……

也就是handle_arch_irq保存的是mdesc->handle_irq;

在mdesc中保存是系统体系架构相关的一些初始化配置:

MACHIE_START(EMEDKB,“PXA988”)

.map_io=mmp_map_io,

.init_early=pxa988_init_early,

.init_irq=pxa988_init_irq,

.timer=&pxa988_timer,

.reserve=pxa988_reserve,

.handle_irq=gic_handle_irq,

.init_machine=emeidkb_init_common,

.restart=mmp_arch_reset,

MACHINE_END

这个结构比较重要,每个型号的主板都会有对应的一个这样的结构。

从这里可以看到,handle_arch_irq保存的是machine_desc结构的handle_irq指针:

asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)

{

u32 irqstat, irqnr;

struct gic_chip_data *gic = &gic_data[0];

void __iomem *cpu_base = gic_data_cpu_base(gic);

do {

irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);

irqnr = irqstat & ~0x1c00;

if (likely(irqnr > 15 && irqnr < 1021)) {

irqnr = irq_find_mapping(gic->domain, irqnr);

handle_IRQ(irqnr, regs);

continue;

}

if (irqnr < 16) {

writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);

#ifdef CONFIG_SMP

handle_IPI(irqnr, regs);

#endif

continue;

}

break;

} while (1);

}

在这个函数里头,它首先从中断控制器中读取触发中断的硬件中断号,再通过一个映射表获取到软件中断号,然后进入handle_IRQ();再经过generic_handle_irq()->generic_handle_irq_desc():

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)

{

desc->handle_irq(irq, desc);

}

开头有讲,我们在设备驱动当中会根据中断号申请注册中断和中断处理函数,而中断产生后,在这里系统会根据中断号来执行相应的中断处理函数,也就是desc->handle_irq(irq,desc);到这里,当一次中断产生后,系统根据中断异常从异常向量表中获取并执行中断异常流程,然后根据handler_arch_irq关联到对应平台的中断控制器,从中断控制器中得到中断号,然后执行对应的中断处理函数的大致流程就是这样了。

这篇关于中断子系统分析(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1027512

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

FreeRTOS学习笔记(四)Freertos的中断管理及临界保护

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、Cortex-M 中断管理1.1 中断优先级分组1.2 相关寄存器1.3 相关宏定义1.4 FreeRTOS 开关中断 二、临界段及其保护2.1 taskENTER_CRITICAL( ) 和 taskEXIT_CRITICAL( )2.2 taskENTER_CRITICAL_FROM_ISR( )

Cortex-A7:ARM官方推荐的嵌套中断实现机制

0 参考资料 ARM Cortex-A(armV7)编程手册V4.0.pdf ARM体系结构与编程第2版 1 前言 Cortex-M系列内核MCU中断硬件原生支持嵌套中断,开发者不需要为了实现嵌套中断而进行额外的工作。但在Cortex-A7中,硬件原生是不支持嵌套中断的,这从Cortex-A7中断向量表中仅为外部中断设置了一个中断向量可以看出。本文介绍ARM官方推荐使用的嵌套中断实现机

外部中断的边缘触发和电平触发

MCS-51单片机中的边缘触发是指当输入引脚电平由高到低发生跳变时,才引起中断。而电平触发是指只要外部引脚为低电平就引起中断。         在电平触发方式下,当外部引脚的低电平在中断服务返回前没有被拉高时(即撤除中断请求状态),会引起反复的不需要的中断,造成程序执行的错误。这类中断方式下,需要在中断服务程序中设置指令,清除外部中断的低电平状态,使之变为高电平。

【Java编程思想】线程的基本协作机制 与 线程的中断

wait/notify Java在Object类中定义了一些线程协作的基本方法,wait和notify public final void wait() throws InterruptedException;public final native void wait(long timeout) throws InterruptedException; 一个带时间参数,单位是毫秒,表示最

基于IMX6ULL的Cortex-A中断原理讲解,以及编写其中断向量表

首先借助STM32我们需要了解中断系统是如何构成的         会有一个中断源,也就是能够向CPU发出中断请求的设备或事件。中断源不分硬件和软件,也就是产生中断信号,就会执行中断服务函数         但是CPU是如何知道中断源产生后就找到对应的中断服务函数呢,这个时候就要引入中断向量表,它的主要功能是描述中断对应的中断服务函数,每个中断源都有一个唯一的中断号(也称向量号),

【银河麒麟高级服务器操作系统实例】虚拟化平台系统服务中断现象分析及处理建议

服务器环境以及配置 【机型】虚机 处理器: Kunpeng-920 内存: 40G 【内核版本】 4.19.90-23.8.v2101.ky10.aarch64 【OS镜像版本】 银河麒麟操作系统 Kylin-Server-10-SP1-Release-Build20-20210518-arm64 【第三方软件】 智能运维系统、mysql数据集群 现象描述 环境描

s3c2440---中断控制器

一、概述 S3C2440A 中的中断控制器接受来自 60 个中断源的请求。提供这些中断源的是内部外设,如 DMA 控制器、 UART、IIC 等等。 在这些中断源中,UARTn、AC97 和 EINTn 中断对于中断控制器而言是“或”关系。 当从内部外设和外部中断请求引脚收到多个中断请求时,中断控制器在仲裁步骤后请求 ARM920T 内核的 FIQ 或 IRQ。 仲裁步骤由硬件优先级逻辑决定

Linux 一个简单的中断信号实现

1.查看手册,学习中断处理图 流程:(次级源->开关)到 源挂起 到 开关  到 处理优先级 到 中断挂起标志 到 CPSR里面的开关(图中未展现) 最后cpu处理 此次我们先使用k1按键实现中断,即是eint8 2.此次仅涉及一个中断挂起,步骤较简单,有的寄存器未涉及处理。 寄存器挂起后,通过写1清除对应位( 硬件设计逻辑: 中断标志位通常由硬件自动设置为 1,表示中断发生。

江协科技STM32学习- P11 中断系统,EXTI外部中断

🚀write in front🚀   🔎大家好,我是黄桃罐头,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​  💬本系列哔哩哔哩江科大STM32的视频为主以及自己的总结梳理📚  🚀Projeet source code🚀    💾工程代码放在了本人的Gitee仓库:iPickCan (iPickCan