06 Linux中断和中断处理

合集下载

中断和中断处理程序

中断和中断处理程序

中断和中断处理程序1. 中断Linux内核要对连接到计算机上的所有硬件设备进⾏管理,⾸先要能和它们互相通信。

从所周知,处理器的速度跟外围硬件设备的速度往往不在⼀个数量级上。

所以,需要⼀种机制,如果轮询(polling)是⼀种解决办法,可以让内核定期对设备的状态进⾏查询,然后做出相应的处理,但这让内核做了不少⽆⽤功。

更好的办法是由我们来提供⼀种机制,让硬件在需要的时候再向内核发出信号。

这就是中断机制。

中断本质上是⼀种特殊的电信号,由硬件设备⽣成,并直接送⼊中断控制器的输⼊引脚上,再由中断控制器向处理器发送相应的信号,处理器⼀经检测到此信号,便中断⾃⼰当前⼯作转⽽处理中断,最后由OS来负责处理新到来的数据。

中断是异步的。

什么是中断?简单地说就是CPU在忙着作⾃⼰的事情,这时候硬件(⽐如说键盘按了⼀下)触发了⼀个电信号,这个信号通过中断线到达中断控制器i8259A,i8259A接受到这个信号后,向CPU发送INT信号申请CPU来执⾏刚才的硬件操作,并且将中断类型号也发给CPU,此时CPU保存当前正在做的事情(REST指令把程序计数器PC中的下⼀条待执⾏的指令的内存地址保存到栈)的情景现场,然后去处理这个申请,根据中断类型号找到它的中断向量(即中断程序在内存中的地址),然后去执⾏这段程序(这段程序已经写好,在内存中),执⾏完后再向i8259A发送⼀个INTA信号表⽰其已经处理完刚才的申请。

此时CPU就可以继续做它刚才被打断做的事情了,将刚才保存的情景现场恢复出来,CPU继续执⾏接下来下⾯的程序。

不同的设备对应的中断不同,⽽每个中断都通过⼀个唯⼀的数字标识。

这些中断值通常被称为中断请求(IRQ)线。

⽐如,IRQ0是时钟中断,⽽IRQ1是键盘中断。

并不是所有的中断号都这样严格定义,像PCI总线上的设备,中断就是动态分配的。

1.1. 异常与中断异常与中断不同,它在产⽣时必须考虑与处理器时钟同步。

实际上,异常也称为同步中断。

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链表中注册的处理函数。

中断和中断处理流程

中断和中断处理流程

中断和中断处理流程转⾃:1. 中断概念中断是指由于接收到来⾃外围硬件(相对于中央处理器和内存)的异步信号或来⾃软件的同步信号,⽽进⾏相应的硬件/软件处理。

发出这样的信号称为进⾏中断请求(interrupt request,IRQ)。

硬件中断导致处理器通过⼀个上下⽂切换(context switch)来保存执⾏状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为CPU指令集中的⼀个指令,以可编程的⽅式直接指⽰这种上下⽂切换,并将处理导向⼀段中断处理代码。

中断在计算机多任务处理,尤其是实时系统中尤为有⽤。

这样的系统,包括运⾏于其上的操作系统,也被称为“中断驱动的”(interrupt-driven)。

中断是⼀种使CPU中⽌正在执⾏的程序⽽转去处理特殊事件的操作,这些引起中断的事件称为中断源,它们可能是来⾃外设的输⼊输出请求,也可能是计算机的⼀些异常事故或其它内部原因。

中断:在运⾏⼀个程序的过程中,断续地以“插⼊”⽅式执⾏⼀些完成特定处理功能的程序段,这种处理⽅式称为中断。

2. 中断的作⽤并⾏操作硬件故障报警与处理⽀持多道程序并发运⾏,提⾼计算机系统的运⾏效率⽀持实时处理功能3. 术语按中断源进⾏分类:发出中断请求的设备称为中断源。

按中断源的不同,中断可分为1. 内中断:即程序运⾏错误引起的中断2. 外中断:即由外部设备、接⼝卡引起的中断3. 软件中断:由写在程序中的语句引起的中断程序的执⾏,称为软件中断允许/禁⽌(开/关)中断: CPU通过指令限制某些设备发出中断请求,称为屏蔽中断。

