Linux内核中断内部人员

电脑杂谈  发布时间:2020-05-12 17:24:20  来源:网络整理

linux io中断编程_编程io控制_linux io编程

故障

可能恢复的错误

同步

返回当前命令

终止

不可恢复的错误

同步

不会返回

X86体系结构中的每个中断都分配有唯一的编号或向量(8位无符号整数). 未屏蔽的中断和异常向量是固定的,而可屏蔽的中断向量可以通过对中断控制器进行编程来更改.

中断描述符表(IDT)是与每个中断或异常向量关联的系统表. 每个向量在表中存储相应中断或异常处理程序的入口地址. 内核必须在允许中断发生之前(即在系统初始化期间)将IDT表的初始化地址加载到idtr寄存器中,然后初始化表中的每个项目.

在实模式下,IDT由BIOS程序初始化和使用. 但是,一旦Linux开始接手,IDT被移至ARM的另一个区域,并进行了第二次初始化,因为Linux不使用任何BIOS程序,而是使用其自己的特殊中断服务程序(例程)(中断服务程序, ISR). 中断和异常处理程序与常规C函数非常相似

linux io中断编程_linux io编程_编程io控制

有三个主要数据结构,其中包含与IRQ相关的所有信息: hw_interrupt_typelinux io中断编程,irq_desc_t和irqaction. 图3解释了它们之间的关系.

IRQ结构之间的关系

IRQ结构之间的关系

在X86系统中,对于两种不同类型的中断控制器8259A和I / O APIC,hw_interrupt_type结构被分配了不同的值. 具体差异请参见表2.

表2: 8259A和I / O APIC之间的区别PIC 8259AI / O APIC

static struct hw_interrupt_type i8259A_irq_type = {“ XT-PIC”,startup_8259A_irq,shutdown_8259A_irq,enable_8259A_irq,disable_8259A_irq,mask_and_ack_8259A,end_8259A_irq,NULL};

静态结构hw_interrupt_type ioapic_edge_type = {.typename =“ IO-APIC-edge” ,. startup = startup_edge_ioapic,.shutdown = shutdown_edge_ioapic,.enable = enable_edge_ioapic,.disable = disable_edge_ioapic,.ack = ack_edge_ioapic,io .set_affinity = set_ioapic_affinity,};静态结构hw_interrupt_type ioapic_level_type = {.typename =“ IO-APIC级”,. startup = startup_level_ioapic,.shutdown = shutdown_level_ioapic,.enable = enable_level_ioapic,.disable = disable_level_ioapic,mask_io_ic end = end_level_ioapic,.set_affinity = set_ioapic_affinity,};

在中断初始化阶段,调用类型为hw_interrupt_type的变量来初始化irq_desc_t结构的句柄成员. 在早期的系统中,使用级联的8259A,因此将使用i8259A_irq_type进行初始化,对于SMP系统,将使用ioapic_edge_type或ioapic_level_type来初始化handle变量.

对于每个设备,中断处理程序都是静态(声明为static类型的全局变量)或动态(调用request_irq函数)向Linux内核注册的. 无论采用哪种注册方法,都将声明或分配irqaction结构(处理程序指向中断服务例程)linux io中断编程,然后调用setup_irq()函数将irq_desc_t和irqaction关联起来.

发生中断时,可通过中断描述符表IDT获得中断服务程序入口地址. 对于32≤i≤255(i≠128)之间的中断向量,将执行push $ i-256,jmp common_interrupt指令. 然后将调用do_IRQ()函数,将中断向量用作irq_desc []结构的下标,获取指向操作的指针,然后调用处理程序指向的中断服务例程.

从上面的描述中,我们可以轻松地看到整个中断过程,如图4所示:

编程io控制_linux io中断编程_linux io编程

X86中断流

X86中断流

本文的一位作者对2.6.10中断系统进行了方案分析. 有兴趣的读者可以联系作者以获得相关信息.

在SMP体系结构中,我们可以通过调用系统调用和一组相关的宏,将一个或多个进程绑定到一个或多个要运行的处理器来设置CPU亲和力. 中断在这方面并不弱,并且具有相同的特征. 中断相似性是指将一个或多个中断源绑定到特定的CPU以便运行. 中断亲和力最初是由Ingo Molnar设计和实现的.

在/ proc / irq目录中,对于已注册中断处理程序的硬件设备,此目录中将有一个以中断编号命名的目录IRQ#,并且在IRQ#目录中将有一个smp_affinity文件(SMP体系结构). 文件仅可用),它是CPU的位掩码,可用于设置中断的相似性. 默认值为0xffffffff,表示该中断已发送到所有CPU进行处理. 如果中断控制器不支持IRQ关联性,则不能更改此默认值,并且不能关闭所有CPU位掩码,即不能将其设置为0x0.

我们以网卡(eth1,中断号为44)为例,在具有8个CPU的服务器上设置网卡中断的相关性(以下数据来自内核源文档\ IRQ-affinity.txt) :

