资讯详情

linux内核中断子系统Ⅱ----通用(gic)框架处理

1 概述

2数据结构分析

3 流程分析

3.1 中断注册

3.1.1设备硬件中断号到Linux irq中断号的映射

3.1.2request_irq/request_threaded_irq

3.2 中断处理

3.3 线程中断处理

3.4 总结


1 概述

【原创】Linux关于底层硬件的中断控制器和驱动分析GIC驱动,以及Arch-Specific本文将研究一般的中断处理过程,属于硬件无关层。当然,我建议你看看上一篇文章。

本文将回答两个问题:

  1. 用户如何使用中断?(中断注册)?
  2. 当外设触发中断信号时,如何调用中断信号?handler的(中断处理)?

2数据结构分析

让我们来看看总数据结构。核心是围绕它struct irq_desc来展开:

  • Linux内核的中断处理围绕中断描述符结构struct irq_desc内核提供了两种中断描述符组织形式:

    1. 打开CONFIG_SPARSE_IRQ宏(中断号不连续)radix-tree来组织,用户在初始化时进行动态分配,然后再插入radix-tree中;
    2. 关闭CONFIG_SPARSE_IRQ宏(连续中断编号),中断描述符以数组的形式组织,并已分配;
    3. 无论哪种形式,最终都可以通过linux irq找到相应的中断描述符;
  • 图左侧灰色部分主要初始化设置在中断控制器驱动中,包括各结构中函数指针的指向等struct irq_chip用于中断控制器的硬件操作,struct irq_domain对应于中断控制器,完成的工作是硬件中断到Linux irq的映射;

  • 图上灰色部分中断了描述符的创建(这是指CONFIG_SPARSE_IRQ),在获取设备中断信息的过程中,使设备树中的中断能够与具体的中断描述一致irq_desc匹配;

  • 图中剩余部分设置在设备注册申请中断过程中,如struct irqactionhandler该设置用于指向我们设备驱动程序中的中断处理函数;

中断处理主要包括以下功能模块:

  1. 硬件中断号到Linux irq中断号映射,创建良好的irq_desc中断描述符;
  2. 中断注册时,先获取设备中断号,根据中断号找到相应的中断号irq_desc,添加设备的中断处理函数irq_desc中;
  3. 当设备触发中断信号时,根据硬件中断号获得Linux irq找到对应的中断号irq_desc,最终调用到设备的中断处理函数;

以上描述相对简单,过程更详细,往下看。

3 流程分析

3.1 中断注册

这一次,让我们以问题的方式展开: 首先,让我们回答第一个问题:用户如何使用中断?

  1. 熟悉设备驱动的学生应该知道,他们经常调用驱动程序request_irq()接口或者request_threaded_irq()接口注册设备中断处理函数;
  2. request_irq()/request_threaded_irq需要在接口中使用irq,也就是中断号,那么这个中断号是从哪里来的呢?Linux irq,它是如何映射到特定硬件设备的中断号的?

3.1.1设备硬件中断号到Linux irq中断号的映射

  • 设备树上的硬件设备中断信息device tree在系统启动过程中,这些信息被加载到内存中并进行分析;
  • 通常用于驱动platform_get_irqirq_of_parse_and_map根据设备树的信息创建映射关系(硬件中断号到linux irq中断号映射);
  • 【原创】Linux中断子系统(1)-中断控制器和驱动分析struct irq_domain因此,它被用来完成映射工作irq_create_fwspec_mapping在接口中,会先找到匹配的irq domain,再去回调该irq domain中函数集,通常irq domain在中断控制器驱动中初始化ARM GICv2例如,最终回调到gic_irq_domain_hierarchy_ops中的函数;
  • 如果映射已经创建,可以直接返回linux irq中断号,否则需要irq_domain_alloc_irqs创建映射关系;
  • irq_domain_alloc_irqs完成两项工作:
    1. 针对linux irq创建一个中断号irq_desc中断描述符;
    2. 调用domain->ops->allocARM GICv2驱动中对应gic_irq_domain_alloc函数,这个函数很关键,下面介绍一下;