从CPU要不要接收中断即能不能限制某些中断发⽣的⾓度,中断可分为1. 可屏蔽中断:可被CPU通过指令限制某些设备发出中断请求的中断,那是不是意味着进中断时disable整个中断,其实disable的都是可屏蔽中断?2. 不可屏蔽中断:不允许屏蔽的中断如电源掉电中断允许触发器:在CPU内部设置⼀个中断允许触发器,只有该触发器置“1”,才允许中断;置“0”,不允许中断。

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内核中断处理机制<什么是中断>计算停下当前处理任务,并保存现场,转⽽去处理其他是任务,当完成任务后再回到原来的任务中去。

<中断的分类>a:软中断软中断时执⾏中断指令产⽣的,软中断不⽤施加中断请求信号,因此中断的产⽣的不是随机的⽽是由程序安排的。

内核线程是实现软中断的助⼿。

b:硬中断硬中断时由外部硬件产⽣的,具有随机性。

<中断的实现>int request_irq(unsigned int irq,irq_handler_t handler,unsigned long intflags,const char devname, void *dev_id)解释:irq:申请的中断号。

handler:中断处理函数指针irqflags:中断处理属性,与上半段和下半段有关系devname:中断设备名字dev_id:与共享中断号有关系。

注:该函数的主要作⽤是在内核中的⼀个重要的结构体"irq_desc"中注册中断号与对应的中断处理函数。

<释放中断线>void free_irq(unsigned int irq ,void dev_id);注:⼀般操作硬件的端⼝都会调⽤ioremap()函数,该函数⽤来将计算实际的物理地址映射成虚拟地址,这样系统才能访问。

<中断处理机制之上半部分和下半部>a:Linux中中断处理是⼀种很霸道的东西,只要没有屏蔽中断,CPU就会⽴即相应。

为了加开处理的数据,Linux中通常将中断处理中的硬件相关的操作放在上半部分,耗时的操作放在下半部分——linux 内核⼀般将中断处理分为两不份:上半部(top_half)和下半部(bottom_half)。

b:上半部分⼀般调⽤中断处理函数,⼀进⼊中断处理函数就是进⾏相应的硬件操作,这些都是放在上半部分。

然后将耗时的操作放在下半部分中。

c:下半部分Linux中实现下半部分的处理有:softirq机制,tasklist机制(⼩任务⽚段),workqueue机制----tasklet将任务延迟到安全时间执⾏,每个tasklet都和⼀个函数相关联,当tasklet运⾏时,----该函数就被调⽤,并且tasklet可以调度⾃⼰。

linux 终止指令运行的方法

linux 终止指令运行的方法

linux 终止指令运行的方法在Linux终端下,可以通过多种方式来终止正在运行的指令。

以下是常用的几种方法:1.使用Ctrl+C组合键:在终端中按下Ctrl键同时再按下C键,即可立即终止当前正在运行的指令。

这是最常用的终止指令的方式。

Ctrl+C会向被运行的程序发送一个中断信号(SIGINT),通常会使程序终止。

2.使用Ctrl+Z组合键:在终端中按下Ctrl键同时再按下Z键,可以将当前正在运行的指令挂起到后台,并返回命令提示符。

这时指令并没有真正停止运行,只是进入了休眠状态。

可以使用`jobs`命令查看后台挂起的程序,并使用`fg`命令将其重新调回前台运行,或使用`bg`命令将其在后台恢复运行。

3.使用kill命令:使用kill命令可以向指定的进程发送信号来终止其运行。

首先需要查找要终止的进程的PID(进程ID),可以使用`ps`命令、`top`命令或者`pgrep`命令等来获取。

然后使用`kill PID`命令,将PID替换为实际的进程ID,即可发送终止信号。

默认情况下,kill命令发送的是TERM信号(15号),但也可以使用其他信号,如INT(2号)或KILL(9号)。

4.使用pkill命令:pkill命令可以直接根据进程名或其他条件来终止指定的进程。

