1.GIC

GIC,Generic Interrupt Controller。是ARM公司提供的一个通用的中断控制器。主要作用为:接受硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理。

GIC v3中断类别

GICv3定义了以下中断类型:

  • SGI (Software Generated Interrupt):软件触发的中断。软件可以通过写 GICD_SGIR 寄存器来触发一个中断事件,一般用于核间通信,内核中的 IPI:inter-processor interrupts 就是基于 SGI。
  • PPI (Private Peripheral Interrupt):私有外设中断。这是每个核心私有的中断。PPI会送达到指定的CPU上,应用场景有CPU本地时钟。
  • SPI (Shared Peripheral Interrupt):公用的外部设备中断,也定义为共享中断。中断产生后,可以分发到某一个CPU上。比如按键触发一个中断,手机触摸屏触发的中断。
  • LPI (Locality-specific Peripheral Interrupt):LPI 是 GICv3 中的新特性,它们在很多方面与其他类型的中断不同。LPI 始终是基于消息的中断,它们的配置保存在表中而不是寄存器。比如 PCIe 的 MSI/MSI-x 中断。
中断类型 硬件中断号
SGI 0-15
PPI 16-31
SPI 32-1019
reserved
LPI 8192-MAX

GIC v3 组成:

GICv3 控制器由以下三部分组成:

  • Distributor:SPI中断的管理,将中断发送给Redistributor
  1. 打开或关闭每个中断。Distributor对中断的控制分成两个级别。一个是全局中断的控制(GIC_DIST_CTRL)。一旦关闭了全局的中断,那么任何的中断源产生的中断事件都不会被传递到 CPU interface。另外一个级别是对针对各个中断源进行控制(GIC_DIST_ENABLE_CLEAR),关闭某一个中断源会导致该中断事件不会分发到 CPU interface,但不影响其他中断源产生中断事件的分发。
  2. 控制将当前优先级最高的中断事件分发到一个或者一组 CPU interface。当一个中断事件分发到多个 CPU interface 的时候,GIC的内部逻辑应该保证只 assert一个CPU。
  3. 优先级控制
  4. interrupt属性设定。设置每个外设中断的触发方式:电平触发、边缘触发;
  5. interrupt group的设定。设置每个中断的Group,其中 Group0用于安全中断,支持 FIQ 和 IRQ,Group1 用于非安全中断,只支持IRQ
  • Redistributor:SGI,PPI,LPI中断的管理,将中断发送给CPU interface
  1. 启用和禁用 SGI 和 PPI。
  2. 设置 SGI 和 PPI 的优先级
  3. 将每个 PPI 设置为电平触发或边缘触发。
  4. 将每个 SGI 和 PPI 分配给中断组。
  5. 控制SGI 和 PPI 的状态。
  6. 内存中数据结构的基址控制,支持 LPI 的相关中断属性和挂起状态。
  7. 电源管理支持
  • CPU interface:传输中断给 Core
  1. 打开或关闭 CPU interface 向连接的 CPU assert 中断事件。对于 ARM,CPU interface 和 CPU 之间的中断信号线是 nIRQCPU 和 nFIQCPU。如果关闭了中断,即便是 Distributor 分发了一个中断事件到 CPU interface,也不会 assert 指定的 nIRQ 或者 nFIQ 通知 Core
  2. 中断的确认。Core 会向 CPU interface 应答中断(应答当前优先级最高的那个中断),中断一旦被应答,Distributor 就会把该中断的状态从 pending 修改成 active 或者 pending and active(这是和该中断源的信号有关,例如如果是电平中断并且保持了该 asserted 电平,那么就是 pending and active)。ack 了中断之后,CPU interface 就会 deassert nIRQCPU 和 nFIQCPU 信号线。
  3. 中断处理完毕的通知。当 interrupt handler 处理完了一个中断的时候,会向写 CPU interface 的寄存器通知 GIC CPU 已经处理完该中断。做这个动作一方面是通知 Distributor 将中断状态修改为 deactive,另外一方面,CPU interface 会 priority drop,从而允许其他的 pending 的中断向 CPU 提交。
  4. 为 CPU 设置中断优先级掩码。通过 priority mask,可以 mask 掉一些优先级比较低的中断,这些中断不会通知到 CPU。
  5. 设置 CPU 的中断抢占(preemption)策略。
  6. 在多个中断事件同时到来的时候,选择一个优先级最高的通知 CPU。