gic_irq_domain_alloc函数如下:

  • gic_irq_domain_translate:负责分析设备树中描述的中断号和中断触发类型(边缘触发、电平触发等。
  • gic_irq_domain_map:中断硬件号和linux当中断号绑定到一个结构时,就完成了映射。此外,它还被绑定irq_desc结构中的其他字段,最重要的是设置了irq_desc->handle_irq函数指针,这最终是中断响应时向上执行的入口,这是关键,下面将提到中断处理过程;
  • 根据硬件中断号的范围设置irq_desc->handle_irq共享中断入口为指针handle_fasteoi_irq,私有中断入口为handle_percpu_devid_irq
  • 上述函数执行完成后,完成了两大工作:

    1. 硬件中断号与Linux中断号完成映射,并为Linux中断号创建了irq_desc中断描述符;
    2. 数据结构的绑定及初始化,关键的地方是设置了中断处理往上执行的入口;

    request_irq/request_threaded_irq

    设备驱动中,获取到了irq中断号后,通常就会采用request_irq/request_threaded_irq来注册中断,其中request_irq用于注册普通处理的中断,request_threaded_irq用于注册线程化处理的中断;

    在讲具体的注册流程前,先看一下主要的中断标志位:

    #define IRQF_SHARED		0x00000080              //多个设备共享一个中断号,需要外设硬件支持
    #define IRQF_PROBE_SHARED	0x00000100              //中断处理程序允许sharing mismatch发生
    #define __IRQF_TIMER		0x00000200              //时钟中断
    #define IRQF_PERCPU		0x00000400              //属于特定CPU的中断
    #define IRQF_NOBALANCING	0x00000800              //禁止在CPU之间进行中断均衡处理
    #define IRQF_IRQPOLL		0x00001000              //中断被用作轮训
    #define IRQF_ONESHOT		0x00002000              //一次性触发的中断,不能嵌套,1)在硬件中断处理完成后才能打开中断;2)在中断线程化中保持关闭状态,直到该中断源上的所有thread_fn函数都执行完
    #define IRQF_NO_SUSPEND		0x00004000              //系统休眠唤醒操作中,不关闭该中断
    #define IRQF_FORCE_RESUME	0x00008000              //系统唤醒过程中必须强制打开该中断
    #define IRQF_NO_THREAD		0x00010000              //禁止中断线程化
    #define IRQF_EARLY_RESUME	0x00020000              //系统唤醒过程中在syscore阶段resume,而不用等到设备resume阶段
    #define IRQF_COND_SUSPEND	0x00040000              //与NO_SUSPEND的用户共享中断时,执行本设备的中断处理函数
    

    request_irq也是调用request_threaded_irq,只是在传参的时候,线程处理函数thread_fn函数设置成NULL;

    由于在硬件中断号和Linux中断号完成映射后,irq_desc已经创建好,可以通过irq_to_desc接口去获取对应的irq_desc

    创建irqaction,并初始化该结构体中的各个字段,其中包括传入的中断处理函数赋值给对应的字段;

    __setup_irq用于完成中断的相关设置,包括中断线程化的处理:

    1. 中断线程化用于减少系统关中断的时间,增强系统的实时性;
    2. ARM64默认开启了
    3. 中断线程化会为每个中断都创建一个内核线程,如果中断进行共享,对应irqaction将连接成链表,每个irqaction都有thread_mask位图字段,当所有共享中断都处理完成后才能unmask中断,解除中断屏蔽;

    3.2 中断处理

    当完成中断的注册后,所有结构的组织关系都已经建立好,剩下的工作就是当信号来临时,进行中断的处理工作。

    来回顾一下【原创】Linux中断子系统(一)-中断控制器及驱动分析中的Arch-specific处理流程:

    • 中断收到之后,首先会跳转到异常向量表的入口处,进而逐级进行回调处理,最终调用到generic_handle_irq来进行中断处理。

    generic_handle_irq处理如下图:

    • generic_handle_irq函数最终会调用到desc->handle_irq(),这个也就是对应到上文中在建立映射关系的过程中,调用irq_domain_set_info函数,设置好了函数指针,也就是handle_fasteoi_irqhandle_percpu_devid_irq
    • handle_fasteoi_irq:处理共享中断,并且遍历irqaction链表,逐个调用action->handler()函数,这个函数正是设备驱动程序调用request_irq/request_threaded_irq接口注册的中断处理函数,此外如果中断线程化处理的话,还会调用__irq_wake_thread()唤醒内核线程;
    • handle_percpu_devid_irq:处理per-CPU中断处理,在这个过程中会分别调用中断控制器的处理函数进行硬件操作,该函数调用action->handler()来进行中断处理;

    [    6.217119] #### /home/users/quan01.wang/dev_xj3_1029/kernel/drivers/net/ethernet/hobot/hobot_eth.c xj3_poll 4913,hw_eth_rx_idx_test=6,current->comm=swapper/0
    [    6.217130] CPU: 0 PID: 0 Comm: swapper/0 Tainted: P                4.14.87 #17
    [    6.217137] Hardware name: Hobot J3 SOC MP DVB (DT)
    [    6.217143] Call trace:
    [    6.217154] [<ffffff8008008ae0>] dump_backtrace+0x0/0x3f0
    [    6.217162] [<ffffff8008008ee4>] show_stack+0x14/0x20
    [    6.217165] [<ffffff80087a8b98>] dump_stack+0x98/0xb8
    [    6.217169] [<ffffff80084a4484>] xj3_poll+0xbec/0xc00
    [    6.217174] [<ffffff800863ceb8>] net_rx_action+0x358/0x3f0
    [    6.217177] [<ffffff8008001008>] __do_softirq+0x110/0x1fc
    [    6.217181] [<ffffff8008026070>] irq_exit+0xd8/0x108
    [    6.217184] [<ffffff8008066c30>] __handle_domain_irq+0x60/0xb8
    [    6.217187] [<ffffff8008000d58>] gic_handle_irq+0x58/0xb0
    [    6.217190] Exception stack(0xffffff8008ab3de0 to 0xffffff8008ab3f20)
    [    6.217194] 3de0: 0000000000000020 000000407648b000 0000000000000000 ffffff8008a9e810
    [    6.217198] 3e00: ffffff8008a9e7d8 ffffff8008ab3f10 000000407648b000 000000407648b000
    [    6.217202] 3e20: 00000000000000ff ffffffc07ef2bd80 0000000000000001 ffffff80087d8648
    [    6.217205] 3e40: 0000000000000000 0000000000000001 0000000000000001 0000000000000000
    [    6.217209] 3e60: ffffff8008024f20 0000007f9bbb0600 0000000000000060 ffffff8008a9c000
    [    6.217214] 3e80: ffffff8008ab8a2c ffffff8008ab89a0 ffffff8008ac2580 ffffff8008ac2580
    [    6.217217] 3ea0: ffffff8008a6e028 0000000034d5d915 0000000004001e9c 0000000000000400
    [    6.217221] 3ec0: 0000000000c20018 ffffff8008ab3f20 ffffff80080048ac ffffff8008ab3f20
    [    6.217225] 3ee0: ffffff80080048b0 0000000060000145 0000000034d5d915 0000000004001e9c
    [    6.217229] 3f00: ffffffffffffffff ffffff8008059004 ffffff8008ab3f20 ffffff80080048b0
    [    6.217232] [<ffffff8008002830>] el1_irq+0xb0/0x140
    [    6.217236] [<ffffff80080048b0>] arch_cpu_idle+0x10/0x18
    [    6.217241] [<ffffff8008059038>] do_idle+0xd8/0x118
    [    6.217245] [<ffffff8008059158>] cpu_startup_entry+0x20/0x28
    [    6.217249] [<ffffff80087b957c>] rest_init+0xac/0xb8
    [    6.217252] [<ffffff8008a20b38>] start_kernel+0x340/0x354
    [    6.217276] arp: #### /home/users/quan01.wang/dev_xj3_1029/kernel/net/ipv4/arp.c arp_process 825,dev_type=1,ar_op=256,ar_pro=8
    [    6.218784] #### /home/users/quan01.wang/dev_xj3_1029/kernel/drivers/net/ethernet/hobot/hobot_eth.c xj3_interrupt 3149,hw_eth_xj3_interrupt_idx_test=8,current->comm=swapper/0
    [    6.218788] CPU: 0 PID: 0 Comm: swapper/0 Tainted: P                4.14.87 #17
    [    6.218789] Hardware name: Hobot J3 SOC MP DVB (DT)
    [    6.218791] Call trace:
    [    6.218795] [<ffffff8008008ae0>] dump_backtrace+0x0/0x3f0
    [    6.218800] [<ffffff8008008ee4>] show_stack+0x14/0x20
    [    6.218803] [<ffffff80087a8b98>] dump_stack+0x98/0xb8
    [    6.218807] [<ffffff80084a1028>] xj3_interrupt+0xa78/0xa88
    [    6.218811] [<ffffff80080674a4>] __handle_irq_event_percpu+0x8c/0xe8
    [    6.218814] [<ffffff8008067540>] handle_irq_event_percpu+0x40/0x88
    [    6.218818] [<ffffff80080675cc>] handle_irq_event+0x44/0x78
    [    6.218821] [<ffffff800806b184>] handle_fasteoi_irq+0x9c/0x190
    [    6.218826] [<ffffff80080665fc>] generic_handle_irq+0x24/0x38
    [    6.218829] [<ffffff8008066c2c>] __handle_domain_irq+0x5c/0xb8
    [    6.218833] [<ffffff8008000d58>] gic_handle_irq+0x58/0xb0
    [    6.218835] Exception stack(0xffffffc07ef335e0 to 0xffffffc07ef33720)
    [    6.218839] 35e0: ffffffffffffffff 00000000fffee11a 0000000000000000 0000000000000030
    [    6.218843] 3600: 0000000000000000 0000000000000011 0000000000000002 0000000078bd8a81
    [    6.218847] 3620: ffffffbf00000000 ffffff800c989000 ffffffc07c447000 ffffff80080168c0
    [    6.218851] 3640: 0000000000000001 0000000000000000 00000000c0a8016f 0000000000000000
    [    6.218855] 3660: 0000000000000000 ffffff800887dda0 0000000000000000 ffffff8008aaa000
    [    6.218859] 3680: 000000407648b000 ffffff8008ab6000 0000000000000128 0000000000000001
    [    6.218863] 36a0: ffffffc07ef337a0 ffffffc07ef35000 ffffff8008a9c000 ffffffc07a8fc9b8
    [    6.218867] 36c0: 0000000000000004 ffffffc07ef33720 ffffff800863ceb8 ffffffc07ef33720
    [    6.218871] 36e0: ffffff800863cca4 0000000020000145 ffffffc07a8fc9b8 0000000000000000
    [    6.218875] 3700: 0000007fffffffff ffffffc07a8fc000 ffffffc07ef33720 ffffff800863cca4
    [    6.218878] [<ffffff8008002830>] el1_irq+0xb0/0x140
    [    6.218882] [<ffffff800863cca4>] net_rx_action+0x144/0x3f0
    [    6.218886] [<ffffff8008001008>] __do_softirq+0x110/0x1fc
    [    6.218889] [<ffffff8008026070>] irq_exit+0xd8/0x108
    [    6.218893] [<ffffff8008066c30>] __handle_domain_irq+0x60/0xb8
    [    6.218896] [<ffffff8008000d58>] gic_handle_irq+0x58/0xb0
    [    6.218898] Exception stack(0xffffff8008ab3de0 to 0xffffff8008ab3f20)
    [    6.218902] 3de0: 0000000000000020 000000407648b000 0000000000000000 ffffff8008a9e810
    [    6.218906] 3e00: ffffff8008a9e7d8 ffffff8008ab3f10 000000407648b000 000000407648b000
    [    6.218910] 3e20: 00000000000000ff ffffffc07ef2bd80 0000000000000001 ffffff80087d8648
    [    6.218914] 3e40: 0000000000000000 0000000000000001 0000000000000001 0000000000000000
    [    6.218918] 3e60: ffffff8008024f20 0000007f9bbb0600 0000000000000060 ffffff8008a9c000
    [    6.218922] 3e80: ffffff8008ab8a2c ffffff8008ab89a0 ffffff8008ac2580 ffffff8008ac2580
    [    6.218925] 3ea0: ffffff8008a6e028 0000000034d5d915 0000000004001e9c 0000000000000400
    [    6.218929] 3ec0: 0000000000c20018 ffffff8008ab3f20 ffffff80080048ac ffffff8008ab3f20
    [    6.218933] 3ee0: ffffff80080048b0 0000000060000145 0000000034d5d915 0000000004001e9c
    [    6.218937] 3f00: ffffffffffffffff ffffff8008059004 ffffff8008ab3f20 ffffff80080048b0
    [    6.218941] [<ffffff8008002830>] el1_irq+0xb0/0x140
    [    6.218944] [<ffffff80080048b0>] arch_cpu_idle+0x10/0x18
    [    6.218949] [<ffffff8008059038>] do_idle+0xd8/0x118
    [    6.218953] [<ffffff8008059158>] cpu_startup_entry+0x20/0x28
    [    6.218956] [<ffffff80087b957c>] rest_init+0xac/0xb8
    [    6.218960] [<ffffff8008a20b38>] start_kernel+0x340/0x354

    3.3 中断处理线程化

    回顾一下注册中断映射的流程如果使能了中断线程化force_irqthreads,此时调用了 irq_setup_forced_threading函数重新映射 注册 handler 函数为 irq_default_primary_handler(该函数返回唤醒中断线程)。同时重定向了 thread_fn 为 handler 即设备驱动程序调用request_irq/request_threaded_irq接口注册的中断处理函数,thread_fn在线程入口函数中的irq_forced_thread_fn 得到调用;

    static int irq_setup_forced_threading(struct irqaction *new)
    {
    	if (!force_irqthreads)
    		return 0;
        ...
    	new->thread_fn = new->handler;
    	new->handler = irq_default_primary_handler;
    	return 0;
    }
    
    static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)
    {
    	return IRQ_WAKE_THREAD;
    }
    
    irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
    {
    	irqreturn_t retval = IRQ_NONE;
    	unsigned int irq = desc->irq_data.irq;
    	struct irqaction *action;
    
    	record_irq_time(desc);
    
    	for_each_action_of_desc(desc, action) {
    		irqreturn_t res;
    
    		trace_irq_handler_entry(irq, action);
    		res = action->handler(irq, action->dev_id);//irq_default_primary_handler
    		trace_irq_handler_exit(irq, action, res);
    
    		switch (res) {
    		case IRQ_WAKE_THREAD:
                ...
    			__irq_wake_thread(desc, action);
    		case IRQ_HANDLED:
    			*flags |= action->flags;
    			break;
    
    		default:
    			break;
    		}
    
    		retval |= res;
    	}
    
    	return retval;
    }
    
    static irqreturn_t
    irq_forced_thread_fn(struct irq_desc *desc, struct irqaction *action){
    	irqreturn_t ret;
        ...
    	ret = action->thread_fn(action->irq, action->dev_id);
        ...
    }

    下面来看看中断线程化处理后的唤醒流程吧__handle_irq_event_percpu->__irq_wake_thread

    • __handle_irq_event_percpu->__irq_wake_thread将唤醒irq_thread中断内核线程;
    • irq_thread内核线程,将根据是否为强制中断线程化对函数指针handler_fn进行初始化,以便后续进行调用;
    • irq_thread内核线程将while(!irq_wait_for_interrupt)循环进行中断的处理,当满足条件时,执行handler_fn,在该函数中最终调用action->thread_fn,也就是完成了中断的处理;
    • irq_wait_for_interrupt函数,将会判断中断线程的唤醒条件,如果满足了,则将当前任务设置成TASK_RUNNING状态,并返回0,这样就能执行中断的处理,否则就调用schedule()进行调度,让出CPU,并将任务设置成TASK_INTERRUPTIBLE可中断睡眠状态;

    [ 	 4.764399] #### /home/users/quan01.wang/dev_xj3_1029/kernel/drivers/net/ethernet/hobot/hobot_eth.c xj3_interrupt 3149,hw_eth_xj3_interrupt_idx_test=0,current->comm=irq/44-eth%d
    [    4.764407] CPU: 2 PID: 949 Comm: irq/44-eth%d Tainted: P                4.14.87 #17
    [    4.764409] Hardware name: Hobot J3 SOC MP DVB (DT)
    [    4.764411] Call trace:
    [    4.764422] [<ffffff8008008ae0>] dump_backtrace+0x0/0x3f0
    [    4.764427] [<ffffff8008008ee4>] show_stack+0x14/0x20
    [    4.764432] [<ffffff80087a8b98>] dump_stack+0x98/0xb8
    [    4.764438] [<ffffff80084a1028>] xj3_interrupt+0xa78/0xa88
    [    4.764443] [<ffffff80080684b8>] irq_forced_thread_fn+0x38/0xd0
    [    4.764447] [<ffffff80080687b8>] irq_thread+0x120/0x1a8
    [    4.764452] [<ffffff800803e64c>] kthread+0xfc/0x128
    [    4.764456] [<ffffff80080041d0>] ret_from_fork+0x10/0x18
    [    4.764471] st_gmac a5014000.ethernet: Link is Up - 1000/Full
    [    4.764500] IPv6: ADDRCONF(NETDEV_CHANGE): eth0: link becomes ready
    [    5.117079] #### /home/users/quan01.wang/dev_xj3_1029/kernel/drivers/net/ethernet/hobot/hobot_eth.c xj3_interrupt 3149,hw_eth_xj3_interrupt_idx_test=1,current->comm=irq/43-eth-tx
    [    5.117084] CPU: 2 PID: 950 Comm: irq/43-eth-tx Tainted: P                4.14.87 #17
    [    5.117086] Hardware name: Hobot J3 SOC MP DVB (DT)
    [    5.117088] Call trace:
    [    5.117095] [<ffffff8008008ae0>] dump_backtrace+0x0/0x3f0
    [    5.117100] [<ffffff8008008ee4>] show_stack+0x14/0x20
    [    5.117104] [<ffffff80087a8b98>] dump_stack+0x98/0xb8
    [    5.117110] [<ffffff80084a1028>] xj3_interrupt+0xa78/0xa88
    [    5.117115] [<ffffff80080684b8>] irq_forced_thread_fn+0x38/0xd0
    [    5.117119] [<ffffff80080687b8>] irq_thread+0x120/0x1a8
    [    5.117123] [<ffffff800803e64c>] kthread+0xfc/0x128
    [    5.117127] [<ffffff80080041d0>] ret_from_fork+0x10/0x18
    [    5.117137] #### /home/users/quan01.wang/dev_xj3_1029/kernel/drivers/net/ethernet/hobot/hobot_eth.c xj3_poll 4913,hw_eth_rx_idx_test=0,current->comm=irq/43-eth-tx
    [    5.117140] CPU: 2 PID: 950 Comm: irq/43-eth-tx Tainted: P                4.14.87 #17
    [    5.117142] Hardware name: Hobot J3 SOC MP DVB (DT)
    [    5.117143] Call trace:
    [    5.117147] [<ffffff8008008ae0>] dump_backtrace+0x0/0x3f0
    [    5.117151] [<ffffff8008008ee4>] show_stack+0x14/0x20
    [    5.117155] [<ffffff80087a8b98>] dump_stack+0x98/0xb8
    [    5.117159] [<ffffff80084a4484>] xj3_poll+0xbec/0xc00
    [    5.117165] [<ffffff800863ceb8>] net_rx_action+0x358/0x3f0
    [    5.117169] [<ffffff8008001008>] __do_softirq+0x110/0x1fc
    [    5.117173] [<ffffff8008025acc>] do_softirq.part.2+0x5c/0x60
    [    5.117176] [<ffffff8008025b74>] __local_bh_enable_ip+0xa4/0xa8
    [    5.117180] [<ffffff8008068504>] irq_forced_thread_fn+0x84/0xd0
    [    5.117183] [<ffffff80080687b8>] irq_thread+0x120/0x1a8
    [    5.117187] [<ffffff800803e64c>] kthread+0xfc/0x128
    [    5.117191] [<ffffff80080041d0>] ret_from_fork+0x10/0x18
    [    5.405868] st_gmac a5014000.ethernet eth0: Link is Up - 1Gbps/Full - flow control rx/tx

    3.4 总结

    中断的处理,总体来说可以分为两部分来看:

    1. 从上到下:围绕irq_desc中断描述符建立好连接关系,这个过程就包括:中断源信息的解析(设备树),硬件中断号到Linux中断号的映射关系、irq_desc结构的分配及初始化(内部各个结构的组织关系)、中断的注册(填充irq_desc结构,包括handler处理函数)等,总而言之,就是完成静态关系创建,为中断处理做好准备;
    2. 从下到上,当外设触发中断信号时,中断控制器接收到信号并发送到处理器,此时处理器进行异常模式切换,并逐步从处理器架构相关代码逐级回调。如果涉及到中断线程化,则还需要进行中断内核线程的唤醒操作,最终完成中断处理函数的执行。

标签: 连接器gic

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台