[root@moon 44]# cat smp_affinity
ffffffff
[root@moon 44]# echo 0f > smp_affinity
[root@moon 44]# cat smp_affinity
0000000f
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
...
--- hell ping statistics ---
6029 packets transmitted, 6027 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.1/0.4 ms
[root@moon 44]# cat /proc/interrupts | grep 44:
 44:   0   1785   1785   1783   1783   1   1   0   IO-APIC-level   eth1
[root@moon 44]# echo f0 > smp_affinity
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
..
--- hell ping statistics ---
2779 packets transmitted, 2777 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.5/585.4 ms
[root@moon 44]# cat /proc/interrupts | grep 44:
 44:  1068  1785  1785  1784   1784   1069   1070   1069   IO-APIC-level  eth1
[root@moon 44]#

在上面的示例中,我们首先只允许中断CPU0〜3上的网卡,然后运行ping程序. 不难发现在CPU4〜7上没有处理网卡的中断. 然后仅在CPU4〜7上处理网卡的中断,CPU0〜3不处理网卡的任何中断. 运行ping程序后,再次检查/ proc / interrupts文件时,不难发现CPU4〜7上的中断数量已显着增加. 但是,CPU0〜3上的中断数没有太大变化.

在讨论中断相似性的原理之前,让我们首先了解I / O APIC的组成.

I / O APIC由一组24条IRQ线,一个24项的中断重定向表,一个可编程寄存器以及一个通过APIC总线发送和接收APIC信息的信息单元组成. 中断亲和力与中断重定向表密切相关. 中断重定向表中的每一项都可以单独编程以指示中断向量和优先级,目标处理器以及选择处理器的方式.

从表2中不难发现8259A和APIC中断控制器之间的最大区别是hw_interrupt_type类型变量的最后一项. 对于8259A类型,set_affinity设置为NULL,对于SMP APIC类型,set_affinity分配值set_ioapic_affinity.

linux io编程_linux io中断编程_编程io控制

在系统初始化期间,对于SMP架构,将调用setup_IO_APIC_irqs()函数来初始化I / O APIC芯片,并填充芯片中的中断重定向表中的24个条目. 在系统启动期间,所有CPU都执行setup_local_APIC()函数来完成本地APIC初始化. 触发中断时,将相应的中断重定向表中的值转换为一条消息,然后将该消息通过APIC总线发送到一个或多个本地APIC单元,以便可以将中断立即传递到特定的CPU ,或一组CPU或所有CPU,以实现中断关联.

当我们通过cat命令将CPU掩码写入smp_affinity文件时,此时的调用路由映射为: write()-> sys_write()-> vfs_write()-> proc_file_write()-> irq_affinity_write_proc() -> set_affinity()-> set_ioapic_affinity()-> set_ioapic_affinity_irq()-> io_apic_write();其中以中断号和CPU掩码作为参数调用set_ioapic_affinity_irq()函数,然后继续调用io_apic_write()修改相应的中断重定向该值以完成中断亲和力的设置. 当执行ping命令时,将触发网卡中断并生成一个中断信号. 根据中断重定向表和仲裁机制中的值,多APIC系统选择CPU 0〜3中的一个,并将信号传递给相应的本地APIC,本地APIC中断其CPU,并且不将整个事件通知给所有其他CPU.

在嵌入式领域,业界对实时Linux的要求越来越高,必须转换中断. 在Linux中,中断具有最高优先级. 无论何时,只要发生中断事件,内核都会立即执行相应的中断处理程序,并等待所有待处理的中断和软中断得到处理,然后才能执行正常任务,因此可能会导致实时任务无法及时处理. 中断线程化后,中断将作为内核线程运行,并被赋予不同的实时优先级. 实时任务可以比中断线程具有更高的优先级. 这样,可以对优先级最高的实时任务进行优先处理,即使在负载很重的情况下也可以保证实时性能.

当前,较新的Linux 2.6.17不支持中断线程. 但是,由Ingo Molnar设计和实现的实时补丁实现了中断线程. 最新的下载地址是:

〜mingo /实时抢占/ patch-2.6.17-rt9

以下是对中断线程的简要分析.

在初始化阶段,线程中断的中断与常规中断基本相同. 在start_kernel()函数中,调用了两个函数trap_init()和init_IRQ()来初始化irq_desc_t结构. 在内核初始化期间创建init线程时,被中断的线程中断还将在init()函数中调用init_hardirqs(内核/ irq / manage.c(已在上面修补))为每个IRQ创建一个内核线程具有最大的实数-50的实时优先级,以此类推,直到25,因此所有IRQ线程的最低实时优先级为25.

void __init init_hardirqs(void)
{
……
	for (i = 0; i < NR_IRQS; i++) {
		irq_desc_t *desc = irq_desc + i;
		if (desc->action && !(desc->status & IRQ_NODELAY))
			desc->thread = kthread_create(do_irqd, desc, "IRQ %d", irq);
    ……
	}
}
static int do_irqd(void * __desc)
{
    ……
	/*
	 * Scale irq thread priorities from prio 50 to prio 25
	 */
	param.sched_priority = curr_irq_prio;
	if (param.sched_priority > 25)
		curr_irq_prio = param.sched_priority - 1;
   ……
}


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/ruanjian/article-207687-1.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...