GICv3 控制器内部模块和各中断类型的关系如下图所示:

中断路由

GICv3 使用 hierarchy 来标识一个具体的 core, 如下图是一个四层的结构(aarch64):

用 <affinity level 3>.<affinity level 2>.<affinity level 1>.<affinity level 0> 的形式组成一个 PE 的路由。每一个 core 的 affnity 值可以通过 MPIDR_EL1 寄存器获取, 每一个 affinity 占用8bit。配置对应 core 的 MPIDR 值,可以将中断路由到该 core 上。

中断状态机

中断处理的状态机如下图:

  • Inactive:无中断状态,即没有 Pending 也没有 Active。
  • Pending:硬件或软件触发了中断,该中断事件已经通过硬件信号通知到 GIC,等待 GIC 分配的那个 CPU 进行处理,在电平触发模式下,产生中断的同时保持 Pending 状态。
  • Active:CPU 已经应答(acknowledge)了该中断请求,并且正在处理中。
  • Active and pending:当一个中断源处于 Active 状态的时候,同一中断源又触发了中断,进入 pending 状态。

中断处理流程

  1. 外设发起中断,发送给 Distributor
  2. Distributor 将该中断,分发给合适的 Redistributor
  3. Redistributor 将中断信息,发送给 CPU interface
  4. CPU interface 产生合适的中断异常给处理器
  5. 处理器接收该异常,并且软件处理该中断

2. GIC驱动

设备树

gic: interrupt-controller@51a00000 {compatible = "arm,gic-v3";reg = <0x0 0x51a00000 0 0x10000>, /* GIC Dist */<0x0 0x51b00000 0 0xC0000>, /* GICR */<0x0 0x52000000 0 0x2000>,  /* GICC */<0x0 0x52010000 0 0x1000>,  /* GICH */<0x0 0x52020000 0 0x20000>; /* GICV */#interrupt-cells = <3>;interrupt-controller;interrupts = <GIC_PPI 9(GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_HIGH)>;interrupt-parent = <&gic>;
};
  • compatible:用于匹配GICv3驱动
  • reg :GIC的物理基地址,分别对应GICD,GICR,GICC…
  • #interrupt-cells:这是一个中断控制器节点的属性。它声明了该中断控制器的中断指示符(interrupts)中 cell 的个数
  • interrupt-controller: 表示该节点是一个中断控制器
  • interrupts:分别代表中断类型,中断号,中断类型, PPI中断亲和, 保留字段

关于设备数的各个字段含义,详细可以参考 Documentation/devicetree/bindings 下的对应信息。

初始化

1. irq chip driver 的声明:

IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);

定义 IRQCHIP_DECLARE 之后,相应的内容会保存到 __irqchip_of_table 里边:

#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)#define OF_DECLARE_2(table, name, compat, fn) \ _OF_DECLARE(table, name, compat, fn, of_init_fn_2)#define _OF_DECLARE(table, name, compat, fn, fn_type)            \ static const struct of_device_id __of_table_##name        \ __used __section(__##table##_of_table)            \ = { .compatible = compat,                \ .data = (fn == (fn_type)NULL) ? fn : fn  }

__irqchip_of_table 在链接脚本 vmlinux.lds 里,被放到了 __irqchip_begin 和 __irqchip_of_end 之间,该段用于存放中断控制器信息:

#ifdef CONFIG_IRQCHIP#define IRQCHIP_OF_MATCH_TABLE()                    \. = ALIGN(8);                           \VMLINUX_SYMBOL(__irqchip_begin) = .;                \*(__irqchip_of_table)                       \*(__irqchip_of_end)
#endif

在内核启动初始化中断的函数中,of_irq_init 函数会去查找设备节点信息,该函数的传入参数就是 __irqchip_of_table 段,由于 IRQCHIP_DECLARE 已经将信息填充好了,of_irq_init 函数会根据 “arm,gic-v3” 去查找对应的设备节点,并获取设备的信息。or_irq_init 函数中,最终会回调 IRQCHIP_DECLARE 声明的回调函数,也就是 gic_of_init,而这个函数就是 GIC 驱动的初始化入口

2. gic_of_init 流程:

