理解Linux中断 (2)【转】

转自:http://blog.csdn.net/tommy_wxie/article/details/7425692

版权声明:本文为博主原创文章,未经博主允许不得转载。
3、内核的中断处理
3.1、中断处理入口
由上节可知,中断向量的对应的处理程序位于interrupt数组中,下面来看看interrupt:
[html] view plain copy print?
341 .data #数据段
342 ENTRY(interrupt)
343 .text
344
345 vector=0
346 ENTRY(irq_entries_start)
347 .rept NR_IRQS #348-354行重复NR_IRQS次
348 ALIGN
349 1: pushl $vector-256 #vector在354行递增
350 jmp common_interrupt #所有的外部中断处理函数的统一部分,以后再讲述
351 .data
352 .long 1b #存储着指向349行的地址,但是随着348行-354被gcc展开,每次的值都不同
353 .text
354 vector=vector+1
355 .endr #与347行呼应
356
357 ALIGN  

    #公共处理函数
common_interrupt:
    SAVE_ALL            /*寄存器值入栈*/
    movl %esp,%eax /*栈顶指针保存到eax*/
    call do_IRQ   /*处理中断*/
    jmp ret_from_intr /*从中断返回*/
分析如下:
首先342行和352行都处于.data段,虽然看起来它们是隔开的,但实际上被gcc安排在了连续的数据段内存 中,同理在代码段内存中,354行与350行的指令序列也是连续存储的。另外,348-354行会被gcc展开NR_IRQS次,因此每次352行都会存 储一个新的指针,该指针指向每个349行展开的新对象。最后在代码段内存中连续存储了NR_IRQS个代码片断,首地址由 irq_entries_start指向。而在数据段内存中连续存储了NR_IRQS个指针,首址存储在interrupt这个全局变量中。这样,例如 IRQ号是0 (从init_IRQ()调用,它对应的中断向量是FIRST_EXTERNAL_VECTOR)的中断通过中断门后会触发 interrput[0],从而执行:
pushl 0-256
jmp common_interrupt
的代码片断,进入到Linux内核安排好的中断入口路径。

3.2、数据结构
3.2.1、IRQ描述符
Linux支持多个外设共享一个IRQ,同时,为了维护中断向量和中断服务例程(ISR)之间的映射关系,Linux用一个irq_desc_t数据结构来描述,叫做IRQ描述符。除了分配给异常的
32个向量外,其余224(NR_IRQS)个中断向量对应的IRQ构成一个数组irq_desc[],定义如下:
[html] view plain copy print?
<pre name="code" class="html">//位于linux/irq.h
typedef struct irq_desc {
    unsigned int status;        /* IRQ status */
    hw_irq_controller *handler;
    struct irqaction *action;    /* IRQ action list */
    unsigned int depth;        /* nested irq disables */
    unsigned int irq_count;        /* For detecting broken interrupts */
    unsigned int irqs_unhandled;
    spinlock_t lock;
} ____cacheline_aligned irq_desc_t;  

//IRQ描述符表
extern irq_desc_t irq_desc [NR_IRQS];</pre>
<pre></pre>
<p></p>
<pre></pre>
“____cacheline_aligned”表示这个数据结构的存放按32字节(高速缓存行的大小)进行对齐,以便于将来存放在高速缓存并容易存取。status:描述IRQ中断线状态,在irq.h中定义。如下:
<p></p>
<p><span style="color:#008000"></span></p>
<pre name="code" class="html">#define IRQ_INPROGRESS  1   /* 正在执行这个IRQ的一个处理程序*/
#define IRQ_DISABLED    2    /* 由设备驱动程序已经禁用了这条IRQ中断线 */
#define IRQ_PENDING     4    /* 一个IRQ已经出现在中断线上,且被应答,但还没有为它提供服务 */
#define IRQ_REPLAY      8    /* 当Linux重新发送一个已被删除的IRQ时 */
#define IRQ_AUTODETECT  16  /* 当进行硬件设备探测时,内核使用这条IRQ中断线 */
#define IRQ_WAITING     32   /*当对硬件设备进行探测时,设置这个状态以标记正在被测试的irq */
#define IRQ_LEVEL       64    /* IRQ level triggered */
#define IRQ_MASKED      128    /* IRQ masked - shouldn't be seen again */
#define IRQ_PER_CPU     256     /* IRQ is per CPU */
</pre><br>
<br>
<p></p>
<p><span style="color:#008000"><br>
</span></p>
<p>handler:指向hw_interrupt_type描述符,这个描述符是对中断控制器的描述。下面有描述。<br>
action:指向一个单向链表的指针,这个链表就是对中断服务例程进行描述的irqaction结构。下面有描述。<br>
 <br>