使用`pkill进程名`命令,将"进程名"替换为实际的进程名或匹配条件,即可发送终止信号。

例如`pkill firefox`将会终止所有名为"firefox"的进程。

5.使用killall命令:与pkill类似,killall命令也可以根据进程名来终止指定的进程。

使用`killall进程名`命令,将"进程名"替换为实际的进程名,即可发送终止信号。

与pkill不同的是,killall只能匹配完整的进程名,而不能使用正则表达式。

6.使用xkill命令:在使用图形界面时,可以使用xkill命令来终止指定窗口下的程序。

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


以上是traps.c中的 trap_init()函数片断,trap_init() 函数又被操作系统的初始化工作start_kernel()函数 调用。从中可以看出Linux所处理的异常,异常3-5, 就是中断指令int3,int4,int5可以被所有的进程调 用,而其他的异常只能被处于特权级0的进程调用, 也就是说这些异常只能被内核所处理。
LINUX中断和中断处理
武汉大学计算机学院 郑鹏 Email:pzheng51@
中断的基本概念



在Intel处理器中,中断分为内部中断和外 部中断。内部中断是处理器产生的异常, 而外部中断则由外部设备产生。异常必须 由操作系统处理。 中断的主要功能是允许打断CPU的工作, 告之其它某些急须注意的事件发生。有了 中断机制后,CPU可与外部设备并行工作。 外部中断通常由设备驱动程序处理,由于 使用外部中断的设备经常变化,所以Linux 提供了灵活的机制,让设备驱动程序根据 需要注册自己的中断处理程序。
中断向量表的初始化(5)

init_IRQ()

往中断向量表中填外部中断的中断门,共填 224个
中断向量表的初始化(6)
arch/i386/kernel/i8259.c: #define NR_IRQS 224 #define FIRST_EXTERNAL_VECTOR #define SYSCALL_VECTOR in init_IRQ(): for (i = 0; i < NR_IRQS; i++) { intvector = FIRST_EXTERNAL_VECTOR + i; if (vector != SYSCALL_VECTOR) set_intr_gate(vector, interrupt[i]); }
中断请求队列的初始化(1)