static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{void __iomem *dist_base;struct redist_region *rdist_regs;u64 redist_stride;u32 nr_redist_regions;int err, i;/* 映射 GICD 的寄存器地址空间。*/dist_base = of_iomap(node, 0);if (!dist_base) {pr_err("%pOF: unable to map gic dist registers\n", node);return -ENXIO;}
/* 验证 GICD 的版本是 GICv3 还是 GICv4
(主要通过读GICD_PIDR2寄存器bit[7:4].
0x1代表GICv1, 0x2代表GICv2…以此类推)。*/err = gic_validate_dist_version(dist_base);if (err) {pr_err("%pOF: no distributor detected, giving up\n", node);goto out_unmap_dist;}
/* 通过 DTS 读取 redistributor-regions 的值。*/if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))nr_redist_regions = 1;rdist_regs = kcalloc(nr_redist_regions, sizeof(*rdist_regs),GFP_KERNEL);if (!rdist_regs) {err = -ENOMEM;goto out_unmap_dist;}/* 为一个 GICR 域分配基地址*/for (i = 0; i < nr_redist_regions; i++) {struct resource res;int ret;ret = of_address_to_resource(node, 1 + i, &res);rdist_regs[i].redist_base = of_iomap(node, 1 + i);if (ret || !rdist_regs[i].redist_base) {pr_err("%pOF: couldn't map region %d\n", node, i);err = -ENODEV;goto out_unmap_rdist;}rdist_regs[i].phys_base = res.start;}
/* 通过 DTS 读取 redistributor-stride 的值*/if (of_property_read_u64(node, "redistributor-stride", &redist_stride))redist_stride = 0;gic_enable_of_quirks(node, gic_quirks, &gic_data);err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,redist_stride, &node->fwnode);if (err)goto out_unmap_rdist;
/* 设置一组 PPI 的亲和性。*/gic_populate_ppi_partitions(node);if (static_branch_likely(&supports_deactivate_key))gic_of_setup_kvm_info(node);return 0;out_unmap_rdist:for (i = 0; i < nr_redist_regions; i++)if (rdist_regs[i].redist_base)iounmap(rdist_regs[i].redist_base);kfree(rdist_regs);
out_unmap_dist:iounmap(dist_base);return err;
}

3. gic_init_bases 流程:

static int __init gic_init_bases(void __iomem *dist_base,struct redist_region *rdist_regs,u32 nr_redist_regions,u64 redist_stride,struct fwnode_handle *handle)
{u32 typer;int err;if (!is_hyp_mode_available())static_branch_disable(&supports_deactivate_key);if (static_branch_likely(&supports_deactivate_key))pr_info("GIC: Using split EOI/Deactivate mode\n");gic_data.fwnode = handle;gic_data.dist_base = dist_base;gic_data.redist_regions = rdist_regs;gic_data.nr_redist_regions = nr_redist_regions;gic_data.redist_stride = redist_stride;/* 确认支持 SPI 中断号最大的值为多少。*/typer = readl_relaxed(gic_data.dist_base + GICD_TYPER);gic_data.rdists.gicd_typer = typer;gic_enable_quirks(readl_relaxed(gic_data.dist_base + GICD_IIDR),gic_quirks, &gic_data);pr_info("%d SPIs implemented\n", GIC_LINE_NR - 32);pr_info("%d Extended SPIs implemented\n", GIC_ESPI_NR);/** ThunderX1 explodes on reading GICD_TYPER2, in violation of the* architecture spec (which says that reserved registers are RES0).*/if (!(gic_data.flags & FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539))gic_data.rdists.gicd_typer2 = readl_relaxed(gic_data.dist_base + GICD_TYPER2);
/* 向系统中注册一个 irq domain 的数据结构,irq_domain 主要作用是将硬件中断号映射到 irq number */gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops,&gic_data);gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));gic_data.rdists.has_rvpeid = true;gic_data.rdists.has_vlpis = true;gic_data.rdists.has_direct_lpi = true;gic_data.rdists.has_vpend_valid_dirty = true;if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) {err = -ENOMEM;goto out_free;}irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);gic_data.has_rss = !!(typer & GICD_TYPER_RSS);pr_info("Distributor has %sRange Selector support\n",gic_data.has_rss ? "" : "no ");if (typer & GICD_TYPER_MBIS) {err = mbi_init(handle, gic_data.domain);if (err)pr_err("Failed to initialize MBIs\n");}
/* 设定 arch 相关的 irq handler */set_handle_irq(gic_handle_irq);gic_update_rdist_properties();/* 初始化 Distributor。 */gic_dist_init();
/* 初始化 CPU interface。 */gic_cpu_init();
/* 设置 SMP 核间交互的回调函数,用于 IPI,回到函数为 gic_raise_softir。 */gic_smp_init();
/* 初始化 GIC 电源管理。 */ gic_cpu_pm_init();gic_syscore_init();/* 初始化 ITS。 */if (gic_dist_supports_lpis()) {its_init(handle, &gic_data.rdists, gic_data.domain);its_cpu_init();} else {if (IS_ENABLED(CONFIG_ARM_GIC_V2M))gicv2m_init(handle, gic_data.domain);}gic_enable_nmi_support();return 0;out_free:if (gic_data.domain)irq_domain_remove(gic_data.domain);free_percpu(gic_data.rdists.rdist);return err;
}

