linux 中断处理?Linux卸载软件
这篇文章给大家聊聊关于linux 中断处理,以及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中断子系统 - linux 中断处理流程
当硬件触发中断时,中断信号直接导致处理器执行跳转至中断向量表,进入中断模式(IRQ模式),随后再次跳转至系统模式(svc模式),进行具体的中断处理操作。这一系列模式转换和现场保存操作主要由晦涩难懂的汇编代码实现。在做好必要的准备工作后,调用`handle_arch_irq`函数,最终进入C语言环境进行中断处理。
解释`handle_arch_irq`函数,这是一个平台相关的函数指针。通过`set_handle_irq`函数将其设置为`gic_handle_irq`。在中断发生后,中断处理流程会调用`gic_handle_irq`函数。
`gic_handle_irq`函数包含关键代码逻辑,处理三种类型中断:软件触发中断(SGI)、硬件触发中断(PPI)、以及外部硬件中断(SPI)。SGI用于CPU之间的通信,PPI是CPU独立触发的,典型例子是时钟中断,而SPI是最常见的外部中断。
SGI中断处理相对简单,仅涉及软件处理。PPI和SPI中断则需要考虑GIC的级联问题。系统中存在多个GIC时,需要递归处理。对于PPI和SPI,它们共享处理部分,但SPI处理需要考虑GIC的级联,而PPI处理直接进入SPI处理框架。
中断源信息记录在GIC的`GIC_CPU_INTACK`寄存器中,包括CPU ID和中断ID。SGI中断中CPU ID具有意义,而PPI/SPI中断中CPU ID无意义,仅读取低10位获取hwirq。
在处理中断号时,代码执行在一个死循环中,确保中断处理过程中产生的额外中断可以立即处理,而无需重新跳转至中断向量表。当从`GIC_CPU_INTACK`读取的中断号大于等于1020时,中断处理退出。
处理外设中断如PPI和SPI,调用`handle_domain_irq`函数,传入当前GIC所属domain、hwirq以及pt_regs。内核中存在中断编号(irq),与GIC的interrupt ID不同,存在映射关系,由domain保存。
进一步调用`__handle_domain_irq`函数处理中断,涉及设置中断状态、跳转至中断处理程序、执行软中断和退出中断环境等步骤。中断状态位在进入中断处理阶段时被设置,而非中断结束时。在特定条件下,中断环境会通过`tick broadcast`更新`jiffies`以防止软中断唤醒的误判。
通用中断处理函数`generic_handle_irq`实现核心处理逻辑,通过`irq_to_desc`获取中断描述信息,调用`desc->handle_irq()`回调函数进行处理。
中断回调函数`handle_fasteoi_irq`处理共享中断和级联中断,确保正确执行中断处理程序并进行中断线程化。对于SGI中断,处理流程直接进入IPI中断的特定处理。
IPI中断的响应与触发机制涉及GIC驱动初始化代码中的`set_smp_cross_call`函数,用于配置发送SGI中断的机制。例如,发送唤醒IPI中断时,调用路径涉及`arch_send_wakeup_ipi_mask`、`__smp_cross_call`,最终调用`gic_raise_softirq`函数。
中断处理流程涉及多种中断类型和机制,包括硬件触发、软件触发、级联处理、中断线程化等,确保系统能够高效响应中断,进行必要的操作并恢复至正常执行状态。
参考资料和链接提供进一步学习中断处理流程的资源。
linux 中断 下半部 处理时间过长 怎么办
一、中断处理为什么要下半部?
Linux在中断处理中间中断处理分了上半部和下半部,目的就是提高系统的响应能力和并发能力。通俗一点来讲:当一个中断产生,调用该中断对应的处理程序(上半部)然后告诉系统,对应的后半部可以执行了。然后中断处理程序就返回,下半部会在合适的时机有系统调用。这样一来就大大的减少了中断处理所需要的时间。
二、那些工作应该放在上半部,那些应该放在下半部?
没有严格的规则,只有一些提示:
1、对时间非常敏感,放在上半部。
2、与硬件相关的,放在上半部。
3、不能被其他中断打断的工作,放在上半部。
以上三点之外的,考虑放在下半部。
三、下半部机制在Linux中是怎么实现的?
下半部在Linux中有以下实现机制:
1、BH(在2.5中删除)
2、任务队列(task queue,在2.5删除)
3、软中断(softirq,2.3开始。本文重点)
4、tasklet(2.3开始)
5、工作队列(work queue,2.5开始)
四、软中断是怎么实现的(以下代码出自2.6.32)?
软中断不会抢占另外一个软中断,唯一可以抢占软中断的是中断处理程序。
软中断可以在不同CPU上并发执行(哪怕是同一个软中断)
1、软中断是编译期间静态分配的,定义如下:
struct softirq_action{ void(*action)(struct softirq_action*);};
/*
* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
* frequency threaded job scheduling. For almost all the purposes
* tasklets are more than enough. F.e. all serial device BHs et
* al. should be converted to tasklets, not to softirqs.
*/
enum{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,/* Preferable RCU should always be the last softirq*/
NR_SOFTIRQS
};
/*
* map softirq index to softirq name. update'softirq_to_name' in* kernel/softirq.c when adding a new softirq.
*/
extern char*softirq_to_name[NR_SOFTIRQS];
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
说明:
(1)、软中断的个数书上说是32,看来到这个版本已经发生变化了。
(2)、void(*action)(struct softirq_action*);传递整个结构体指针在于当结构体成员发生变化是,接口不变。
2、系统执行软中断一个注册的软中断必须被标记后才会执行(触发软中断),通常中断处理程序会在返回前标记它的软中断。在下列地方,待处理的软中断会被执行:
(1)、从一个硬件中断代码处返回。
(2)、在ksoftirqd内核线程。
(3)、在那些显示检查和执行待处理的软中断代码中。
ksoftirqd说明:
每个处理器都有一个这样的线程。所有线程的名字都叫做ksoftirq/n,区别在于n,它对应的是处理器的编号。在一个双CPU的机器上就有两个这样的线程,分别叫做ksoftirqd/0和ksoftirqd/1。为了保证只要有空闲的处理器,它们就会处理软中断,所以给每个处理器都分配一个这样的线程。
执行软中断的代码如下:
asmlinkage void __do_softirq(void)
{
struct softirq_action*h;
__u32 pending;
int max_restart= MAX_SOFTIRQ_RESTART;
int cpu;
pending= local_softirq_pending();
account_system_vtime(current);
__local_bh_disable((unsigned long)__builtin_return_address(0));
lockdep_softirq_enter();
cpu= smp_processor_id();
restart:
/* Reset the pending bitmask before enabling irqs*/
set_softirq_pending(0);
local_irq_enable();
h= softirq_vec;
do{
if(pending& 1){
int prev_count= preempt_count();
kstat_incr_softirqs_this_cpu(h- softirq_vec);
trace_softirq_entry(h, softirq_vec);
h->action(h);
trace_softirq_exit(h, softirq_vec);
if(unlikely(prev_count!= preempt_count())){
printk(KERN_ERR"huh, entered softirq%td%s%p"
"with preempt_count%08x,"
" exited with%08x?\n", h- softirq_vec,
softirq_to_name[h- softirq_vec],
h->action, prev_count, preempt_count());
preempt_count()= prev_count;
}
rcu_bh_qs(cpu);
}
h++;
pending>>= 1;
} while(pending);
local_irq_disable();
pending= local_softirq_pending();
if(pending&&--max_restart)
goto restart;
if(pending)
wakeup_softirqd();
lockdep_softirq_exit();
account_system_vtime(current);
_local_bh_enable();
}
3、编写自己的软中断
(1)、分配索引,在HI_SOFTIRQ与NR_SOFTIRQS中间添加自己的索引号。
(2)、注册处理程序,处理程序:open_softirq(索引号,处理函数)。
(3)、触发你的软中断:raise_softirq(索引号)。
4、软中断处理程序注意
(1)、软中断处理程序执行的时候,允许响应中断,但自己不能休眠。
(2)、如果软中断在执行的时候再次触发,则别的处理器可以同时执行,所以加锁很关键。