linux 中断分析

合集下载

Linux网络疑难杂症解决断网问题的几个窍门

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中断处理流程

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 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中断处理是操作系统中的一个重要组成部分,用于响应硬件设备的事件。

在Linux中,中断可以是外部中断,如硬件设备发送的中断信号,也可以是内部中断,如软件产生的异常或系统调用。

中断处理的目的是及时响应硬件设备的事件,并采取相应的措施来处理这些事件。

一、中断的触发中断是由硬件设备发送的一个信号,用于通知操作系统某个事件的发生。

这个信号可以是一个电平的变化,一个特定的数据包,或者一个指定的硬件寄存器的变化。

当硬件设备检测到某个事件发生时,它会向处理器发送一个中断信号,处理器会立即停止当前正在执行的任务,保存当前的上下文,并跳转到中断处理程序的入口点。

二、中断处理程序的执行中断处理程序是一个特殊的函数,负责处理中断事件。

当中断发生时,处理器会跳转到中断处理程序的入口点,并执行相应的代码。

中断处理程序的执行过程可以分为以下几个步骤:1. 保存上下文:在执行中断处理程序之前,处理器需要保存当前任务的上下文,包括程序计数器、寄存器和堆栈指针等。

这样可以确保在中断处理程序执行完成后,能够正确地返回到原来的任务。

2. 中断处理程序的执行:一旦保存了上下文,处理器就会执行中断处理程序的代码。

中断处理程序根据中断的类型,执行相应的操作。

例如,对于外部中断,中断处理程序可能需要读取硬件设备的状态,处理数据包或执行特定的操作。

对于内部中断,中断处理程序可能需要处理异常或系统调用。

3. 中断处理程序的结束:当中断处理程序执行完成后,处理器会恢复之前保存的上下文,并将控制权返回给原来的任务。

这样原来的任务就可以继续执行,而不会受到中断的影响。

三、中断处理的优先级在Linux中,中断处理有不同的优先级。

这是为了确保对于紧急事件的及时处理。

中断的优先级由硬件设备决定,通常是通过一个优先级编码器来实现的。

当多个中断同时发生时,处理器会按照优先级的顺序来处理中断。

高优先级的中断会立即被处理,而低优先级的中断则会被推迟到稍后处理。

linux中断处理之IRQ中断

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, &regs, 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) &regs;*--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, &regs);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的系统内核,经过⼀段时间的学习,先整理出⼀份关于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中的时钟中断代码的⼀点理解在读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机制-回复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内核中,有一些常用的软中断,如定时器中断、网络中断等。

这些软中断可以及时地处理系统中的紧急任务,并且不会对系统的正常运行产生太大的影响。

软中断的原理是基于内核线程的机制实现的。

在Linux内核中,有一个特殊的内核线程,称为软中断处理线程。

这个线程会不断地检查软中断队列,如果队列中有待处理的中断,就会调用相应的中断处理函数。

软中断的实现需要考虑到多核处理器的情况。

在多核处理器中,每个CPU核心都有自己的软中断处理队列和软中断处理线程。

这样,每个CPU核心都可以独立地处理软中断,提高系统的并发处理能力。

软中断的使用可以提高系统的可靠性和稳定性。

在Linux内核中,软中断可以用于处理系统中的紧急任务,如定时器中断、网络中断等。

这些紧急任务需要及时地处理,以保证系统的正常运行。

Linux软中断是一种高效的中断处理机制,可以提高系统的响应速度和处理能力。

它通过将中断处理函数放入一个队列中,然后由专门的内核线程负责执行,可以及时地处理系统中的紧急任务,并且不会对系统的正常运行产生太大的影响。

通过软中断的使用,可以提高系统的可靠性和稳定性,保证系统的正常运行。

Linux系统基于PIC中断实现的分析

Linux系统基于PIC中断实现的分析
l 6位选择器 由如下所示 的三个部 分组 成 : l Biblioteka 索 引 l
1 r I

RL P