X86只支持256个中断向量,这对众多的外 设显然是不够的 Linux中,每个外部中断向量都有一个中断 请求队列,允许外设的中断为多个中断源 共享 在填中断向量表之前,要对中断请求队列 进行初始化
中断请求队列的初始化(2)
include/linux/irq.h: typedef struct{ unsigned intstatus; hw_irq_controller*handler; struct irqaction*action; unsigned intdepth; spinlock_tlock; } irq_desc_t; //IRQ状态 //中断服务队列共用“中断通道”的控制器 //指向中断服务队列
extern irq_desc_tirq_desc[NR_IRQS];
//中断请求队列数组
struct irqaction{ void (*handler)(int, void *, structpt_regs*);//指向具体的设备中断处理程序 unsigned long flags; unsigned long mask; const char *name; void *dev_id; struct irqaction*next; };
中断的基本概念

中断分为硬中断和软中断。 硬中断:来自硬件,如外设。 软中断:来自INT n指令。 异常:来自于处理器,由执行指令引发的。
X86中断的硬件支持


中断控制器:PC使用Intel 82C59A-2 CMOS可编程控 制器。此控制器的寄存器在ISA系统的内存空间中占 有固定位置。硬件设备通过该控制器向CPU发出中断 请求。 中断向量表寄存器TDTR:存放中断向量表IDT的基址。 CPU的四种门:任务门,中断门,陷阱门和调用门。 任务状态段TSS:
中断向量表的初始化(1)

异常和系统调用

填陷阱门 不同的异常的中断服务程序各不相同 由trap_init()填写中断向量
填中断门 外部中断服务程序实际上都跳转统一的中断服务 程序 由init_IRQ()填写中断向量

外部中断


中断向量表的初始化(2)

trap_init()

在中断向量表中填上int0-int 19的中断向量,并 填上system_call的中断向量,每个中断向量表 项填上相应的门(外部中断填中断门,异常填陷 阱门)。 set_system_gate(0x80, &system_call) –陷阱门,中断号0x80,入口地址system_call –int0x80将触发一个系统调用
可编程中断控制器

当发生中断信号时,中断处理程序读取两个中断状态寄存器 (ISR)的值。它将位于0x20的ISR放入一个16位的中断寄存 器的低8位,同时将位于0xA0的ISR放入高8位。PIC1中的第 二位用于中断控制器的级连,所以任何来自PIC2的中断都将 导致PIC1中的第二位置1。
中断向量表的初始化

0x20 0x80

interrupt[i]为函数指针数组,这些函数指 针实际上是宏,在形式上它们是一样的。 必须跳过用于系统调用的向量0x80。
中断向量表的初始化(7)


下面是一个外部中断服务程序的例子: asmlinkvoid IRQ0x01_interrupt(); __asm__(\ ―\n‖\ ―IRQ0x01_interrupt: \n\t‖\ ―pushl $0x01 –256\n\t‖ //为了避免与内部中断号冲突 “jmp common_interrupt‖);
中断请求队列的初始化(3)
struct hw_interrupt_type{ arch/i386/kernel/i8259.c: const char * typename; void __init init_ISA_irqs(void) unsigned int(*startup)(unsignedintirq); { inti; void (*shutdown)(unsignedintirq); #ifdefCONFIG_X86_LOCAL_APIC void (*enable)(unsignedintirq); init_bsp_APIC(); void (*disable)(unsignedintirq); void (*ack)(unsignedintirq); #endif void (*end)(unsignedintirq); init_8259A(0); void (*set_affinity)(unsignedintirq, unsigned long mask); for (i = 0; i < NR_IRQS; i++) { }; irq_desc[i].status= IRQ_DISABLED; typedef struct hw_interrupt_type hw_irq_controller; irq_desc[i].action= 0; static structhw_interrupt_type i8259A_irq_type = { irq_desc[i].depth= 1; "XT-PIC", if (i < 16) { // 16 old-style INTA-cycle interrupts: startup_8259A_irq, irq_desc[i].handler= &i8259A_irq_type; shutdown_8259A_irq, } else { // 'high' PCI IRQsfilled in on demand enable_8259A_irq, irq_desc[i].handler= &no_irq_type; disable_8259A_irq, } mask_and_ack_8259A,end_8259A_irq, NULL } }; }
中断向量表的初始化(4)

_set_gate()具体负责向中断向量表中填数据。这里 的set_XXXX_gate()都会调用_set_gate(),只是所 传的参数值不同:

set_trap_gate()type:陷阱门,dpl=0 set_intr_gate() type:中断门,dpl=0 set_system_gate()type:陷阱门,dpl=3

trap_init()(arch/i386/kernel/traps.c):


单一入口,通过使用系统调用号标识具体的 系统调用
中断向量表的初始化(3)

arch/i386/kernel/traps.c: #define _set_gate(gate_addr,type,dpl,addr) In trap_int(): set_trap_gate(0,&divide_error); set_trap_gate(1,&debug); set_intr_gate(2,&nmi); set_system_gate(3,&int3); /* int3-5 can be called from all */ set_system_gate(4,&overflow); set_system_gate(5,&bounds); set_trap_gate(6,&invalid_op); set_trap_gate(7,&device_not_available); set_trap_gate(8,&double_fault); set_trap_gate(9,&coprocessor_segment_overrun); set_trap_gate(10,&invalid_TSS); set_trap_gate(11,&segment_not_present); set_trap_gate(12,&stack_segment); set_trap_gate(13,&general_protection); set_intr_gate(14,&page_fault); set_trap_gate(15,&spurious_interrupt_bug); set_trap_gate(16,&coprocessor_error); set_trap_gate(17,&alignment_check); set_trap_gate(18,&machine_check); set_trap_gate(19,&simd_coprocessor_error); set_system_gate(SYSCALL_VECTOR,&system_call); /*default LDT is a single-entry callgate to lcall7 for iBCS * and a callgate to lcall27 for Solaris/x86 binaries */ set_call_gate(&default_ldt[0],lcall7); set_call_gate(&default_ldt[4],lcall27);
相关文档
最新文档