depth:如果启用这条IRQ中断线,depth则为0,如果禁用这条IRQ中断线不止一次,则为一个正数。每当调用一次disable_irq(),该函数就对这个域的值加1;如果depth等于0,该函数就禁用这条IRQ中断线。相反,每当调用enable_irq()函数时,该函数就对这个域的值减1;如果depth变为0,该函数就启用这条IRQ中断线。<br>
lock:保护该数据结构的自旋锁。<span style="color:#008000"><br>
<img alt="" src="http://images.cnblogs.com/cnblogs_com/hustcat/Kernel/kernel_1_4.JPG" height="312" width="701"><br>
IRQ描述符的初始化:<br>
<span style="color:#008000">//</span><span style="color:#008000">位于arch/i386/kernel/i8259.c</span><span style="color:#008000"><br>
</span><span style="color:#0000ff">void</span><span style="color:#000000"> __init init_ISA_irqs (</span><span style="color:#0000ff">void</span><span style="color:#000000">)<br>
{<br>
    </span><span style="color:#0000ff">int</span><span style="color:#000000"> i;<br>
<br>
#ifdef CONFIG_X86_LOCAL_APIC<br>
    init_bsp_APIC();<br>
</span><span style="color:#0000ff">#endif</span><span style="color:#000000"><br>
    </span><span style="color:#008000">//</span><span style="color:#008000">初始化8259A</span><span style="color:#008000"><br>
</span><span style="color:#000000">    init_8259A(</span><span style="color:#800080">0</span><span style="color:#000000">);<br>
    </span><span style="color:#008000">//</span><span style="color:#008000">IRQ描述符的初始化</span><span style="color:#008000"><br>
</span><span style="color:#000000">    </span><span style="color:#0000ff">for</span><span style="color:#000000"> (i </span><span style="color:#000000">=</span><span style="color:#000000"> </span><span style="color:#800080">0</span><span style="color:#000000">; i </span><span style="color:#000000"><</span><span style="color:#000000"> NR_IRQS; i</span><span style="color:#000000">++</span><span style="color:#000000">) {<br>
        irq_desc[i].status </span><span style="color:#000000">=</span><span style="color:#000000"> IRQ_DISABLED;<br>
        irq_desc[i].action </span><span style="color:#000000">=</span><span style="color:#000000"> NULL;<br>
        irq_desc[i].depth </span><span style="color:#000000">=</span><span style="color:#000000"> </span><span style="color:#800080">1</span><span style="color:#000000">;<br>
<br>
        </span><span style="color:#0000ff">if</span><span style="color:#000000"> (i </span><span style="color:#000000"><</span><span style="color:#000000"> </span><span style="color:#800080">16</span><span style="color:#000000">) {<br>
            </span><span style="color:#008000">/*</span><span style="color:#008000"><br>
             * 16 old-style INTA-cycle interrupts:<br>
             </span><span style="color:#008000">*/</span><span style="color:#000000"><br>
            irq_desc[i].handler </span><span style="color:#000000">=</span><span style="color:#000000"> </span><span style="color:#000000">&</span><span style="color:#000000">i8259A_irq_type;<br>
        } </span><span style="color:#0000ff">else</span><span style="color:#000000"> {<br>
            </span><span style="color:#008000">/*</span><span style="color:#008000"><br>
             * 'high' PCI IRQs filled in on demand<br>
             </span><span style="color:#008000">*/</span><span style="color:#000000"><br>
            irq_desc[i].handler </span><span style="color:#000000">=</span><span style="color:#000000"> </span><span style="color:#000000">&</span><span style="color:#000000">no_irq_type;</span><br>
<span style="color:#000000">        }</span><br>
<span style="color:#000000">    }</span><br>
<span style="color:#000000">}</span><br>
</span>从这段程序可以看出,初始化时,让所有的中断线都处于禁用状态;每条中断线上还没有任何中断服务例程(action为0);因为中断线被禁用,因此depth为1;对中断控制器的描述分为两种情况,一种就是通常所说的8259A,另一种是其它控制器。<br>
3.2.2、中断控制器描述符hw_interrupt_type<br>
这个描述符包含一组指针,指向与特定的可编程中断控制器电路(PIC)打交道的低级I/O例程,定义如下:<span style="color:#008000"><br>
</span></p>
<pre name="code" class="html">//位于linux/irq.h
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, cpumask_t dest);
};  