其 中:P R L是请 求者特权 级 , 位 ;I 2 T 是选择域 , 位 , 明描述符 的来源 ( D 1 说 G T或 L T 。 D )
索引域 : 以该域 的值 作为在 1 确定 的描述符表中选择描述 符的索引 , 以该描述 符 中的段基 址作为 中断处理 程序的 r I 并
维普资讯
第 2 卷第 3 3 期
V0. 3 No 3 12 . .
攀枝花学 院学报
Ju a fP n hh a U iest o r lo az iu nv ri n y
20 06年 6月
Jn2 o u .0 6

自然科学 研 究 ・
二 、iu Lnx系统 中断 的软 件组织
1 中断向量 、 每个中断和异常都可以用 —个 0- 5 2 5之间的无符号整数来标识 , 称为中断向量 (nerp etr 。通常 , It ut c ) r V o 不可屏蔽 中断 和异常的 中断向量是 固定的 , 而可屏蔽中断的中断向量则可以对中断控制器进行编程来改变。2 6个中断向量分配如下 : 5
讨论后一种 中断。 .
异常分为处理器探 测异常和编程异 常。编程异 常通 常也称 为软中断 ; 而处理器探 测异常根据 C U控制单元产生异常 P
时保存在 内核态堆栈 E i p寄存 器的值 , 又分为故障 (al 、 (r ) 异常 中止 ( br 三 种。 中断分 为可屏蔽 中断和 Fu )陷阱 Ta 及 t p A o) t
0~3 共 3 向量用 于异常和不可屏蔽 中断 ;2— 7 l l 2个 3 4 共 6个向量用 于可屏蔽 中断 , 对应 于主 、 85 A中断控制器 从 29 的 I Q输入端 ;8~ 5 R 4 2 5用于软 中断 。

Linux内核中的时钟中断tic...

Linux内核中的时钟中断tic...

第七章 Linux内核的时钟中断(By 詹荣开,NUDT)Copyright © 2003 by 詹荣开E-mail:***************Linux-2.4.0Version 1.0.0,2003-2-14摘要:本文主要从内核实现的角度分析了Linux 2.4.0内核的时钟中断、内核对时间的表示等。

本文是为那些想要了解Linux I/O子系统的读者和Linux驱动程序开发人员而写的。

关键词:Linux、时钟、定时器申明:这份文档是按照自由软件开放源代码的精神发布的,任何人可以免费获得、使用和重新发布,但是你没有限制别人重新发布你发布内容的权利。

发布本文的目的是希望它能对读者有用,但没有任何担保,甚至没有适合特定目的的隐含的担保。

更详细的情况请参阅GNU通用公共许可证(GPL),以及GNU自由文档协议(GFDL)。

你应该已经和文档一起收到一份GNU通用公共许可证(GPL)的副本。

如果还没有,写信给:The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA欢迎各位指出文档中的错误与疑问。

前言时间在一个操作系统内核中占据着重要的地位,它是驱动一个OS内核运行的“起博器”。

一般说来,内核主要需要两种类型的时间:1. 在内核运行期间持续记录当前的时间与日期,以便内核对某些对象和事件作时间标记(timestamp,也称为“时间戳”),或供用户通过时间syscall进行检索。

2. 维持一个固定周期的定时器,以提醒内核或用户一段时间已经过去了。

PC机中的时间是有三种时钟硬件提供的,而这些时钟硬件又都基于固定频率的晶体振荡器来提供时钟方波信号输入。

这三种时钟硬件是:(1)实时时钟(Real Time Clock,RTC);(2)可编程间隔定时器(Programmable Interval Timer,PIT);(3)时间戳计数器(Time Stamp Counter,TSC)。

ARM Linux对中断的处理

ARM Linux对中断的处理
int (*set_wake)(unsigned int irq, unsigned int on);
void (*bus_lock)(unsigned int irq);
void (*bus_sync_unlock)(unsigned int irq);
/* Currently used only by UML, might disappear one day.*/
kstat_irqs: irq stats per cpu
irq_2_iommu: iommu with this irq
handle_irq:高层的irq时间处理程序(如果为NULL,则默认调用__do_IRQ())
chip:底层的中断硬件访问,指向PIC对象(irq_chip结构),它服务于IRQ线,Linux中断管理系统使用该成员来进行中断控制器的访问。
struct proc_dir_entry *dir;
#endif
const char *name;
}____cacheline_internodealigned_in_smp;
irq_desc结构体(中断描述符)中各个字段说明:
irq:中断描述符的中断号
timer_rand_state: pointer to timer rand state struct
#endif
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 */