3.中断的映射

irq_domain 的引入相当于一个中断控制器就是一个 irq_domain。这样一来所有的中断控制器就会出现级联的布局。利用树状的结构可以充分的利用 irq 数目,而且每一个 irq_domain 区域可以自己去管理自己 interrupt 的特性。

每一个中断控制器对应多个中断号, 而硬件中断号在不同的中断控制器上是会重复编码的, 这时仅仅用硬中断号已经不能唯一标识一个外设中断,因此 linux kernel 提供了一个虚拟中断号的概念

外设的驱动创建硬中断和虚拟中断号的映射关系:

设备的驱动在初始化的时候可以调用 irq_of_parse_and_map 这个接口函数进行该 device node 中和中断相关的内容的解析,并建立映射关系

最后,我们可以通过 /proc/interrupts 下的值来看下它们的关系:

4.中断的注册

设备驱动中,获取到了 irq 中断号后,通常就会采用 request_irq/request_threaded_irq 来注册中断,其中 request_irq 用于注册普通处理的中断。request_threaded_irq 用于注册线程化处理的中断,线程化中断的主要目的把中断上下文的任务迁移到线程中,减少系统关中断的时间,增强系统的实时性

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
{return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}#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的用户共享中断时,执行本设备的中断处理函数

5.中断的处理

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

假设当前在 EL0 运行一个应用程序,触发了一个 EL0 的 irq中断,则处理器先会跳到 arm64 对应的异常向量表:

/** Exception vectors.*/.pushsection ".entry.text", "ax".align  11
SYM_CODE_START(vectors)......kernel_ventry   1, sync                         // el1 下的同步异常,例如指令执行异常、缺页中断等kernel_ventry   1, irq                          // el1 下的异步异常,硬件中断。1代表异常等级kernel_ventry   1, fiq_invalid                  // FIQ EL1hkernel_ventry   1, error                        // Error EL1hkernel_ventry   0, sync                         // el0 下的同步异常,例如指令执行异常、缺页中断(跳转地址或者取地址)、系统调用等kernel_ventry   0, irq                          // el0 下的异步异常,硬件中断。0代表异常等级kernel_ventry   0, fiq_invalid                  // FIQ 64-bit EL0kernel_ventry   0, error                        // Error 64-bit EL0......
#endif
SYM_CODE_END(vectors)

arm64 的异常向量表 vectors 中设置了各种异常的入口。kernel_ventry 展开后,可以看到有效的异常入口有两个同步异常 el0_sync,el1_sync 和两个异步异常 el0_irq,el1_irq,其他异常入口暂时都 invalid。中断属于异步异常。

通过上图,我们可以看出中断的处理分为三个部分,保护现场,中断处理,恢复现场。其中 el0_irq 和 el1_irq 的具体实现略有不同,但处理流程大致是相同的。

保护现场
kernel_entry 0,其中 kernel_entry 是一个宏,此宏会将 CPU 寄存器按照 pt_regs 结构体的定义将第一现场保存到栈上