typedef struct hw_interrupt_type  hw_irq_controller;
</pre><br>
Linux除了支持常见的8259A芯片外,也支持其他的PIC电路,如SMP IO-APIC、PIIX4的内部 8259 PIC及 SGI的Visual Workstation Cobalt (IO-)APIC。8259A的描述符如下:<span style="color:#008000"><br>
</span>
<p></p>
<p></p>
<pre name="code" class="html"><span style="color:#008000;">//位于arch/i386/kernel/i8259.c
static struct hw_interrupt_type i8259A_irq_type = {
    "XT-PIC",
    startup_8259A_irq,
    shutdown_8259A_irq,
    enable_8259A_irq,
    disable_8259A_irq,
    mask_and_ack_8259A,
    end_8259A_irq,
    NULL
};</span>
</pre>在这个结构中的第一个域“XT-PIC”是一个名字。接下来,8259A_irq_type包含的指针指向五个不同的函数,这些函数就是对PIC编程的函数。前两个函数分别启动和关闭这个芯片的中断线。但是,在使用8259A芯片的情况下,这两个函数的作用与后两个函数是一样的,后两个函数是启用和禁用中断线。mask_and_ack_8259A函数通过把适当的字节发往8259A I/O端口来应答所接收的IRQ。end_8259A_irq在IRQ的中断处理程序结束时被调用。<br>
<br>
3.2.3、中断服务例程描述符irqaction<br>
为了处理多个设备共享一个IRQ,Linux中引入了irqaction数据结构。定义如下:<span style="color:#008000"><br>
</span><pre name="code" class="html"><span style="color:#008000;">//位于linux/interrupt.h
struct irqaction {
    irqreturn_t (*handler)(int, void *, struct pt_regs *);
    unsigned long flags;
    cpumask_t mask;
    const char *name;
    void *dev_id;
    struct irqaction *next;
    int irq;
    struct proc_dir_entry *dir;
};</span>
</pre>handler:指向一个具体I/O设备的中断服务例程。这是允许多个设备共享同一中断线的关键域。<br>
flags:用一组标志描述中断线与I/O设备之间的关系。<br>
SA_INTERRUPT<br>
中断处理程序必须以禁用中断来执行<br>
SA_SHIRQ<br>
该设备允许其中断线与其他设备共享。<br>
SA_SAMPLE_RANDOM<br>
可以把这个设备看作是随机事件发生源;因此,内核可以用它做随机数产生器。(用户可以从/dev/random 和/dev/urandom设备文件中取得随机数而访问这种特征)<br>
SA_PROBE<br>
内核在执行硬件设备探测时正在使用这条中断线。<br>
name:I/O设备名(通过读取/proc/interrupts文件,可以看到,在列出中断号时也显示设备名。)<br>
dev_id:指定I/O设备的主设备号和次设备号。<br>
next:指向irqaction描述符链表的下一个元素。共享同一中断线的每个硬件设备都有其对应的中断服务例程,链表中的每个元素就是对相应设备及中断服务例程的描述。<br>
irq:IRQ线。<br>
<br>
3.2.4、中断服务例程(Interrupt Service Routine)<br>
在Linux中,中断服务例程和中断处理程序(Interrupt Handler)是两个不同的概念。可以这样认为,中断处理程序相当于某个中断向量的总处理程序,它与中断描述表(IDT)相关;中断服务例程(ISR)在中断处理过程被调用,它与IRQ描述符相关,一般来说,它是设备驱动的一部分。<br>
(1)    注册中断服务例程<br>
中断服务例程是硬件驱动的组成部分,如果设备要使用中断,相应的驱动程序在初始化的过程中可以通过调用request_irq函数注册中断服务例程。<br>
<pre name="code" class="html"><span style="color:#008000;">//位于kernel/irq/manage.c
/*irq:IRQ号
**handler:中断服务例程
**irqflags:SA_SHIRQ,SA_INTERRUPT或SA_SAMPLE_RANDOM
**devname:设备名称,这些名称会被/proc/irq和/proc/interrupt使用
**dev_id:主要用于设备共享
 */