嵌入式Linux系统中断设计要点详解

嵌入式Linux系统中断设计要点详解

类型 、 体系结构的设计及机器本 身。Ln x中断移植 不仅 需要 i u
充 分 了解 使 用 片 的 中 断 设 计 , 时还 需要 对 Ln x内 核 中 断 同 iu 系 统 的 结 构 与 运 作 有 较 深 入 的 理 解 。 i x内核 代 码 极 为 庞 人 Ln u 和 复 杂 , 提 供 的 A I 多 , 给 Lnx的 中 断 移 植 造 成 了 内核 P众 这 iu
211 中断 控 制 器 描 述 符 i c p .. r hi q
_
图 1 Ln x内核 中断 函 数 抽 象 图 iu
很大 的困难 。因此, 本文 以三星公司 的 ¥ C 4 0芯片为例, 3 24 对
嵌 入 式 Ln x版 本 263 1 中 断 系 统 进 行 分 析 , 绍 嵌 入 式 iu ( .. 的 2 介 中断设计的要点与优化细节。
便中断 自己的当前工作转 而处理中断。此后 , 处理器会通 知操 作系统 已经产生 中断, 这样 , 操作系统就可 以对 这个 中断进行
作者 简 介 :罗智 杰 , , 东广 州人 , 学士 , 理 工 程 师 。主 要 研 究方 向 : n x内核 开发 , 入 式 系统 设 计 男 广 双 助 lu i 嵌
21 中断 服 务 例 程 描 述 符 i at n .. 2 r ci q o 在 I Q 描 述 符 中 我 们 看 到 指 针 at n 的 结 构 为 i a— R ci o r c q t n 它 是 为 多 个 设 备 能 共 享 条 中 断 线 而 设 鬣 的 一 数 据 结 i , o 个
21 中 断方 式 设置 符 i o ade .. 3 r f w hn lr ql i o a de 要 明 确 了 内核 中 各 种 类 型 的 中 断 处 r f w hnl ql r主 理 方 式 ,用 户 可 以根 据 自己 需 要 的 形 式 制 定 不 同 的 中 断 处 理

【原创】Linux中断子系统(四)-Workqueue

【原创】Linux中断子系统(四)-Workqueue

【原创】Linux中断子系统(四)-Workqueue背景•Read the fucking source code! --By 鲁迅•A picture is worth a thousand words. --By 高尔基说明:1.Kernel版本:4.142.ARM64处理器,Contex-A53,双核3.使用工具:Source Insight 3.5, Visio1. 概述•Workqueue工作队列是利用内核线程来异步执行工作任务的通用机制;•Workqueue工作队列可以用作中断处理的Bottom-half机制,利用进程上下文来执行中断处理中耗时的任务,因此它允许睡眠,而Softirq和Tasklet在处理任务时不能睡眠;来一张概述图:•在中断处理过程中,或者其他子系统中,调用workqueue的调度或入队接口后,通过建立好的链接关系图逐级找到合适的worker,最终完成工作任务的执行;2. 数据结构2.1 总览此处应有图:•先看看关键的数据结构:1.<>e>work_struct:工作队列调度的最小单位,work item;2.workqueue_struct:工作队列,work item都挂入到工作队列中;3.worker:work item的处理者,每个worker对应一个内核线程;4.worker_pool:worker池(内核线程池),是一个共享资源池,提供不同的worker来对work item进行处理;5.pool_workqueue:充当桥梁纽带的作用,用于连接workqueue和worker_pool,建立链接关系;下边看看细节吧:2.2 workstruct work_struct用来描述work,初始化一个work并添加到工作队列后,将会将其传递到合适的内核线程来进行处理,它是用于调度的最小单位。

关键字段描述如下:struct work_struct {atomic_long_t data; //低比特存放状态位,高比特存放worker_pool的ID或者pool_workqueue的指针struct list_head entry; //用于添加到其他队列上work_func_t func; //工作任务的处理函数,在内核线程中回调#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;#endif};图片说明下data字段:2.3 workqueue•内核中工作队列分为两种:1.bound:绑定处理器的工作队列,每个worker创建的内核线程绑定到特定的CPU上运行;2.unbound:不绑定处理器的工作队列,创建的时候需要指定WQ_UNBOUND标志,内核线程可以在处理器间迁移;•内核默认创建了一些工作队列(用户也可以创建):1.system_mq:如果work item执行时间较短,使用本队列,调用schedule[_delayed]_work[_on]()接口就是添加到本队列中;2.system_highpri_mq:高优先级工作队列,以nice值-20来运行;3.system_long_wq:如果work item执行时间较长,使用本队列;4.system_unbound_wq:该工作队列的内核线程不绑定到特定的处理器上;5.system_freezable_wq:该工作队列用于在Suspend时可冻结的work item;6.system_power_efficient_wq:该工作队列用于节能目的而选择牺牲性能的work item;7.system_freezable_power_efficient_wq:该工作队列用于节能或Suspend时可冻结目的的work item;struct workqueue_struct关键字段介绍如下:struct workqueue_struct {struct list_head pwqs; /* WR: all pwqs of this wq */ //所有的pool_workqueue都添加到本链表中struct list_head list; /* PR: list of all workqueues */ //用于将工作队列添加到全局链表workqueues中struct list_head maydays; /* MD: pwqs requesting rescue */ //rescue状态下的pool_workqueue添加到本链表中struct worker *rescuer; /* I: rescue worker */ //rescuer内核线程,用于处理内存紧张时创建工作线程失败的情况struct pool_workqueue *dfl_pwq; /* PW: only for unbound wqs */char name[WQ_NAME_LEN]; /* I:workqueue name *//* hot fields used during command issue, aligned to cacheline */unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */ //Per-CPU都创建pool_workqueuestruct pool_workqueue __rcu *numa_pwq_tbl[]; /* PWR: unbound pwqs indexed by node */ //Per-Node创建pool_workqueue...};2.4 worker•每个worker对应一个内核线程,用于对work item的处理;•worker根据工作状态,可以添加到worker_pool的空闲链表或忙碌列表中;•worker处于空闲状态时并接收到工作处理请求,将唤醒内核线程来处理;•内核线程是在每个worker_pool中由一个初始的空闲工作线程创建的,并根据需要动态创建和销毁;关键字段描述如下:struct worker {/* on idle list while idle, on busy hash table while busy */union {struct list_head entry; /* L: while idle */ //用于添加到worker_pool的空闲链表中struct hlist_node hentry; /* L: while busy */ //用于添加到worker_pool的忙碌列表中};struct work_struct *current_work; /* L: work being processed */ //当前正在处理的workwork_func_t current_func; /* L: current_work's fn */ //当前正在执行的work回调函数struct pool_workqueue *current_pwq; /* L: current_work's pwq */ //指向当前work所属的pool_workqueuestruct list_head scheduled; /* L: scheduled works */ //所有被调度执行的work都将添加到该链表中/* 64 bytes boundary on 64bit, 32 on 32bit */struct task_struct *task; /* I: worker task */ //指向内核线程struct worker_pool *pool; /* I: the associated pool */ //该worker所属的worker_pool/* L: for rescuers */struct list_head node; /* A: anchored at pool->workers */ //添加到worker_pool->workers链表中/* A: runs through worker->node */...};2.5 worker_pool•worker_pool是一个资源池,管理多个worker,也就是管理多个内核线程;•针对绑定类型的工作队列,worker_pool是Per-CPU创建,每个CPU都有两个worker_pool,对应不同的优先级,nice值分别为0和-20;•针对非绑定类型的工作队列,worker_pool创建后会添加到unbound_pool_hash哈希表中;•worker_pool管理一个空闲链表和一个忙碌列表,其中忙碌列表由哈希管理;关键字段描述如下:struct worker_pool {spinlock_t lock; /* the pool lock */int cpu; /* I: the associated cpu */ //绑定到CPU的workqueue,代表CPU IDint node; /* I: the associated node ID */ //非绑定类型的workqueue,代表内存Node IDint id; /* I: pool ID */unsigned int flags; /* X: flags */unsigned long watchdog_ts; /* L: watchdog timestamp */struct list_head worklist; /* L: list of pending works */ //pending状态的work添加到本链表int nr_workers; /* L: total number of workers */ //worker的数量/* nr_idle includes the ones off idle_list for rebinding */int nr_idle; /* L: currently idle ones */struct list_head idle_list; /* X: list of idle workers */ //处于IDLE状态的worker添加到本链表struct timer_list idle_timer; /* L: worker idle timeout */struct timer_list mayday_timer; /* L: SOS timer for workers *//* a workers is either on busy_hash or idle_list, or the manager */DECLARE_HASHTABLE(busy_hash,BUSY_WORKER_HASH_ORDER); //工作状态的worker添加到本哈希表中/* L: hash of busy workers *//* see manage_workers() for details on the two manager mutexes */struct worker *manager; /* L: purely informational */struct mutex attach_mutex; /* attach/detach exclusion */struct list_head workers; /* A: attached workers */ //worker_pool管理的worker添加到本链表中struct completion *detach_completion; /* all workers detached */struct ida worker_ida; /* worker IDs for task name */struct workqueue_attrs *attrs; /* I: worker attributes */struct hlist_node hash_node; /* PL: unbound_pool_hash node */ //用于添加到unbound_pool_hash中...} ____cacheline_aligned_in_smp;2.6 pool_workqueue•pool_workqueue充当纽带的作用,用于将workqueue和worker_pool关联起来;关键字段描述如下:struct pool_workqueue {struct worker_pool *pool; /* I: the associated pool */ //指向worker_poolstruct workqueue_struct *wq; /* I: the owning workqueue */ //指向所属的workqueueint nr_active; /* L: nr of active works */ //活跃的work数量int max_active; /* L: max active works */ //活跃的最大work数量struct list_head delayed_works; /* L: delayed works */ //延迟执行的work挂入本链表struct list_head pwqs_node; /* WR: node on wq->pwqs */ //用于添加到workqueue链表中struct list_head mayday_node; /* MD: node on wq->maydays */ //用于添加到workqueue链表中...} __aligned(1 << WORK_STRUCT_FLAG_BITS);2.7 小结再来张图,首尾呼应一下:3. 流程分析3.1 workqueue子系统初始化•workqueue子系统的初始化分成两步来完成的:workqueue_init_early和workqueue_init。

Linux之时钟中断详解

Linux之时钟中断详解

Linux之时钟中断详解⽬录时钟中断的产⽣Linux实现时钟中断的全过程1.可编程定时/计数器的初始化2.与时钟中断相关的函数3.系统调⽤返回函数:总结在Linux的0号中断是⼀个定时器中断。

在固定的时间间隔都发⽣⼀次中断,也是说每秒发⽣该中断的频率都是固定的。

该频率是常量HZ,该值⼀般是在100 ~ 1000之间。

该中断的作⽤是为了定时更新系统⽇期和时间,使系统时间不断地得到跳转。

另外该中断的中断处理函数除了更新系统时间外,还需要更新本地CPU统计数。

指的是调⽤scheduler_tick递减进程的时间⽚,若进程的时间⽚递减到0,进程则被调度出去⽽放弃CPU使⽤权。

时钟中断的产⽣Linux的OS时钟的物理产⽣原因是可编程定时/计数器产⽣的输出脉冲,这个脉冲送⼊CPU,就可以引发⼀个中断请求信号,我们就把它叫做时钟中断。

“时钟中断”是特别重要的⼀个中断,因为整个操作系统的活动都受到它的激励。

系统利⽤时钟中断维持系统时间、促使环境的切换,以保证所有进程共享CPU;利⽤时钟中断进⾏记帐、监督系统⼯作以及确定未来的调度优先级等⼯作。

可以说,“时钟中断”是整个操作系统的脉搏。

时钟中断的物理产⽣如图所⽰:操作系统对可编程定时/计数器进⾏有关初始化,然后定时/计数器就对输⼊脉冲进⾏计数(分频),产⽣的三个输出脉冲Out0、Out1、Out2各有⽤途,很多接⼝书都介绍了这个问题,我们只看Out0上的输出脉冲,这个脉冲信号接到中断控制器8259A_1的0号管脚,触发⼀个周期性的中断,我们就把这个中断叫做时钟中断,时钟中断的周期,也就是脉冲信号的周期,我们叫做“滴答”或“时标”(tick)。

从本质上说,时钟中断只是⼀个周期性的信号,完全是硬件⾏为,该信号触发CPU去执⾏⼀个中断服务程序,但是为了⽅便,我们就把这个服务程序叫做时钟中断。

Linux实现时钟中断的全过程1.可编程定时/计数器的初始化IBM PC中使⽤的是8253或8254芯⽚。

linux内核中断处理的irq_thread机制

linux内核中断处理的irq_thread机制

Linux内核中断处理的irq_thread机制是一种将中断处理任务分配给单独线程的方法,以提高Linux内核中断处理的irq_thread机制是一种将中断处理任务分配给单独线程的方法,以提高系统的性能和响应速度。

在传统的中断处理模型中,中断处理程序(IRQ handler)是在中断发生时由内核直接调用的,这会导致CPU上下文切换,从而影响系统性能。

而使用irq_thread机制,可以将中断处理任务分配给一个专门的线程来执行,从而减少上下文切换的次数,提高系统的响应速度。

irq_thread机制的主要步骤如下:
1. 当中断发生时,内核首先将中断处理程序标记为可运行状态。

2. 然后,内核启动一个新的线程来执行这个中断处理程序。

这个线程通常被称为irq_thread。

3. irq_thread线程在执行过程中会调用do_IRQ()函数来处理实际的中断事件。

4. do_IRQ()函数会根据中断类型调用相应的硬件抽象层(HAL)中的处理函数,以完成对硬件设备的操作。

5. 最后,irq_thread线程会返回到用户空间,继续执行之前的程序。

通过使用irq_thread机制,Linux内核可以在多个处理器核心之间实现高效的中断处理,从而提高系统的整体性能。

同时,这种机制还可以简化中断处理程序的设计,使其更加模块化和易于维护。

MIPS Linux异常中断处理

MIPS Linux异常中断处理

MIPS Linux异常中断代码分析CurrentVersion: 0.16Date: 2007-04-12Author: Dajie Tan <jiankemeng@>版本历史版本状态作 者参与者起止日期备注0.10Dajie Tan07-04-12完成草稿0.12Dajie Tan07-04-19完善A 0.14Dajie Tan07-08-17完善B.1 0.15Dajie Tan07-08-19完善B.2 0.16Dajie Tan07-09-10完善B.3本文拟以龙芯2E (兼容MIPS III)为例,对内核的异常、中断系统作一个框架式的描述,将结合2.6.18的代码来说明。

A. 概述龙芯2E在高优先级异常出现时,在设置了某些寄存器后,会根据异常类型跳转到相应的固定地址处(高优先级异常服务入口),操作系统会将相应的异常处理程序,置于这些地址处。

龙芯2E的高优先级异常有:冷启动、热重启、非屏蔽中断,TLB重填(32位模式),xTLB重填(64位模式),cache错误,其他异常。

龙芯2E之高优先级异常入口地址有以下五个:Table A.1 优先级异常入口当龙芯正常运行时,STATUS 寄存器之BEV位为0 ,0xFFFFFFFF 80000000地址处不经TLB映射、但缓存;当龙芯启动时,STATUS 寄存器之BEV位为1,0xFFFF FFFF BFC0 0200 地址处龙芯不缓存、不经TLB映射。

MIPS 下TLB、Cache都要OS参与管理,在其启动时OS尚未接管系统,这个时候不采用TLB、Cache机制是很重要的。

注意,冷启动、热重启、非屏蔽中断的入口地址始终位于0xFFFF FFFF BFC0 0000由此可见龙芯2E启动时(冷启动异常或者热重启异常),执行的第一条指令是位于地址0xFFFF FFFF BFC0 0000 处的,实际上龙芯电脑上所用之BOOTLOADER(PMON)的第一条指令就映射在地址空间的0xFFFF FFFF BFC0 0000处。

IA32上Linux内核中断机制分析

IA32上Linux内核中断机制分析
426movl%esp,%eax;\
427callsmp_/**/name;\
428jmpret_from_intr;
其中,SAVE_ALL宏就是用来保存寄存器的。
内核书籍中经常提到的中断上下文,指的是内核正在运行中断服务程序或softirq,无法代表当前进程的情形。中断上下文没有自己专有的堆栈,相反,它借用被中断进程的内核堆栈──IA-32上的Linux默认这个堆栈只有8k大小,而且很可能在处理中断的过程中又被另一个中断源中断。因此如果你自己编写中断处理程序,递归层次太深或者函数局部变量太大,都有可能导致栈溢出。(i386有一个4KStacks补丁,如果编译时打开该选项,则中断上下文使用独立的栈,而不占用被中断进程的。)
2)陷阱(Trap)。陷阱处理返回时,不重新执行引发陷阱的那条指令。
3)中止(Abort)。中止是严重的异常,将导致任务的中止而不会返回。
还有一种是程序产生的异常,如INT3指令、BOUND指令等。CPU把这种异常当作是陷阱来处理。
5,中断描述表IDT
异常与中断发生时,都需要到IDT中查找相关信息,以找到对应的处理程序以及其他动作。需要注意的是,保护模式下发生权限提升时,中断穿越的是中断门,而异常穿越的是陷阱门。二者的区别是:当CPU穿越中断门时,是自动关中断的;而穿越异常门则不会。
return1;
}
spin_lock(&desc->lock);
desc->handler->ack(irq);/*给i8259A或APIC应答信号*/
/*
*REPLAYiswhenLinuxresendsanIRQthatwasdroppedearlier
*WAITINGisusedbyprobetomarkirqsthatarebeingtested

linux的irq中断子系统分析

linux的irq中断子系统分析

本文以Linux中断子系统架构为视角,旨在提供一个对Linux中断系统的全局认识,不涉及具体实现细节。

一、Linux中断子系统架构在Linux中断子系统(generic irq)出现之前,内核使用__do_IRQ处理所有的中断,这意味着__do_IRQ中要处理各种类型的中断,这会导致软件的复杂性增加,层次不分明,而且代码的可重用性也不好。

通用中断子系统的原型最初出现于ARM体系中,一开始内核的开发者们把3种中断类型区分出来(电平中断、边缘中断、简易中断),后来又针对某些需要回应eoi(end of interrupt)的中断控制器,加入了fast eoi type,针对smp加入了per cpu type。

把这些不同的中断类型抽象出来后,成为了中断子系统的流控层。

要使所有的体系架构都可以重用这部分的代码,中断控制器也被进一步地封装起来,形成了中断子系统中的芯片级硬件封装层。

二、芯片级硬件封装层中断系统与CPU硬件关系密切,linux系统为了兼容各种型号的CPU,提供了对于各种CPU的特性及其中断控制器的底层封装,这样就可以把底层的硬件实现尽可能地隐藏起来,使得驱动程序的开发人员不用关注底层的实现。

该部分主要工作是:●实现不同CPU的中断入口,初始化中断向量表,该部分通常由汇编实现。

●对中断控制器实现软件抽象(struct irq_chip),源码路径如:”arch/arm/plat-s3c24xx/irq.c”该部分初始化过程中,系统根据设备使用的中断控制器的类型,实现irq_chip结构中的接口,并把该irq_chip实例注册到irq_desc.irq_data.chip字段中,这样各个irq和中断控制器就进行了关联,只要知道irq编号,即可得到对应到irq_desc结构,进而可以通过chip指针访问中断控制器。

其初始化流程如下图所示:三、中断流控层由linux内核提供,所谓中断流控是指合理并正确地处理连续发生的中断,比如一个中断在处理中,同一个中断再次到达时如何处理,何时应该屏蔽中断,何时打开中断,何时回应中断控制器等一系列的操作。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

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()时执行。

相关文档
最新文档