linux 中断分析
Linux系统中有很多不同的硬件设备。
你可以同步使用这些设备,也就是说你可以发出一个请求,然后等待一直到设备完成操作以后再进行其他的工作。
但这种方法的效率却非常的低,因为操作系统要花费很多的等待时间。
一个更为有效的方法是发出请求以后,操作系统继续其他的工作,等设备完成操作以后,给操作系统发送一个中断,操作系统再继续处理和此设备有关的操作。
在将多个设备的中断信号送往CPU的中断插脚之前,系统经常使用中断控制器来综合多个设备的中断。
这样即可以节约CPU的中断插脚,也可以提高系统设计的灵活性。
中断控制器用来控制系统的中断,它包括屏蔽和状态寄存器。
设置屏蔽寄存器的各个位可以允许或屏蔽某一个中断,状态寄存器则用来返回系统中正在使用的中断。
大多数处理器处理中断的过程都相同。
当一个设备发出中段请求时,CPU停止正在执行的指令,转而跳到包括中断处理代码或者包括指向中断处理代码的转移指令所在的内存区域。
这些代码一般在CPU的中断方式下运行。
在此方式下,将不会再有中断发生。
但有些CPU的中断有自己的优先权,这样,更高优先权的中断则可以发生。
这意味着第一级的中断处理程序必须拥有自己的堆栈,以便在处理更高级别的中断前保存CPU的执行状态。
当中断处理完毕以后,CPU将恢复到以前的状态,继续执行中断处理前正在执行的指令。
中断处理程序十分简单有效,这样,操作系统就不会花太长的时间屏蔽其他的中断。
[设置Softirq]cpu_raise_softirq是一个轮训,唤醒ksoftirqd_CPU0内核线程, 进行管理cpu_raise_softirq|__cpu_raise_softirq|wakeup_softirqd|wake_up_process·cpu_raise_softirq [kernel/softirq.c]·__cpu_raise_softirq [include/linux/interrupt.h]·wakeup_softirq [kernel/softirq.c]·wake_up_process [kernel/sched.c][执行Softirq]当内核线程ksoftirqd_CPU0被唤醒, 它会执行队列里的工作。
当然ksoftirqd_CPU0也是一个死循环:for (;{if (!softirq_pending(cpu))schedule();__set_current_state(TASK_RUNNING);while (softirq_pending(cpu)) {do_softirq();if (current->;need_resched)schedule}__set_current_state(TASK_INTERRUPTIBLE)}·ksoftirqd [kernel/softirq.c][目录]--------------------------------------------------------------------------------软中断软中断「一」一、引言软中断是linux系统原“底半处理”的升级,在原有的基础上发展的新的处理方式,以适应多cpu 、多线程的软中断处理。
要了解软中断,我们必须要先了原来底半处理的处理机制。
二、底半处理机制(基于2.0.3版本)某些特殊时刻我们并不愿意在核心中执行一些操作。
例如中断处理过程中。
当中断发生时处理器将停止当前的工作, 操作系统将中断发送到相应的设备驱动上去。
由于此时系统中其他程序都不能运行, 所以设备驱动中的中断处理过程不宜过长。
有些任务最好稍后执行。
Linux底层部分处理机制可以让设备驱动和Linux核心其他部分将这些工作进行排序以延迟执行。
系统中最多可以有32个不同的底层处理过程;bh_base是指向这些过程入口的指针数组。
而bh_active 和bh_mask用来表示那些处理过程已经安装以及那些处于活动状态。
如果bh_mask的第N位置位则表示bh_base的第N个元素包含底层部分处理例程。
如果bh_active的第N位置位则表示第N个底层处理过程例程可在调度器认为合适的时刻调用。
这些索引被定义成静态的;定时器底层部分处理例程具有最高优先级(索引值为0),控制台底层部分处理例程其次(索引值为1)。
典型的底层部分处理例程包含与之相连的任务链表。
例如immediate底层部分处理例程通过那些需要被立刻执行的任务的立即任务队列(tq_immediate)来执行。
--引自David A Rusling的《linux核心》。
三、对2.4.1 软中断处理机制下面,我们进入软中断处理部份(softirq.c):由softirq.c的代码阅读中,我们可以知道,在系统的初始化过程中(softirq_init()),它使用了两个数组:bh_task_vec[32],softirq_vec[32]。
其中,bh_task_vec[32]填入了32个bh_action()的入口地址,但soft_vec[32]中,只有softirq_vec[0],和softirq_vec[3]分别填入了tasklet_action()和tasklet_hi_action()的地址。
其余的保留它用。
当发生软中断时,系统并不急于处理,只是将相应的cpu的中断状态结构中的active 的相应的位置位,并将相应的处理函数挂到相应的队列,然后等待调度时机来临(如:schedule(),系统调用返回异常时,硬中断处理结束时等),系统调用do_softirq()来测试active位,再调用被激活的进程在这处过程中,软中断的处理与底半处理有了差别,active 和mask不再对应bh_base[nr], 而是对应softirq_vec[32]。
在softirq.c中,我们只涉及了softirq_vec[0]、softirq_vec[3]。
这两者分别调用了tasklet_action()和tasklet_hi_action()来进行后续处理。
这两个过程比较相似,大致如下:1 锁cpu的tasklet_vec[cpu]链表,取出链表,将原链表清空,解锁,还给系统。
2 对链表进行逐个处理。
3 有无法处理的,(task_trylock(t)失败,可能有别的进程锁定),插回系统链表。
至此,系统完成了一次软中断的处理。
接下来有两个问题:1 bh_base[]依然存在,但应在何处调用?2 tasklet_vec[cpu]队列是何时挂上的?四、再探讨再次考查softirq.c 的bh_action()部份,发现有两个判断:A:if(!spin_trylock(&global_bh_lock))goto:rescue 指明如果global_bh_lock 不能被锁上(已被其它进程锁上),则转而执行rescue,将bh_base[nr]挂至tasklet_hi_vec[cpu]队列中。
等候中断调度。
B:if(!hardirq_trylock(cpu)) goto tescue unlock 此时有硬中断发生,放入队列推迟执行。
若为空闲,现在执行。
由此可见,这部分正是对应底半处理的程序,bh_base[]的延时处理正是底半处理的特点,可以推测,如果没有其它函数往tasklet_hi_vec[cpu]队列挂入,那tasklet_hi_vec[cpu]正完全对应着bh_base[]底半处理在bh_action()中,把bh_ation()挂入tasklet_hi_vec[cpu]的正是mark_bh(),在整个源码树中查找,发现调用mark_bh()的函数很多,可以理解,软中断产生之时,相关的函数会调用mark_bh(),将bh_action挂上tasklet_hi_vec队列,而bh_action()的作用不过是在发现bh_base[nr]暂时无法处理时重返队列的方法。
由此可推测tasklet_vec队列的挂接应与此相似,查看interrupt.h,找到tasklet_schedule()函数:157 static inline void tasklet_schedule(struct tasklet_struct *t)158 {159 if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->;state)) {160 int cpu = smp_processor_id();161 unsigned long flags;162163 local_irq_save(flags);164 t->;next = tasklet_vec[cpu].list;165 tasklet_vec[cpu].list = t; /*插入队列。
166 __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);167 local_irq_restore(flags);168 }169 }正是它为tasklet_vec[cpu]队列的建立立下了汗马功劳,在源码树中,它亦被多个模块调用,来完成它的使命。
至此,我们可以描绘一幅完整的软中断处理图了。
现在,再来考查do_softirq()的softirq_vec[32],在interrupt.h中有如下定义:56 enum57 {58 HI_SOFTIRQ=0,59 NET_TX_SOFTIRQ,60 NET_RX_SOFTIRQ,61 TASKLET_SOFTIRQ62 };这四个变量应都是为softirq_vec[]的下标,那么,do_softirq()也将会处理NET_TX_SOFTIRQ和NET _RX_SOFTIRQ,是否还处理其它中断,这有待探讨。
也许,这个do_softirq()有着极大的拓展性,等着我们去开发呢。
主要通过__cpu_raise_softirq来设置在hi_tasklet(也就是一般用于bh的)的处理里面,在处理完当前的队列后,会将补充的队列重新挂上,然后标记(不管是否补充队列里面有tasklet):local_irq_disable();t->;next = tasklet_hi_vec[cpu].list;tasklet_hi_vec[cpu].list = t;__cpu_raise_softirq(cpu, HI_SOFTIRQ);local_irq_enable();因此,对mark_bh根本不用设置这个active位。
对于一般的tasklet也一样:local_irq_disable();t->;next = tasklet_vec[cpu].list;tasklet_vec[cpu].list = t;__cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);local_irq_enable();其它的设置,可以检索上面的__cpu_raise_softirq bottom half, softirq, tasklet, tqueue[bottom half]bh_base[32]|\/bh_action();|\/bh_task_vec[32];| mark_bh(), tasklet_hi_schedule()\/task_hi_actionbh_base对应的是32个函数,这些函数在bh_action()中调用static void bh_action(unsigned long nr){int cpu = smp_processor_id();if (!spin_trylock(&global_bh_lock))goto resched;if (!hardirq_trylock(cpu))goto resched_unlock;if (bh_base[nr])bh_base[nr]();hardirq_endlock(cpu);spin_unlock(&global_bh_lock);return;resched_unlock:spin_unlock(&global_bh_lock);resched:mark_bh(nr);}在软中断初始化时,将bh_action()放到bh_task_vec[32]中,bh_task_vec[32]中元素的类型是tasklet_struct,系统使用mark_bh()或task_hi_schedule()函数将它挂到task_hi_vec[]的对列中,在系统调用do_softirq()时执行。
Linux网络疑难杂症解决断网问题的几个窍门
Linux网络疑难杂症解决断网问题的几个窍门在使用Linux操作系统时,有时会遇到网络连接断开的问题,这对于依赖网络工作的人来说是非常头疼的。
本文将分享几个解决Linux网络断网问题的窍门,希望能帮助读者尽快解决这一疑难杂症。
一、检查网络硬件连接网络连接出现问题的第一步是检查网络硬件连接,包括网线、网卡和路由器等设备。
确保网线插紧,网卡连接正常,路由器无故障,从而排除硬件连接问题对网络故障的影响。
二、使用ping命令检查网络连通性使用ping命令是一种简单有效的方法,可以检查Linux主机与其他设备的网络连通性。
在终端中执行ping命令,加上目标主机的IP地址或域名,如果能够连通,则表示无问题。
如果无法连通,请检查本地网络配置或目标主机是否正常。
三、检查网络配置文件Linux系统的网络配置文件位于/etc/network/interfaces文件中。
可以通过编辑此文件来检查和修改网络配置。
确保配置文件中指定的IP地址、子网掩码、网关等参数是正确的。
如果有任何更改,请使用sudo service networking restart命令重启网络服务以使更改生效。
四、重启网络管理服务有时,网络管理服务可能出现问题,导致网络连接断开。
可以尝试重启网络管理服务以解决这个问题。
通过执行sudo service network-manager restart命令,可以重新启动网络管理服务。
请注意,此命令可能因Linux发行版的不同而有所区别,请根据您所使用的发行版进行调整。
五、禁用IPv6IPv6是一种新的网络协议,用于替代IPv4。
虽然IPv6在未来是必要的,但当前仍有些网络环境对其支持存在问题。
尝试禁用IPv6可能会解决某些网络断开的问题。
可以通过编辑/etc/sysctl.conf文件,在末尾添加以下行来禁用IPv6:```net.ipv6.conf.all.disable_ipv6 = 1net.ipv6.conf.default.disable_ipv6 = 1```保存文件后,通过执行sudo sysctl -p命令使更改生效。
Linux中断处理流程
Linux中断处理流程1. 中断处理流程 当中断发⽣时,Linux系统会跳转到asm_do_IRQ()函数(所有中断程序的总⼊⼝函数),并且把中断号irq传进来。
根据中断号,找到中断号对应的irq_desc结构(irq_desc结构为内核中中断的描述结构,内核中有⼀个irq_desc结构的数组irq_desc_ptrs[NR_IRQS]),然后调⽤irq_desc中的handle_irq函数,即中断⼊⼝函数。
我们编写中断的驱动,即填充并注册irq_desc结构。
2. 中断处理数据结构:irq_desc Linux内核将所有的中断统⼀编号,使⽤⼀个irq_desc[NR_IRQS]的结构体数组来描述这些中断:每个数组项对应着⼀个中断源(也可能是⼀组中断源),记录中断⼊⼝函数、中断标记,并提供了中断的底层硬件访问函数(中断清除、屏蔽、使能)。
另外通过这个结构体数组项中的action,能够找到⽤户注册的中断处理函数。
struct irq_desc {unsigned int irq;irq_flow_handler_t handle_irq;struct irq_chip *chip;struct msi_desc *msi_desc;void *handler_data;void *chip_data;struct irqaction *action; /* IRQ action list */unsigned int status; /* IRQ status */unsigned int depth; /* nested irq disables */unsigned int wake_depth; /* nested wake enables */unsigned int irq_count; /* For detecting broken IRQs */unsigned long last_unhandled; /* Aging timer for unhandled count */unsigned int irqs_unhandled;spinlock_t lock;const char *name;} ____cacheline_internodealigned_in_smp;(1)handle_irq:中断的⼊⼝函数(2)chip:包含这个中断的清除、屏蔽、使能等底层函数struct irq_chip {const char *name;unsigned int (*startup)(unsigned int irq);void (*shutdown)(unsigned int irq);void (*enable)(unsigned int irq);void (*disable)(unsigned int irq);void (*ack)(unsigned int irq);void (*mask)(unsigned int irq);void (*mask_ack)(unsigned int irq);void (*unmask)(unsigned int irq);void (*eoi)(unsigned int irq);void (*end)(unsigned int irq);void (*set_affinity)(unsigned int irq,const struct cpumask *dest);int (*retrigger)(unsigned int irq);int (*set_type)(unsigned int irq, unsigned int flow_type);int (*set_wake)(unsigned int irq, unsigned int on);/* Currently used only by UML, might disappear one day.*/#ifdef CONFIG_IRQ_RELEASE_METHODvoid (*release)(unsigned int irq, void *dev_id);#endif/** For compatibility, ->typename is copied into ->name.* Will disappear.*/const char *typename;};(3)action:记录⽤户注册的中断处理函数、中断标志等内容struct irqaction {irq_handler_t handler;unsigned long flags;cpumask_t mask;const char *name;void *dev_id;struct irqaction *next;int irq;struct proc_dir_entry *dir;};3. 中断处理流程总结(1)发⽣中断后,CPU执⾏异常向量vector_irq的代码;(2)在vector_irq⾥⾯,最终会调⽤中断处理C程序总⼊⼝函数asm_do_IRQ();(3)asm_do_IRQ()根据中断号调⽤irq_des[NR_IRQS]数组中的对应数组项中的handle_irq();(4)handle_irq()会使⽤chip的成员函数来设置硬件,例如清除中断,禁⽌中断,重新开启中断等;(5)handle_irq逐个调⽤⽤户在action链表中注册的处理函数。
10-5 Linux操作系统 - 中断、异常及系统调用
10.5.4 中断上半部分的处理 一、 中断控制器 •每个硬件设备控制器都能通过中断请求线 发出中断请求(简称IRQ) •所有设备的中断请求线又连到中断控制器 的输入端。 •在x86单CPU的机器上采用两个8259A芯片作 为中断控制器,一主一从。
•当8259A有中断信号输入同时中断信号不被 屏蔽时,主8259A向CPU发出 INT信号,请求 中断。这时如果CPU是处于允许中断状况, CPU就会发信号给8259A进入中断响应周期。 •在对8259A芯片的初始化过程中,第n号中 断在IDT表中的向量号为 n+32
•IDT中向量号的使用情况如下: 0-31 异常与非屏蔽中断使用。 32-47 可屏蔽中断使用32至47 128(0x80)实现系统调用。 其余 未使用 •保存现场 发生异常时在核心栈的程序计数器eip的 值取决于具体情况。一般情况下eip保存的 下一条指令的地址,但对于页面异常,保存 的产生异常的这条指令的地址而不是下一条 指令的地址
中断向量表IDT •IDT是中断/异常处理在内核的入口。IDT表 项还记录了一些其它信息用以安全检查。 •IDT在系统初始化时创建。 •每个中断/异常都有一个向量号,该号的值 在0-255之间,该值是中断/异常在IDT中的 索引。 •每个中断/异常均有其相应的处理函数,中 断/异常在使用前必须在IDT中注册信息以保 证发生中断/异常时能找到相应的处理函数。
struct hw_interrupt_type { const char * typename; unsigned int (*startup)(unsigned int irq); void (*shutdown)(unsigned int irq); void (*enable)(unsigned int irq); void (*disable)(unsigned int irq); void (*ack)(unsigned int irq); void (*end)(unsigned int irq); void (*set_affinity)(unsigned int irq, unsigned long mask); };
linux中断处理流程
linux中断处理流程Linux中断处理流程Linux中断处理是操作系统中的一个重要组成部分,用于响应硬件设备的事件。
在Linux中,中断可以是外部中断,如硬件设备发送的中断信号,也可以是内部中断,如软件产生的异常或系统调用。
中断处理的目的是及时响应硬件设备的事件,并采取相应的措施来处理这些事件。
一、中断的触发中断是由硬件设备发送的一个信号,用于通知操作系统某个事件的发生。
这个信号可以是一个电平的变化,一个特定的数据包,或者一个指定的硬件寄存器的变化。
当硬件设备检测到某个事件发生时,它会向处理器发送一个中断信号,处理器会立即停止当前正在执行的任务,保存当前的上下文,并跳转到中断处理程序的入口点。
二、中断处理程序的执行中断处理程序是一个特殊的函数,负责处理中断事件。
当中断发生时,处理器会跳转到中断处理程序的入口点,并执行相应的代码。
中断处理程序的执行过程可以分为以下几个步骤:1. 保存上下文:在执行中断处理程序之前,处理器需要保存当前任务的上下文,包括程序计数器、寄存器和堆栈指针等。
这样可以确保在中断处理程序执行完成后,能够正确地返回到原来的任务。
2. 中断处理程序的执行:一旦保存了上下文,处理器就会执行中断处理程序的代码。
中断处理程序根据中断的类型,执行相应的操作。
例如,对于外部中断,中断处理程序可能需要读取硬件设备的状态,处理数据包或执行特定的操作。
对于内部中断,中断处理程序可能需要处理异常或系统调用。
3. 中断处理程序的结束:当中断处理程序执行完成后,处理器会恢复之前保存的上下文,并将控制权返回给原来的任务。
这样原来的任务就可以继续执行,而不会受到中断的影响。
三、中断处理的优先级在Linux中,中断处理有不同的优先级。
这是为了确保对于紧急事件的及时处理。
中断的优先级由硬件设备决定,通常是通过一个优先级编码器来实现的。
当多个中断同时发生时,处理器会按照优先级的顺序来处理中断。
高优先级的中断会立即被处理,而低优先级的中断则会被推迟到稍后处理。
linux中断处理之IRQ中断
linux中断处理之IRQ中断本文系本站原创,欢迎转载!转载请注明出处:/------------------------------------------一:前言在前一个专题里曾分析过所有IRQ中断处理流程,经过SAVE_ALL保存硬件环境后,都会进入do_IRQ()进行处理,今天接着分析do_IRQ()处理的相关东西.分为两部中断处理程序与软中断两个大的部份进行介绍.二:中断处理程序在驱动程序中,通常使用request_irq()来注册中断处理程序.我们先从注册中断处理程序的实现说起./*irq:可断号handler:中断处理程序irqflags:中断处理标志.SA_SHIRQ:共享中断线SA_INTERRUPT:快速处理中断必须在关中断的情况下运行.SA_SAMPLE_RANDOM:该中断可能用于产生一个随机数devname dev_id:设备名称与ID*/int request_irq(unsigned int irq,irqreturn_t (*handler)(int, void *, struct pt_regs *),unsigned long irqflags,const char * devname,void *dev_id){int retval;struct irqaction * action;#if 1if (irqflags & SA_SHIRQ) {if (!dev_id)printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);}#endif//参数有效性判断if (irq >= NR_IRQS)return -EINVAL;if (!handler)return -EINVAL;// 分配一个irqactionaction = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_ATOMIC);if (!action)return -ENOMEM;action->handler = handler;action->flags = irqflags;cpus_clear(action->mask);action->name = devname;action->next = NULL;action->dev_id = dev_id;//将创建并初始化完在的action加入irq_desc[NR_IRQS]retval = setup_irq(irq, action);if (retval)kfree(action);return retval;}上面涉及到的irqaction结构与irq_desc[]的关系我们在上一节我们已经详细分析过了,这里不再赘述. 转进setup_irq():int setup_irq(unsigned int irq, struct irqaction * new){int shared = 0;unsigned long flags;struct irqaction *old, **p;irq_desc_t *desc = irq_desc + irq;//如果hander == no_irq_type:说明中断控制器不支持该IRQ线if (desc->handler == &no_irq_type)return -ENOSYS;sif (new->flags & SA_SAMPLE_RANDOM) {rand_initialize_irq(irq);}/** The following block of code has to be executed atomically*/spin_lock_irqsave(&desc->lock,flags);p = &desc->action;if ((old = *p) != NULL) {//判断这条中断线上的中断处理程序是否允许SHARE/* Can't share interrupts unless both agree to */if (!(old->flags & new->flags & SA_SHIRQ)) {spin_unlock_irqrestore(&desc->lock,flags);return -EBUSY;}/* add new interrupt at end of irq queue */do {p = &old->next;old = *p;} while (old);shared = 1;}//将其添加到中断处理函数链的末尾*p = new;//如果这一条线还没有被占用,初始化这条中断线//包含清标志,在8259A上启用这条中断线if (!shared) {desc->depth = 0;desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);desc->handler->startup(irq);}spin_unlock_irqrestore(&desc->lock,flags);//在proc下建立相关的文件register_irq_proc(irq);return 0;}现在知道怎么打一个中断处理程序挂到irq_desc[NR_IRQS]数组上了,继续分析中断处理中如何调用中断处理函数.从我们开篇时说到的do_IRQ()说起.asmlinkage unsigned int do_IRQ(struct pt_regs regs){//屏蔽高位,取得中断号int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code *///取得中断号对应的desc结构irq_desc_t *desc = irq_desc + irq;struct irqaction * action;unsigned int status;irq_enter();// 调试用,忽略#ifdef CONFIG_DEBUG_STACKOVERFLOW/* Debugging check for stack overflow: is there less than 1KB free? */{long esp;__asm__ __volatile__("andl %%esp,%0" :"=r" (esp) : "0" (THREAD_SIZE - 1));if (unlikely(esp < (sizeof(struct thread_info) + STACK_WARN))) {printk("do_IRQ: stack overflow: %ld\n",esp - sizeof(struct thread_info));dump_stack();}}#endif//更新统计计数kstat_this_cpu.irqs[irq]++;spin_lock(&desc->lock);//给8259 回一个ack.回ack之后,通常中断控制会屏蔽掉此条IRQ线desc->handler->ack(irq);//清除IRQ_REPLAY IRQ_WAITING标志status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);//设置IRQ_PENDING:表示中断被应答,但没有真正被处理status |= IRQ_PENDING; /* we _want_ to handle it *//** If the IRQ is disabled for whatever reason, we cannot* use the action we have.*/action = NULL;//中断被屏蔽或者正在处理//IRQ_DIASBLED:中断被禁用//IRQ_INPROGRESS:这个类型的中断已经在被另一个CPU处理了if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action;status &= ~IRQ_PENDING; /* we commit to handling *///置位,表示正在处理中...status |= c; /* we are handling it */}desc->status = status;//没有挂上相应的中断处理例程或者不满足条件if (unlikely(!action))goto out;for (;;) {irqreturn_t action_ret;u32 *isp;union irq_ctx * curctx;union irq_ctx * irqctx;curctx = (union irq_ctx *) current_thread_info();irqctx = hardirq_ctx[smp_processor_id()];spin_unlock(&desc->lock);//通常curctx == irqctx.除非中断程序使用独立的4K堆栈.if (curctx == irqctx)action_ret = handle_IRQ_event(irq, ®s, action);else {/* build the stack frame on the IRQ stack */isp = (u32*) ((char*)irqctx + sizeof(*irqctx));irqctx->tinfo.task = curctx->tinfo.task;irqctx->tinfo.real_stack = curctx->tinfo.real_stack;irqctx->tinfo.virtual_stack = curctx->tinfo.virtual_stack;irqctx->tinfo.previous_esp = current_stack_pointer();*--isp = (u32) action;*--isp = (u32) ®s;*--isp = (u32) irq;asm volatile(" xchgl %%ebx,%%esp \n"" call handle_IRQ_event \n"" xchgl %%ebx,%%esp \n": "=a"(action_ret): "b"(isp): "memory", "cc", "edx", "ecx");}spin_lock(&desc->lock);//调试用,忽略if (!noirqdebug)note_interrupt(irq, desc, action_ret, ®s);if (curctx != irqctx)irqctx->tinfo.task = NULL;//如果没有要处理的中断了,退出if (likely(!(desc->status & IRQ_c)))break;//又有中断到来了,继续处理desc->status &= ~c;}//处理完了,清除IRQ_INPROGRESS标志desc->status &= ~IRQ_INPROGRESS;out:/** The ->end() handler has to deal with interrupts which got* disabled while the handler was running.*///处理完了,调用中断控制器的end.通常此函数会使中断控制器恢复IRQ线中断desc->handler->end(irq);spin_unlock(&desc->lock);//irq_exit():理论上中断处理完了,可以处理它的下半部了irq_exit();return 1;}这段代码比较简单,但里面几个标志让人觉的很迷糊,列举如下:IRQ_DISABLED:相应的IRQ被禁用.既然中断线被禁用了,也就不会产生中断,进入do_IRQ()了?因为电子器件的各种原因可能会产生“伪中断”上报给CPU.IRQ_PENDING:CPU收到这个中断信号了,已经给出了应答,但并末对其进行处理.回顾上面的代码,进入do_IRQ后,发送ack,再设置此标志.IRQ_ INPROGRESS:表示这条IRQ线的中断正在被处理.为了不弄脏CPU的高速缓存.把相同IRQ线的中断放在一起处理可以提高效率,且使中断处理程序不必重入举例说明:如果CPU A接收到一个中断信号.回一个ACK,设置c,假设此时末有这个中断线的中断处理程序在处理,继而会将标志位设为IRQ_ INPROGRESS.转去中断处理函数执行.如果此时,CPU B检测到了这条IRQ线的中断信号.它会回一个ACK.设置IRQ_PENDING.但时此时这条IRQ线的标志为IRQ_ INPROGRESS.所以,它会进经过goto out退出.如果cpu A执行完了中断处理程序,判断它的标志线是否为IRQ_PENDING.因为CPU B已将其设为了IRQ_PENDING.所以继续循环一次.直到循环完后,清除IRQ_INPROGRESS标志注意上述读写标志都是加锁的.linux采用的这个方法,不能不赞一个*^_^*继续看代码:asmlinkage int handle_IRQ_event(unsigned int irq,struct pt_regs *regs, struct irqaction *action){int status = 1; /* Force the "do bottom halves" bit */int ret, retval = 0;//如果没有设置SA_INTERRUPT.将CPU 中断打开//应该尽量的避免CPU关中断的情况,因为CPU屏弊本地中断,会使//中断丢失if (!(action->flags & SA_INTERRUPT))local_irq_enable();//遍历运行中断处理程序do {ret = action->handler(irq, action->dev_id, regs);if (ret == IRQ_HANDLED)status |= action->flags;retval |= ret;action = action->next;} while (action);if (status & SA_SAMPLE_RANDOM)add_interrupt_randomness(irq);//关中断local_irq_disable();return retval;}可能会有这样的疑问.如果在一根中断线上挂上了很多个中断处理程序,会不会使这一段程序的效率变得很低下呢?事实上,我们在写驱动程序的过程中,都会首先在中断处理程序里判断设备名字与设备ID,只有条件符合的设备中断才会变处理.三:软中断为了提高中断的响应速度,很多操作系统都把中断分成了两个部份,上半部份与下半部份.上半部份通常是响应中断,并把中断所得到的数据保存进下半部.耗时的操作一般都会留到下半部去处理.接下来,我们看一下软中断的处理模型:Start_kernel() à softirq_init();在softirq_init()中会注册两个常用类型的软中断,看具体代码:void __init softirq_init(void){open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);}//参数含义:nr:软中断类型action:软中断处理函数data:软中断处理函数参数void open_softirq(int nr, void (*action)(struct softirq_action*), void *data){softirq_vec[nr].data = data;softirq_vec[nr].action = action;}static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;struct softirq_action{void (*action)(struct softirq_action *);void *data;};在上面的代码中,我们可以看到:open_softirq()中.其实就是对softirq_vec数组的nr项赋值.softirq_vec是一个32元素的数组,实际上linux内核只使用了六项. 如下示:enum{HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,SCSI_SOFTIRQ,TASKLET_SOFTIRQ}另外.如果使软中断能被CPU调度,还得要让它激活才可以.激活所使用的函数为__raise_softirq_irqoff()代码如下:#define __raise_softirq_irqoff(nr) do { local_softirq_pending() |= 1UL << (nr); } while (0)这个宏使local_softirq_pending的nr位置1好了,经过open_softirq()à local_softirq_pending()后,我们来看下软中断怎么被CPU调度.继续上面中断处理的代码.在处理完硬件中断后,会调用irq_exit().这就是软中断的入口点了,我们来看下#define irq_exit() \do { \preempt_count() -= IRQ_EXIT_OFFSET; \//注意了,软中断不可以在硬件中断上下文或者是在软中断环境中使用哦^_^//softirq_pending()的判断,注意我们上面分析过的_raise_softirqoff().它判断当前cpu有没有激活软中断if (!in_interrupt() && softirq_pending(smp_processor_id())) \do_softirq(); \preempt_enable_no_resched(); \} while (0)跟踪进do_softirq()asmlinkage void do_softirq(void){__u32 pending;unsigned long flags;//在硬件中断环境中,退出if (in_interrupt())return;//禁止本地中断,不要让其受中断的影响local_irq_save(flags);pending = local_softirq_pending();//是否有软中断要处理?if (pending)__do_softirq();//恢复CPU中断local_irq_restore(flags);}转入__do_softirq()asmlinkage void __do_softirq(void){struct softirq_action *h;__u32 pending;int max_restart = MAX_SOFTIRQ_RESTART;int cpu;pending = local_softirq_pending();//禁止软中断,不允许软中断嵌套local_bh_disable();cpu = smp_processor_id();restart:/* Reset the pending bitmask before enabling irqs *///把挂上去的软中断清除掉,因为我们在这里会全部处理完local_softirq_pending() = 0;//开CPU中断local_irq_enable();//softirq_vec:32元素数组h = softirq_vec;//依次处理挂上去的软中断do {if (pending & 1) {//调用软中断函数h->action(h);rcu_bh_qsctr_inc(cpu);}h++;pending >>= 1;} while (pending);//关CPU 中断local_irq_disable();pending = local_softirq_pending();//在规定次数内,如果有新的软中断了,可以继续在这里处理完if (pending && --max_restart)goto restart;//依然有没有处理完的软中断,为了提高系统响应效率,唤醒softirqd进行处理if (pending)wakeup_softirqd();//恢复软中断__local_bh_enable();}从上面的处理流程可以看到,软中断处理就是调用open_ softirq()的action参数.这个函数对应的参数是软中断本身(h->action(h)),采用这样的形式,可以在改变softirq_action结构的时候,不会重写软中断处理函数在进入了软中断的时候,使用了in_interrupt()来防止软中断嵌套,和抢占硬中断环境。
linux系统中断详解
linux系统中断详解最近为了解决风控问题,⼀直在研究linux的系统内核,经过⼀段时间的学习,先整理出⼀份关于linux中断的⼩记。
1.什么是中断?计算机cpu在执⾏task时,不可能每次都将任务执⾏完毕,会因为各种不同的场景⽽暂停执⾏,所谓中断就是这个暂停执⾏的过程。
2.中断算是⼀种错误吗?严格来说,中断当然算是运⾏错误的⼀种,但是,由于其不可避免,程序开发者⾃然可以将其视为⼀种机制,加以运⽤,反⽽更容易帮助我们完成现实功能的实现。
3.中断的分类从产⽣原因上看,中断可以分为软件中断和硬件中断,⽽从类别划分上看,也可以氛围有出错码中断和⽆出错码中断。
⼆者并不排斥,⽐如说,中断分类存在int0~int255中,其中int0 ~ int31 表⽰软件中断,int32 ~ int255: 可由⽤户⾃⼰设置。
其中int32 ~ int47 对应8259A的IRQ0 ~ IRQ15中断。
需要注意的是,int128 为系统调⽤中断(system_call)。
如果⼤家有看过内核源码或者其他汇编代码,会发现汇编语⾔在调⽤c函数时,就是使⽤system_call,由此可见,调⽤其他函数,也是⼀种中断。
这⾥不⽌局限于linux系统,其中中断逻辑囊括所有计算机逻辑执⾏逻辑,其最基础的实现逻辑就是计算机0/1与或逻辑,属于机器语⾔中最低级也是最⾼效的展现形式。
4.中断时堆栈变化情况堆栈相关知识此处不做介绍,不了解的同学可以⾃⾏查找⼀下相关资料。
上图可以⾮常清楚的展⽰中断发⽣时堆栈的变化情况。
即中断发⽣前,需要将图中的信息按照先后顺序,压⼊中断处理程序的堆栈中。
下⾯进⾏具体的分析:SS(stack segment): 堆栈段ESP(extended stack pointer): 堆栈指针EFLAGS : 状态标志寄存器CS(code segment): 代码段EIP(extended instruction pointer) : 中断后要执⾏的下⼀条指令的地址1)有/⽆出错码:我们只需知道,对于某些中断,⽐如:int0(⽆错误码)是不需要保存出错码的,⽽像int8等中断是需要保存出错码的。
中断处理流程
中断处理流程linux中断处理浅析最近在研究异步消息处理, 突然想起linux内核的中断处理, ⾥⾯由始⾄终都贯穿着"重要的事马上做, 不重要的事推后做"的异步处理思想. 于是整理⼀下~第⼀阶段--获取中断号每个CPU都有响应中断的能⼒, 每个CPU响应中断时都⾛相同的流程. 这个流程就是内核提供的中断服务程序.在进⼊中断服务程序时, CPU已经⾃动禁⽌了本CPU上的中断响应, 因为CPU不能假定中断服务程序是可重⼊的.中断处理程序的第⼀步要做两件事情:1. 将中断号压⼊栈中; (不同中断号的中断对应不同的中断服务程序⼊⼝)2. 将当前寄存器信息压⼊栈中; (以便中断退出时恢复)显然, 这两步都是不可重⼊的(如果在保存寄存器值时被中断了, 那么另外的操作很可能就把寄存器给改写了, 现场将⽆法恢复), 所以前⾯说到的CPU进⼊中断服务程序时要⾃动禁⽌中断.栈上的信息被作为函数参数, 调⽤do_IRQ函数.第⼆阶段--中断串⾏化进⼊do_IRQ函数, 第⼀步进⾏中断的串⾏化处理, 将多个CPU同时产⽣的某⼀中断进⾏串⾏化.其⽅法是如果当前中断处于"执⾏"状态(表明另⼀个CPU正在处理相同的中断), 则重新设置它的"触发"标记, 然后⽴即返回. 正在处理同⼀中断的那个CPU完成⼀次处理后, 会再次检查"触发"标记, 如果设置, 则再次触发处理过程.于是, 中断的处理是⼀个循环过程, 每次循环调⽤handle_IRQ_event来处理中断.第三阶段--关中断条件下的中断处理进⼊handle_IRQ_event函数, 调⽤对应的内核或内核模块通过request_irq函数注册的中断处理函数.注册的中断处理函数有个中断开关属性, ⼀般情况下, 中断处理函数总是在关中断的情况下进⾏的. ⽽调⽤request_irq注册中断处理函数时也可以设置该中断处理函数在开中断的情况下进⾏,这种情况⽐较少见, 因为这要求中断处理代码必须是可重⼊的. (另外, 这⾥如果开中断, 正在处理的这个中断⼀般也是会被阻塞的. 因为正在处理某个中断的时候, 硬件中断控制器上的这个中断并未被ack, 硬件不会发起下⼀次相同的中断.)中断处理函数的过程可能会很长, 如果整个过程都在关中断的情况下进⾏, 那么后续的中断将被阻塞很长的时间.于是, 有了soft_irq. 把不可重⼊的⼀部分在中断处理程序中(关中断)去完成, 然后调⽤raise_softirq 设置⼀个软中断, 中断处理程序结束. 后⾯的⼯作将放在soft_irq⾥⾯去做.第四阶段--开中断条件下的软中断上⼀阶段循环调⽤完当前所有被触发的中断处理函数后, do_softirq函数被调⽤, 开始处理软件中断.在软中断机制中, 为每个CPU维护了⼀个若⼲位的掩码集, 每位掩码代表⼀个中断号. 在上⼀阶段的中断处理函数中, 调⽤raise_softirq设置了对应的软中断, 到了这⾥, 软中断对应的处理函数就会被调⽤(处理函数由open_softirq函数来注册).可以看出, 软中断与中断的模型很类似, 每个CPU有⼀组中断号, 中断有其对应的优先级, 每个CPU处理属于⾃⼰的中断. 最⼤的不同是开中断与关中断.于是, ⼀个中断处理过程被分成了两部分, 第⼀部分在中断处理函数⾥⾯关中断的进⾏, 第⼆部分在软中断处理函数⾥⾯开中断的进⾏.由于这⼀步是在开中断条件下进⾏的,这⾥还可能发⽣新的中断(中断嵌套),然后新中断对应的中断处理⼜将开始⼀个新的第⼀阶段~第三阶段。
关于Linux0.00中的时钟中断代码的一点理解
关于Linux0.00中的时钟中断代码的⼀点理解在读Linux0.00 head.s的代码时,时钟中断这段代码折腾了我半天才弄懂,先贴上代码1 /*2 * head.s contains the 32-bit startup code.3 * Two L3 task multitasking. The code of tasks are in kernel area,4 * just like the Linux. The kernel code is located at 0x10000.5 */6 KRN_BASE = 0x100007 TSS0_SEL = 0x208 LDT0_SEL = 0x289 TSS1_SEL = 0X3010 LDT1_SEL = 0x381112 .text13startup_32:14 movl $0x10,%eax15mov %ax,%ds16mov %ax,%es17mov %ax,%fs18mov %ax,%gs19lss stack_ptr,%esp2021 # setup base fields of descriptors.22 movl $KRN_BASE, %ebx23 movl $gdt, %ecx24lea tss0, %eax25 movl $TSS0_SEL, %edi26call set_base27lea ldt0, %eax28 movl $LDT0_SEL, %edi29call set_base30lea tss1, %eax31 movl $TSS1_SEL, %edi32call set_base33lea ldt1, %eax34 movl $LDT1_SEL, %edi35call set_base3637call setup_idt38call setup_gdt39 movl $0x10,%eax # reload all the segment registers40mov %ax,%ds # after changing gdt.41mov %ax,%es42mov %ax,%fs43mov %ax,%gs44lss stack_ptr,%esp4546 # setup up timer 8253 chip.47 movb $0x36, %al48 movl $0x43, %edx49 outb %al, %dx50 movl $11930, %eax # timer frequency 100 HZ51 movl $0x40, %edx52 outb %al, %dx53 movb %ah, %al54 outb %al, %dx5556 # setup timer & system call interrupt descriptors.57 movl $0x00080000, %eax58 movw $timer_interrupt, %ax59 movw $0x8E00, %dx60 movl $0x20, %ecx61lea idt(,%ecx,8), %esi62 movl %eax,(%esi)63 movl %edx,4(%esi)64 movw $system_interrupt, %ax65 movw $0xef00, %dx66 movl $0x80, %ecx67lea idt(,%ecx,8), %esi68 movl %eax,(%esi)69 movl %edx,4(%esi)7071 # unmask the timer interrupt.72 movl $0x21, %edx73 inb %dx, %al74 andb $0xfe, %al75 outb %al, %dx77 # Move to user mode (task 0)78 pushfl79 andl $0xffffbfff, (%esp)80 popfl81 movl $TSS0_SEL, %eax82ltr %ax83 movl $LDT0_SEL, %eax84lldt %ax85 movl $0, current86sti87 pushl $0x1788 pushl $stack0_ptr89 pushfl /* ⼿动设置返回环境*/90 pushl $0x0f /**/91 pushl $task0 /**/92iret9394 /****************************************/95setup_gdt:96lgdt lgdt_opcode97ret9899setup_idt:100lea ignore_int,%edx101 movl $0x00080000,%eax102 movw %dx,%ax /* selector = 0x0008 = cs */103 movw $0x8E00,%dx /* interrupt gate - dpl=0, present */ 104lea idt,%edi105mov $256,%ecx106rp_sidt:107 movl %eax,(%edi)108 movl %edx,4(%edi)109 addl $8,%edi110dec %ecx111jne rp_sidt112lidt lidt_opcode113ret114115 # in: %eax - logic addr; %ebx = base addr ;116 # %ecx - table addr; %edi - descriptors offset.117set_base:118 addl %ebx, %eax119 addl %ecx, %edi120 movw %ax, 2(%edi)121 rorl $16, %eax122 movb %al, 4(%edi)123 movb %ah, 7(%edi)124 rorl $16, %eax125ret126127write_char:128push %gs129 pushl %ebx130 pushl %eax131mov $0x18, %ebx132mov %bx, %gs133 movl scr_loc, %bx134shl $1, %ebx135 movb %al, %gs:(%ebx)136shr $1, %ebx137 incl %ebx138 cmpl $2000, %ebx139jb 1f140 movl $0, %ebx1411: movl %ebx, scr_loc142 popl %eax143 popl %ebx144pop %gs145ret146147 /***********************************************/148 /* This is the default interrupt "handler" :-) */149 .align 2150ignore_int:151push %ds152 pushl %eax153 movl $0x10, %eax154mov %ax, %ds155 movl $67, %eax /* print 'C' */156call write_char157 popl %eax158pop %ds159iret161 /* Timer interrupt handler */162 .align 2163timer_interrupt:164push %ds165 pushl %edx166 pushl %ecx167 pushl %ebx168 pushl %eax169 movl $0x10, %eax170mov %ax, %ds171 movb $0x20, %al172 outb %al, $0x20173 movl $1, %eax174 cmpl %eax, current175je 1f176 movl %eax, current177 ljmp $TSS1_SEL, $0178jmp 2f1791: movl $0, current180 ljmp $TSS0_SEL, $01812: popl %eax182 popl %ebx183 popl %ecx184 popl %edx185pop %ds186iret187188 /* system call handler */189 .align 2190system_interrupt:191push %ds192 pushl %edx193 pushl %ecx194 pushl %ebx195 pushl %eax196 movl $0x10, %edx197mov %dx, %ds198call write_char199 popl %eax200 popl %ebx201 popl %ecx202 popl %edx203pop %ds204iret205206 /*********************************************/207current:.long 0208scr_loc:.long 0209210 .align 2211 .word 0212lidt_opcode:213 .word 256*8-1 # idt contains 256 entries214 .long idt + KRN_BASE # This will be rewrite by code.215 .align 2216 .word 0217lgdt_opcode:218 .word (end_gdt-gdt)-1 # so does gdt219 .long gdt + KRN_BASE # This will be rewrite by code.220221 .align 3222idt: .fill 256,8,0 # idt is uninitialized223224gdt: .quad 0x0000000000000000 /* NULL descriptor */225 .quad 0x00c09a01000007ff /* 8Mb 0x08, base = 0x10000 */ 226 .quad 0x00c09201000007ff /* 8Mb 0x10 */227 .quad 0x00c0920b80000002 /* screen 0x18 - for display */ 228229 .quad 0x0000e90100000068 # TSS0 descr 0x20230 .quad 0x0000e20100000040 # LDT0 descr 0x28231 .quad 0x0000e90100000068 # TSS1 descr 0x30232 .quad 0x0000e20100000040 # LDT1 descr 0x38233end_gdt:234 .fill 128,4,0235stack_ptr:236 .long stack_ptr237 .word 0x10238239 /*************************************/240 .align 3241ldt0: .quad 0x0000000000000000242 .quad 0x00c0fa01000003ff # 0x0f, base = 0x10000243 .quad 0x00c0f201000003ff # 0x17245 .long 0 /* back link */246 .long stack0_krn_ptr, 0x10 /* esp0, ss0 */247 .long 0, 0 /* esp1, ss1 */248 .long 0, 0 /* esp2, ss2 */249 .long 0 /* cr3 */250 .long 0 /* eip */251 .long 0 /* eflags */252 .long 0, 0, 0, 0 /* eax, ecx, edx, ebx */253 .long 0, 0, 0, 0 /* esp, ebp, esi, edi */254 .long 0,0,0,0,0,0 /* es, cs, ss, ds, fs, gs */255 .long LDT0_SEL /* ldt */256 .long 0x8000000 /* trace bitmap */257258 .fill 128,4,0259stack0_krn_ptr:260 .long 0261262 /************************************/263 .align 3264ldt1: .quad 0x0000000000000000265 .quad 0x00c0fa01000003ff # 0x0f, base = 0x10000266 .quad 0x00c0f201000003ff # 0x17267tss1:268 .long 0 /* back link */269 .long stack1_krn_ptr, 0x10 /* esp0, ss0 */270 .long 0, 0 /* esp1, ss1 */271 .long 0, 0 /* esp2, ss2 */272 .long 0 /* cr3 */273 .long task1 /* eip */274 .long 0x200 /* eflags */275 .long 0, 0, 0, 0 /* eax, ecx, edx, ebx */276 .long stack1_ptr, 0, 0, 0 /* esp, ebp, esi, edi */277 .long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */278 .long LDT1_SEL /* ldt */279 .long 0x8000000 /* trace bitmap */280281 .fill 128,4,0282stack1_krn_ptr:283 .long 0284285 /************************************/286task0:287 movl $0x17, %eax288 movw %ax, %ds289 movl $65, %al /* print 'A' */290int $0x80291 movl $0xfff, %ecx2921: loop 1b293jmp task0294295 .fill 128,4,0296stack0_ptr:297 .long 0298299task1:300 movl $0x17, %eax301 movw %ax, %ds302 movl $66, %al /* print 'B' */303int $0x80304 movl $0xfff, %ecx3051: loop 1b306jmp task1307308 .fill 128,4,0309stack1_ptr:310 .long 0311312 /*** end ***/question:在代码的第177⾏(177 ljmp $TSS1_SEL, $0)和第180⾏(180 ljmp $TSS0_SEL, $0)可以看到,中断程序转移到对应的任务去执⾏了,后边的代码怎么执⾏?iret永远也执⾏不到了么?answer:实际上,程序在开始运⾏时,从第79⾏(79 andl $0xffffbfff, (%esp))可以看到标志寄存器的中断嵌套位NT被设置成了0,也就是说中断返回了esp0,ss0,ldt,trace bitmap设置了外,其余是0.嵌套任务标志NT(Nested Task)嵌套任务标志NT⽤来控制中断返回指令IRET的执⾏。
linux内核中断处理的irq_thread机制 -回复
linux内核中断处理的irq_thread机制-回复Linux内核中断处理的irq_thread机制是一种处理中断请求的机制,旨在提高中断处理的效率和可扩展性。
在本文中,我们将详细介绍irq_thread 的原理和实现细节,并分析其对系统性能的影响。
一、引言随着计算机系统的发展,硬件设备的种类和数量不断增加,从而引发了对中断处理的需求。
在传统中断处理模式下,Linux内核需要通过一个叫做中断处理程序(interrupt handler)的功能模块来处理硬件设备发出的中断请求。
然而,这种模式在多核系统和高速设备的环境下往往存在性能瓶颈和可扩展性问题。
为了解决这些问题,Linux内核引入了irq_thread机制。
二、irq_thread机制的原理irq_thread机制的核心思想是将中断处理程序从内核上下文中移出,转而在用户态创建一个专门的线程来处理中断请求。
这个线程被称为irq_thread。
irq_thread在内核注册的中断向量表中占据一个特殊的槽位,当对应的硬件设备发生中断时,硬件会触发相应的中断请求,并将中断处理程序的控制权交给irq_thread。
在irq_thread的实现中,内核通过分配一个独立的线程栈和一组寄存器保存irq_thread的上下文。
同时,irq_thread中还有一个运行队列,用于保存需要处理的中断任务。
当一个中断请求到达时,irq_thread会从硬件设备读取中断状况,并将相应的中断任务放入运行队列。
三、irq_thread机制的实现细节1. 中断控制器:irq_thread机制需要硬件支持,其中关键的组件是中断控制器(interrupt controller)。
中断控制器是一种硬件设备,用于管理和分发中断请求。
在Linux内核中,主流的中断控制器有APIC(Advanced Programmable Interrupt Controller)和IOAPIC(Input/Output Advanced Programmable Interrupt Controller)等。
linux 软中断原理
linux 软中断原理Linux软中断是操作系统中一种用于处理紧急任务的机制。
它采用的是一种特殊的中断方式,可以在用户态和内核态之间切换,以提供高效的中断处理能力。
在Linux内核中,软中断是一种特殊的中断处理机制。
相比硬中断,软中断的处理过程更加高效,可以在短时间内完成中断处理,并且不会占用过多的系统资源。
软中断的原理是通过将中断处理函数放入一个队列中,然后由内核线程负责依次执行队列中的中断处理函数。
在Linux内核中,每个软中断都有一个唯一的标识符,并且有一个对应的中断处理函数。
当某个软中断被触发时,中断处理函数会被调用。
这样,操作系统就可以根据不同的软中断来执行相应的中断处理操作。
软中断的使用可以提高系统的响应速度和处理能力。
在Linux内核中,有一些常用的软中断,如定时器中断、网络中断等。
这些软中断可以及时地处理系统中的紧急任务,并且不会对系统的正常运行产生太大的影响。
软中断的原理是基于内核线程的机制实现的。
在Linux内核中,有一个特殊的内核线程,称为软中断处理线程。
这个线程会不断地检查软中断队列,如果队列中有待处理的中断,就会调用相应的中断处理函数。
软中断的实现需要考虑到多核处理器的情况。
在多核处理器中,每个CPU核心都有自己的软中断处理队列和软中断处理线程。
这样,每个CPU核心都可以独立地处理软中断,提高系统的并发处理能力。
软中断的使用可以提高系统的可靠性和稳定性。
在Linux内核中,软中断可以用于处理系统中的紧急任务,如定时器中断、网络中断等。
这些紧急任务需要及时地处理,以保证系统的正常运行。
Linux软中断是一种高效的中断处理机制,可以提高系统的响应速度和处理能力。
它通过将中断处理函数放入一个队列中,然后由专门的内核线程负责执行,可以及时地处理系统中的紧急任务,并且不会对系统的正常运行产生太大的影响。
通过软中断的使用,可以提高系统的可靠性和稳定性,保证系统的正常运行。