int request_irq(unsigned int irq,
        irqreturn_t (*handler)(int, void *, struct pt_regs *),
        unsigned long irqflags, const char * devname, void *dev_id)
{
    struct irqaction * action;
    int retval;  

    /*
     * Sanity-check: shared interrupts must pass in a real dev-ID,
     * otherwise we'll have trouble later trying to figure out
     * which interrupt is which (messes up the interrupt freeing
     * logic etc).
     */
    if ((irqflags & SA_SHIRQ) && !dev_id)
        return -EINVAL;
    if (irq >= NR_IRQS)
        return -EINVAL;
    if (!handler)
        return -EINVAL;
    //分配数据结构空间
    action = 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;
    //调用setup_irq完成真正的注册,驱动程序也可以调用它来完成注册
    retval = setup_irq(irq, action);
    if (retval)
        kfree(action);  

    return retval;
}</span>
</pre>来看实时时钟初始化函数如何使用request_irq():<span style="color:#008000"><br>
</span><pre name="code" class="html"><span style="color:#008000;">//位于driver/char/rtc.c
static int __init rtc_init(void)
{
request_irq(RTC_IRQ, rtc_int_handler_ptr, SA_INTERRUPT, "rtc", NULL);
}</span>
</pre>再看看时钟中断初始化函数:<span style="color:#008000"><br>
</span><pre name="code" class="html"><span style="color:#008000;">//位于arch/i386/mach_default/setup.c
static struct irqaction irq0  = { timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL};
//由time_init()调用
void __init time_init_hook(void)
{
    setup_irq(0, &irq0);
}</span>
</pre>3.3、中断处理流程<br>
整个流程如下:<span style="color:#008000"><br>
<img alt="" src="http://images.cnblogs.com/cnblogs_com/hustcat/Kernel/kernel_1_5.JPG" height="471" width="692"><br>
</span>所有I/O中断处理函数的过程如下:<br>
(1)把IRQ值和所有寄存器值压入内核栈;<br>
(2) 给与IRQ中断线相连的中断控制器发送一个应答,这将允许在这条中断线上进一步发出中断请求;<br>
(3)执行共享这个IRQ的所有设备的中断服务例程(ISR);<br>
(4)跳到ret_from_intr()处结束。<br>
<br>
3.3.1、保存现场与恢复现场<br>
中断处理程序做的第一件事就是保存现场,由宏SAVE_ALL(位于entry.S中)完成:<span style="color:#008000"><br>
</span><pre name="code" class="html"><span style="color:#008000;">#define SAVE_ALL \
    cld; \
    pushl %es; \
    pushl %ds; \
    pushl %eax; \
    pushl %ebp; \
    pushl %edi; \
    pushl %esi; \
    pushl %edx; \
    pushl %ecx; \
    pushl %ebx; \
    movl $(__USER_DS), %edx; \
    movl %edx, %ds; \
    movl %edx, %es;</span>
</pre>当内核执行SAVE_ALL后,内核栈中的内容如下:<span style="color:#008000"><br>
<img alt="" src="http://images.cnblogs.com/cnblogs_com/hustcat/Kernel/kernel_1_6.JPG" height="326" width="444"><br>
</span>恢复现场由宏RESTORE_ALL完成<br>
<span style="color:#008000"></span><pre name="code" class="html">#define RESTORE_ALL    \
    RESTORE_REGS    \
    addl $4, %esp;    \
1:    iret;        \
.section .fixup,"ax";   \
2:    sti;        \
    movl $(__USER_DS), %edx; \
    movl %edx, %ds; \
    movl %edx, %es; \
    movl $11,%eax;    \
    call do_exit;    \
.previous;        \
.section __ex_table,"a";\
    .align 4;    \
    .long 1b,2b;    \
.previous  