.macro  kernel_entry, el, regsize = 64
.if     \regsize == 32
mov     w0, w0                          // zero upper 32 bits of x0
.endif
stp     x0, x1, [sp, #16 * 0]
stp     x2, x3, [sp, #16 * 1]
stp     x4, x5, [sp, #16 * 2]
stp     x6, x7, [sp, #16 * 3]
stp     x8, x9, [sp, #16 * 4]
stp     x10, x11, [sp, #16 * 5]
stp     x12, x13, [sp, #16 * 6]
stp     x14, x15, [sp, #16 * 7]
stp     x16, x17, [sp, #16 * 8]
stp     x18, x19, [sp, #16 * 9]
stp     x20, x21, [sp, #16 * 10]
stp     x22, x23, [sp, #16 * 11]
stp     x24, x25, [sp, #16 * 12]
stp     x26, x27, [sp, #16 * 13]
stp     x28, x29, [sp, #16 * 14].if     \el == 0
clear_gp_regs
mrs     x21, sp_el0
ldr_this_cpu    tsk, __entry_task, x20
msr     sp_el0, tsk

中断处理:跳入中断处理 irq_handler。

/** Interrupt handling.*/.macro  irq_handlerldr_l   x1, handle_arch_irqmov     x0, spirq_stack_entry      //进入中断栈blr     x1           //执行 handle_arch_irqirq_stack_exit       //退出中断栈.endm

中断栈用来保存中断的上下文,中断发生和退出的时候调用 irq_stack_entry 和 irq_stack_exit 来进入和退出中断栈。中断栈是在内核启动时就创建好的,内核在启动过程中会去为每个 CPU 创建一个 per cpu 的中断栈:start_kernel->init_IRQ->init_irq_stacks

handle_arch_irq 指针指向 gic_handle_irq 函数

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{u32 irqnr;do {/* 读取中断控制器的寄存器GICC_IAR,并获取 hwirq */irqnr = gic_read_iar();                                     /* 外设触发的中断。硬件中断号 0-15 表示 SGI 类型的中断,15-1020 表示外设中断(SPI或PPI类型),8192-MAX 表示 LPI 类型的中断 */if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {int err;if (static_key_true(&supports_deactivate))gic_write_eoir(irqnr);elseisb();
/* 中断控制器中断处理的主体 */err = handle_domain_irq(gic_data.domain, irqnr, regs); if (err) {WARN_ONCE(true, "Unexpected interrupt received!\n");if (static_key_true(&supports_deactivate)) {if (irqnr < 8192)gic_write_dir(irqnr);} else {gic_write_eoir(irqnr);}}continue;}
/* 软件触发的中断 */if (irqnr < 16) {                                      gic_write_eoir(irqnr);if (static_key_true(&supports_deactivate))gic_write_dir(irqnr);
#ifdef CONFIG_SMP/** Unlike GICv2, we don't need an smp_rmb() here.* The control dependency from gic_read_iar to* the ISB in gic_write_eoir is enough to ensure* that any shared data read by handle_IPI will* be read after the ACK.*/
/* 核间交互触发的中断 */    handle_IPI(irqnr, regs);
#elseWARN_ONCE(true, "Unexpected SGI received!\n");
#endifcontinue;}} while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}

中断控制器中断处理的主体,如下所示:

nt __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,bool lookup, struct pt_regs *regs)
{struct pt_regs *old_regs = set_irq_regs(regs);        unsigned int irq = hwirq;int ret = 0;/* 进入中断上下文 */irq_enter();                       /* 根据 hwirq 去查找 linux 中断号 */
#ifdef CONFIG_IRQ_DOMAINif (lookup)irq = irq_find_mapping(domain, hwirq);
#endif/** Some hardware gives randomly wrong interrupts.  Rather* than crashing, do something sensible.*/if (unlikely(!irq || irq >= nr_irqs)) {ack_bad_irq(irq);ret = -EINVAL;} else {
/* 通过中断号找到全局中断描述符数组 irq_desc[NR_IRQS] 中的一项,然后调用 generic_handle_irq_desc,执行该 irq 号注册的 action */ generic_handle_irq(irq);  /*  */}
/* 退出中断上下文 */irq_exit();                            set_irq_regs(old_regs);return ret;
}

恢复现场:

SYM_CODE_START_LOCAL(ret_to_user)disable_daif                      //DAIF分别为PSTAT中的四个异常屏蔽标志位,此处屏蔽这4中异常gic_prio_kentry_setup tmp=x3
#ifdef CONFIG_TRACE_IRQFLAGSbl      trace_hardirqs_off
#endifldr     x19, [tsk, #TSK_TI_FLAGS]  //获取 thread_info 中的flags变量的值and     x2, x19, #_TIF_WORK_MASKcbnz    x2, work_pending
finish_ret_to_user:user_enter_irqoff/* Ignore asynchronous tag check faults in the uaccess routines */clear_mte_async_tcfenable_step_tsk x19, x2
#ifdef CONFIG_GCC_PLUGIN_STACKLEAKbl      stackleak_erase
#endifkernel_exit 0                      //恢复 pt_regs 中的寄存器上下文

ARM GIC 与Linux kernel的中断子系统(4)GIC和中断处理相关推荐

  1. linux kernel的中断子系统之(三):IRQ number和中断描述符【转】

    转自:http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html 一.前言 本文主要围绕IRQ number和中断描述符(interr ...

  2. linux kernel的中断子系统之(三):IRQ number和中断描述符

    原文地址 http://www.wowotech.net/linux_kenrel/interrupt_descriptor.html linux kernel的中断子系统之(三):IRQ numbe ...

  3. Linux kernel的中断子系统之(九):tasklet

    返回目录:<ARM-Linux中断系统>. 总结: 二介绍了tasklet存在的意义. 三介绍了通过tasklet_struct来抽想一个tasklet,每个CPU维护一个tasklet链 ...

  4. Linux kernel的中断子系统之(二):IRQ Domain介绍

    一.概述 在linux kernel中,我们使用下面两个ID来标识一个来自外设的中断: 1.IRQ number.CPU需要为每一个外设中断编号,我们称之IRQ Number.这个IRQ number ...

  5. Linux中断子系统(一)中断控制器GIC架构

    Linux中断子系统(一)中断控制器GIC架构 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一)中 ...

  6. Linux中断子系统(二)中断控制器GIC驱动分析

    Linux中断子系统(二)中断控制器GIC驱动分析 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一 ...

  7. Linux中断子系统(三)之GIC中断处理过程

    Linux中断子系统(三)之GIC中断处理过程 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: Linux中断子系统(一)中 ...

  8. 嵌入式Linux驱动笔记(二十七)------中断子系统框架分析

    你好!这里是风筝的博客, 欢迎和我一起交流. 中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行. 从硬 ...

  9. 漫画-Linux中断子系统综述

    1.中断引发的面试教训 2.什么是中断? 中断: (英语:Interrupt)指当出现需要时,CPU暂时停止当前程序的执行转而执行处理新情况的程序和执行过程. 即在程序运行过程中,系统出现了一个必须由 ...

最新文章

  1. 数据库防火墙onefirewall,如何防范黑客物理删除?避免携程事件再次上演
  2. 阿里云性能测试服务 PTS 新面貌 - 压测协议、施压能力全新升级
  3. J2EE互联网产品打造
  4. python爬虫之urllib库详解
  5. GNN上用到的Tasks,Dataset and Benchmark
  6. 23、jQuery九类选择器/jQuery常用Method-API/jQuery常用Event-API
  7. C++ Primer 笔记——嵌套类 局部类
  8. Filter(过滤器)Listene(监听器)笔记
  9. 学习RAID磁盘阵列
  10. 如何把视频和音频合并?手把手教你合并
  11. 基于MATLAB的机器人学、机器视觉与控制
  12. electron 获取电脑mac地址遇到的坑
  13. 智能世代的工业/嵌入式网络与I/O桥接芯片解决方案
  14. dpi、dp、sp、px、mm之间的关系
  15. 服装行业施行ERP体系的首要好处是什么?
  16. 王菲离婚后首发微博谈及与李亚鹏离婚原因
  17. 出货量第一的三星为何在手机AI芯片竞争中落后华为和苹果?
  18. 时代互联报道:骗子盯上网银用户,认清银行网站域名,谨防上当
  19. Linux安装gbk字体,Linux安装GBK/GB2312程序显示乱码的五种解决方法 - 软件教程网
  20. 运放稳定性连载11:电容性负载稳定性:RISO、高增益及 CF、噪声增益(2)

热门文章

  1. 张恭庆院士:数学的意义(最全最牛的解释)
  2. 小学带计算机2000的检讨书,【精选】小学学生检讨书模板集合10篇
  3. 使用electron中的webView标签
  4. 模拟演讲者视图_PPT演讲心慌慌?用演讲者视图一边看一边说
  5. css实现的卡片式渐变色卡html页面前端源码
  6. 解决WIN7 无法登陆MicroSoft Edge问题
  7. 栈与队列及其应用 - 1.算术表达式求值
  8. 授课点评:如何通俗地讲授概念?什么是图像?
  9. 关于fluent中亚松弛因子under-ralexation factors的思考
  10. 网络营销团队打造 如何生化出让同行垂涎三尺的网络营销团队