复制代码
</pre><br>
3.3.2、do_IRQ()函数<br>
该函数的大致内容如下:<span style="color:#008000"><br>
</span><pre name="code" class="html"><span style="color:#008000;">//arch/i386/kernel/irq.c
fastcall unsigned int do_IRQ(struct pt_regs *regs)
{
    /* high bits used in ret_from_ code */
    //取得中断号
    int irq = regs->orig_eax & 0xff;
    //增加代表嵌套中断数量的计数器的值,该值保存在current->thread_info->preempt_count
    irq_enter();
    __do_IRQ(irq, regs);
    //减中断计数器preempt_count的值,检查是否有软中断要处理
    irq_exit();
}</span>
</pre>结构体pt_regs如下,位于inclue/asm-i386/ptrace.h:<span style="color:#008000"><br>
</span><pre name="code" class="html">struct pt_regs {
    long ebx;
    long ecx;
    long edx;
    long esi;
    long edi;
    long ebp;
    long eax;
    int  xds;
    int  xes;
    long orig_eax;
    long eip;
    int  xcs;
    long eflags;
    long esp;
    int  xss;
};
</pre><br>
与内核栈相比,是内核栈中内容的一致。<br>
3.3.3、__do_IRQ()函数<br>
该函数的内容如下:<span style="color:#008000"><br>
</span><pre name="code" class="html"><span style="color:#008000;">//位于kernel/irq/handle.c
fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    irq_desc_t *desc = irq_desc + irq;
    struct irqaction * action;
    unsigned int status;  

    kstat_this_cpu.irqs[irq]++;
    if (desc->status & IRQ_PER_CPU) {
        irqreturn_t action_ret;  

        /*
         * No locking required for CPU-local interrupts:
         */
         //确认中断
        desc->handler->ack(irq);
        action_ret = handle_IRQ_event(irq, regs, desc->action);
        if (!noirqdebug)
            note_interrupt(irq, desc, action_ret);
        desc->handler->end(irq);
        return 1;
    }
    /*加自旋锁.对于多CPU系统,这是必须的,因为同类型的其它中断可能产生,并被其它的CPU处理,
    **没有自旋锁,IRQ描述符将被多个CPU同时访问.
    */
    spin_lock(&desc->lock);
    /*确认中断.对于8259A PIC,由mask_and_ack_8259A()完成确认,并禁用当前IRQ线.
    **屏蔽中断是为了确保该中断处理程序结束前,CPU不会又接受这种中断.虽然,CPU在处理中断会自动
    **清除eflags中的IF标志,但是在执行中断服务例程前,可能重新激活本地中断.见handle_IRQ_event.
    */
    /*在多处理器上,应答中断依赖于具体的中断类型.可能由ack方法做,也可能由end方法做.不管怎样,在中断处理结束
    *前,本地APIC不再接收同样的中断,尽管这种中断可以被其它CPU接收.
    */
    desc->handler->ack(irq);
    /*
     * REPLAY is when Linux resends an IRQ that was dropped earlier
     * WAITING is used by probe to mark irqs that are being tested
     */
     //清除IRQ_REPLAY和IRQ_WAITING标志
    status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);  

    /* IRQ_PENDING表示一个IRQ已经出现在中断线上,且被应答,但还没有为它提供服务 */
    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;
    /*现在开始检查是否真的需要处理中断.在三种情况下什么也不做.
    *(1)IRQ_DISABLED被设置.即使在相应的IRQ线被禁止的情况下,do_IRQ()也可能执行.
    *(2)IRQ_INPROGRESS被设置时,在多CPU系统中,表示其它CPU正在处理同样中断的前一次发生.Linux中,同类型
    *中断的中断服务例程由同一个CPU处理.这样使得中断服务例程不必是可重入的(在同一CPU上串行执行).
    *
    *(3)action==NULL.此时,直接跳到out处执行.
    */
    if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
        action = desc->action;
        //清除IRQ_PENDING标志
        status &= ~IRQ_PENDING; /* we commit to handling */  

        /*表示当前CPU正在处理该中断,其它CPU不应该处理同样的中断,而应该让给本CPU处理.一旦设置
        *IRQ_INPROGRESS,其它CPU即使进行do_IRQ,也不会执行该程序段,则action==NULL,则其它CPU什么也不做.
        *当调用handle_IRQ_event执行中断服务例程时,由于释放了自旋锁,其它CPU可能接受到同类型的中断(本CPU
        *不会接受同类型中断),而进入do_IRQ(),并设置IRQ_PENDING.
        */
        status |= IRQ_INPROGRESS; /* we are handling it */
    }
    desc->status = status;  

    /*
     * If there is no IRQ handler or it was disabled, exit early.
     * Since we set PENDING, if another processor is handling
     * a different instance of this same irq, the other processor
     * will take care of it.
     */
    if (unlikely(!action))
        goto out;  

    /*
     * Edge triggered interrupts need to remember
     * pending events.
     * This applies to any hw interrupts that allow a second
     * instance of the same irq to arrive while we are in do_IRQ
     * or in the handler. But the code here only handles the _second_
     * instance of the irq, not the third or fourth. So it is mostly
     * useful for irq hardware that does not mask cleanly in an
     * SMP environment.
     */
    for (;;) {
        irqreturn_t action_ret;
        //释放自旋锁
        spin_unlock(&desc->lock);  

        action_ret = handle_IRQ_event(irq, regs, action);
        //加自旋锁
        spin_lock(&desc->lock);
        if (!noirqdebug)
            note_interrupt(irq, desc, action_ret);
        /*如果此时IRQ_PENDING处于清除状态,说明中断服务例程已经执行完毕,退出循环.反之,说明在执行中断服务例程时,
        *其它CPU进入过do_IRQ,并设置了IRQ_PENDING.也就是说其它CPU收到了同类型的中断.此时,应该清除
        *IRQ_INPROGRESS,并重新循环,执行中断服务例程,处理其它CPU收到的中断.
        */
        if (likely(!(desc->status & IRQ_PENDING)))
            break;
        desc->status &= ~IRQ_PENDING;
    }
    /*所有中断处理完毕,则清除IRQ_INPROGRESS*/
    desc->status &= ~IRQ_INPROGRESS;  

out:
    /*
     * The ->end() handler has to deal with interrupts which got
     * disabled while the handler was running.
     */
     //结束中断处理.对end_8259A_irq()仅仅是重新激活中断线.
     /*对于多处器,end应答中断(如果ack方法还没有做的话)
    */
    desc->handler->end(irq);
    //最后,释放自旋锁,
    spin_unlock(&desc->lock);  

    return 1;
}</span>
</pre>
<p></p>
<div id="cnblogs_post_body">3.3.4、handle_IRQ_event<span style="color:#008000"><br>
//kernel/irq/handle.c<br>
fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs,<br>
                struct irqaction *action)<br>
{<br>
    int ret, retval = 0, status = 0;<br>
    //开启本地中断,对于单CPU,仅仅是sti指令<br>
    if (!(action->flags & SA_INTERRUPT))<br>
        local_irq_enable();<br>
    //依次调用共享该中断向量的服务例程<br>
    do {<br>
        //调用中断服务例程<br>
        ret = action->handler(irq, action->dev_id, regs);<br>
        if (ret == IRQ_HANDLED)<br>
            status |= action->flags;<br>
        retval |= ret;<br>
        action = action->next;<br>
    } while (action);<br>
<br>
    if (status & SA_SAMPLE_RANDOM)<br>
        add_interrupt_randomness(irq);<br>
    //关本地中断,对于单CPU,为cli指令<br>
    local_irq_disable();<br>
<br>
    return retval;<br>
}<br>
<br>
</span></div>
<span style="color:#008000"><br>
</span>
<p></p>
     

 

时间: 2024-10-07 19:11:39

理解Linux中断 (2)【转】的相关文章

理解Linux中断 (1)【转】

转自:http://blog.csdn.net/tommy_wxie/article/details/7425685 版权声明:本文为博主原创文章,未经博主允许不得转载. 一直认为,理解中断是理解内核的开始.中断已经远远超过仅仅为外围设备服务的范畴,它是现代体系结构的重要组成部分. 1.基本输入输出方式 现代体系结构的基本输入输出方式有三种: (1)程序查询: CPU周期性询问外部设备是否准备就绪.该方式的明显的缺点就是浪费CPU资源,效率低下. 但是,不要轻易的就认为该方式是一种不好的方式(漂

理解Linux中断 (3)【转】

转自:http://blog.csdn.net/tommy_wxie/article/details/7425712 版权声明:本文为博主原创文章,未经博主允许不得转载. 4.下半部 在中断处理过程中,不能睡眠.另外,它运行的时候,会把当前中断线在所有处理器上都屏蔽(在ack中完成屏蔽):更糟糕的情况是,如果一个处理程序是SA_INTERRUPT类型,它执行的时候会禁上所有本地中断(通过cli指令完成),所以,中断处理应该尽可能快的完成.所以Linux把中断处理分为上半部和下半部. 上半部由中断

深入理解Linux内存寻址的分段机制

一.前言 最近在学习Linux内核,读到<深入理解Linux内核>的内存寻址一章.原本以为自己对分段分页机制已经理解了,结果发现其实是一知半解.于是,查找了很多资料,最终理顺了内存寻址的知识.现在把我的理解记录下来,希望对内核学习者有一定帮助,也希望大家指出错误之处. 二.分段到底是怎么回事 相信学过操作系统课程的人都知道分段分页,但是奇怪的是书上基本没提分段分页是怎么产生的,这就导致我们知其然不知其所以然.下面我们先扒一下分段机制产生的历史. 实模式的诞生(16位处理器及寻址) 在8086处

LINUX中断学习笔记【转】

转自:http://blog.chinaunix.net/uid-14825809-id-2381330.html 1.中断的注册与释放: 在 , 实现中断注册接口: int request_irq(unsigned int irq,irqreturn_t (*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *dev_name,void *dev_id); void free_irq(unsigne

linux中断--中断嵌套&amp;amp;中断请求丢失

  关于中断嵌套: 在linux内核里,如果驱动在申请注册中断的时候没有特别的指定,do_irq在做中断响应的时候,是开启中断的,如果在驱动的中断处理函数正在执行的过程中,出现同一设备的中断或者不同设备的中断,这时候新的中断会被立即处理,还是被pending,等当前中断处理完成后,再做处理. 在2.4和2.6内核里,关于这一块是否有什么不同. 一般申请中断的时候都允许开中断,即不使用SA_INTERRUPT标志.如果允许共享则加上 SA_SHIRQ,如果可以为内核熵池提供熵值(譬如你写的驱动是i

linux中断--内核中断编程

Linux中断内核编程 前言 在前面分析了中断的基本原理后,就可以写一个内核中断程序来体验以下,也可以借此程序继续深入来了解内核中断的执行过程 一.内核中断程序: 我们还是来看一看成程序: 在看程序之前,要熟悉如何进行模块编程,和了解module_pararm()的用法.如果不熟悉的话请大家看,module_param()的学习和Linux内核模块编程,在此不作解释. 1.程序interrupt.c [c-sharp] view plaincopy 1 /* 2 *file name :inte

深入理解Linux内存管理机制(一)

深入理解Linux内存管理机制(一)通过本文,您即可以: 1. 存储器硬件结构: 2.分段以及对应的组织方式: 3.分页以及对应的组织方式. 注1:本文以Linux内核2.6.32.59本版为例,其对应的代码可以在http://www.kernel.org/pub/linux/kernel/v2.6/longterm/v2.6.32/linux-2.6.32.59.tar.bz2找到. 注2:本文所有的英文专有名词都是我随便翻译的,请对照英文原文进行理解. 注3:推荐使用Source Insig

深入理解linux互斥锁(mutex)

                                      深入理解linux互斥锁(mutex)     锁机制,可以说是linux整个系统的精髓所在,linux内核都是围绕着同步在运转.在多进程和多线程编程中,锁起着极其重要的作用.我这里说的是互斥锁,其实是泛指linux中所有的锁机制.我在这里不讲如果创建锁,关于锁的创建,网上代码很多,我在这里就不多说了.我要谈一谈一个让所有刚刚接触锁机制的程序员都很困惑的问题:如何使用以及锁机制在程序中是如何运作的.     为什么要使用

深入理解linux内核之(二)进程

                                      深入理解linux内核之(二)进程       程序是静态的,进程是正在执行的程序的一个实例,一个程序可以由多个进程组成.进程是资源分配的实体.在进程被创建出来之后,该子进程几乎和父进程一样.子进程复制了父进程的地址空间,从fork()之后的第一条指令开始执行,和父进程有同样的程序可执行代码(exec调用除外).尽管子进程和父进程具有同样的程序执行代码,但是子进程拥有自己的stack和heap,因此,子进程对数据的修改对