精华内容
下载资源
问答
  • 中断处理过程

    万次阅读 2019-08-13 16:05:45
    概念 中断全过程指的是从中断源发出中断请求开始,CPU响应这个请求,现行程序被中断,转至中断服务程序,直到中断...大体上可以把中断全过程分为5个阶段:中断请求、中断判优、中断响应、中断处理和中断返回。 ...

    中断
    在CPU执行程序的过程中,出现了某种紧急情况或异常的事件时,暂停正在执行的程序,转去处理该事件,并在处理完该事件之后返回断点处(指返回主程序时执行的第一条指令的地址)继续执行刚刚被暂停的程序。
    中断源
    任何引发中断的事件。
    中断分类
    1)硬件中断(即外中断)
    (1)非屏蔽中断(NMI):整个系统只有一个。
    (2)可屏蔽中断(INTR)
    2)软件中断(内中断)
    (1)除法错误中断
    (2)溢出中断
    (3)单步中断
    (4)断点中断
    (5)中断指令INT n中断
    单步中断和断点中断用来调试程序
    中断优先级:8086的中断优先级排序:内中断(除单步中断),NMI,INTR,单步中断。
    中断向量表:是存放中断向量(中断服务程序的入口地址)的一个特定的内存区域。这里有必要注意一下,8086可以处理256种中断。
    中断处理步骤
    (1)中断请求:中断源向CPU发出中断请求
    (2)中断响应
    (3)保护断点和现场:以便在中断服务程序执行后正确的返回主程序。
    (4)中断处理
    (5)中断返回
    8086中断处理步骤
    (1)内部中断以及NMI中断不需要从数据总线上读取中断类型码,而INTR需要,而且该中断类型码由发出中断请求的接口电路提供。
    (2)CPU得到中断类型码后将标志寄存器内容压入栈中(PUSHF),保护中断时标志位的状态。
    (3)令单步标志TF =0(禁止CPU以单步方式执行中断服务程序)。
    (4)令中断允许标志IF=0(即:在响应该中断后不再响应别的中断。由于CPU在中断响应时,自动将IF置为0,如果要允许中断嵌套,必须在后面的中断服务程序中用开中断指令STI重新将IF置为1)
    (6)将当前CS:IP入栈,即保护断点和现场。
    (7)根据中断类型码在中断向量表中找到相应中断服务程序的入口地址,将其装入CS:IP,转向中断服务程序。
    (8)为了保证NMI中弄断有着实质性的优先级,所以在执行中断服务之前需要再次检查是否有NMI中断。
    (9)执行中断服务程序。
    (10)IRET指令实现原CS:IP,标志寄存器的出栈,即恢复断点并返回到主程序。

    展开全文
  • 缺页中断处理过程

    千次阅读 2018-11-15 11:24:54
    缺页中断处理过程

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                     缺页中断就是要访问的页不在主存,需要操作系统将其调入主存后再进行访问。

    当进程执行过程中发生缺页中断时,需要进行页面换入,步骤如下:

    <1> 首先硬件会陷入内核,在堆栈中保存程序计数器。大多数机器将当前指令的各种状态信息保存在CPU中特殊的寄存器中。

    <2>启动一个汇编代码例程保存通用寄存器及其它易失性信息,以免被操作系统破坏。这个例程将操作系统作为一个函数来调用。

    (在页面换入换出的过程中可能会发生上下文换行,导致破坏当前程序计数器及通用寄存器中本进程的信息)

    <3>当操作系统发现是一个页面中断时,查找出来发生页面中断的虚拟页面(进程地址空间中的页面)。这个虚拟页面的信息通常会保存在一个硬件寄存器中,如果没有的话,操作系统必须检索程序计数器,取出这条指令,用软件分析该指令,通过分析找出发生页面中断的虚拟页面。

    <4>检查虚拟地址的有效性及安全保护位。如果发生保护错误,则杀死该进程。

    <5>操作系统查找一个空闲的页框(物理内存中的页面),如果没有空闲页框则需要通过页面置换算法找到一个需要换出的页框。

    <6>如果找的页框中的内容被修改了,则需要将修改的内容保存到磁盘上,此时会引起一个写磁盘调用,发生上下文切换(在等待磁盘写的过程中让其它进程运行)。

    (注:此时需要将页框置为忙状态,以防页框被其它进程抢占掉)

    <7>页框干净后,操作系统根据虚拟地址对应磁盘上的位置,将保持在磁盘上的页面内容复制到“干净”的页框中,此时会引起一个读磁盘调用,发生上下文切换。

    <8>当磁盘中的页面内容全部装入页框后,向操作系统发送一个中断。操作系统更新内存中的页表项,将虚拟页面映射的页框号更新为写入的页框,并将页框标记为正常状态。

    <9>恢复缺页中断发生前的状态,将程序指令器重新指向引起缺页中断的指令。

    <10>调度引起页面中断的进程,操作系统返回汇编代码例程。

    <11>汇编代码例程恢复现场,将之前保存在通用寄存器中的信息恢复。

    其实缺页中断的过程涉及了用户态和内核态之间的切换,虚拟地址和物理之间的转换(这个转换过程需要使用MMU和TLB),同时涉及了内核态到用户态的转换。


               

    给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

    这里写图片描述
    展开全文
  • ARM中断处理过程

    万次阅读 2018-05-08 15:18:57
    一、前言本文主要以ARM体系结构下的中断处理为例,讲述整个中断处理过程中的硬件行为和软件动作。具体整个处理过程分成三个步骤来描述:1、第二章描述了中断处理的准备过程2、第三章描述了当发生中的时候,ARM硬件的...

    一、前言

    本文主要以ARM体系结构下的中断处理为例,讲述整个中断处理过程中的硬件行为和软件动作。具体整个处理过程分成三个步骤来描述:

    1、第二章描述了中断处理的准备过程

    2、第三章描述了当发生中的时候,ARM硬件的行为

    3、第四章描述了ARM的中断进入过程

    4、第五章描述了ARM的中断退出过程

    本文涉及的代码来自3.14内核。另外,本文注意描述ARM指令集的内容,有些source code为了简短一些,删除了THUMB相关的代码,除此之外,有些debug相关的内容也会删除。

     

    二、中断处理的准备过程

    1、中断模式的stack准备

    ARM处理器有多种processor mode,例如user mode(用户空间的AP所处于的模式)、supervisor mode(即SVC mode,大部分的内核态代码都处于这种mode)、IRQ mode(发生中断后,处理器会切入到该mode)等。对于linux kernel,其中断处理处理过程中,ARM 处理器大部分都是处于SVC mode。但是,实际上产生中断的时候,ARM处理器实际上是进入IRQ mode,因此在进入真正的IRQ异常处理之前会有一小段IRQ mode的操作,之后会进入SVC mode进行真正的IRQ异常处理。由于IRQ mode只是一个过度,因此IRQ mode的栈很小,只有12个字节,具体如下:

    struct stack { 
        u32 irq[3]; 
        u32 abt[3]; 
        u32 und[3]; 
    } ____cacheline_aligned;


    static struct stack stacks[NR_CPUS];

    除了irq mode,linux kernel在处理abt mode(当发生data abort exception或者prefetch abort exception的时候进入的模式)和und mode(处理器遇到一个未定义的指令的时候进入的异常模式)的时候也是采用了相同的策略。也就是经过一个简短的abt或者und mode之后,stack切换到svc mode的栈上,这个栈就是发生异常那个时间点current thread的内核栈。anyway,在irq mode和svc mode之间总是需要一个stack保存数据,这就是中断模式的stack,系统初始化的时候,cpu_init函数中会进行中断模式stack的设定:

    void notrace cpu_init(void) 
    {

        unsigned int cpu = smp_processor_id();------获取CPU ID 
        struct stack *stk = &stacks[cpu];---------获取该CPU对于的irq abt和und的stack指针

    ……

    #ifdef CONFIG_THUMB2_KERNEL 
    #define PLC    "r"------Thumb-2下,msr指令不允许使用立即数,只能使用寄存器。 
    #else 
    #define PLC    "I" 
    #endif


        __asm__ ( 
        "msr    cpsr_c, %1\n\t"------让CPU进入IRQ mode 
        "add    r14, %0, %2\n\t"------r14寄存器保存stk->irq 
        "mov    sp, r14\n\t"--------设定IRQ mode的stack为stk->irq 
        "msr    cpsr_c, %3\n\t" 
        "add    r14, %0, %4\n\t" 
        "mov    sp, r14\n\t"--------设定abt mode的stack为stk->abt 
        "msr    cpsr_c, %5\n\t" 
        "add    r14, %0, %6\n\t" 
        "mov    sp, r14\n\t"--------设定und mode的stack为stk->und 
        "msr    cpsr_c, %7"--------回到SVC mode 
            :--------------------上面是code,下面的output部分是空的 
            : "r" (stk),----------------------对应上面代码中的%0 
              PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),------对应上面代码中的%1 
              "I" (offsetof(struct stack, irq[0])),------------对应上面代码中的%2 
              PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),------以此类推,下面不赘述 
              "I" (offsetof(struct stack, abt[0])), 
              PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE), 
              "I" (offsetof(struct stack, und[0])), 
              PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE) 
            : "r14");--------上面是input操作数列表,r14是要clobbered register列表 
    }

    嵌入式汇编的语法格式是:asm(code : output operand list : input operand list : clobber list);大家对着上面的code就可以分开各段内容了。在input operand list中,有两种限制符(constraint),"r"或者"I","I"表示立即数(Immediate operands),"r"表示用通用寄存器传递参数。clobber list中有一个r14,表示在汇编代码中修改了r14的值,这些信息是编译器需要的内容。

    对于SMP,bootstrap CPU会在系统初始化的时候执行cpu_init函数,进行本CPU的irq、abt和und三种模式的内核栈的设定,具体调用序列是:start_kernel--->setup_arch--->setup_processor--->cpu_init。对于系统中其他的CPU,bootstrap CPU会在系统初始化的最后,对每一个online的CPU进行初始化,具体的调用序列是:start_kernel--->rest_init--->kernel_init--->kernel_init_freeable--->kernel_init_freeable--->smp_init--->cpu_up--->_cpu_up--->__cpu_up。__cpu_up函数是和CPU architecture相关的。对于ARM,其调用序列是__cpu_up--->boot_secondary--->smp_ops.smp_boot_secondary(SOC相关代码)--->secondary_startup--->__secondary_switched--->secondary_start_kernel--->cpu_init。

    除了初始化,系统电源管理也需要irq、abt和und stack的设定。如果我们设定的电源管理状态在进入sleep的时候,CPU会丢失irq、abt和und stack point寄存器的值,那么在CPU resume的过程中,要调用cpu_init来重新设定这些值。

    2、SVC模式的stack准备

    我们经常说进程的用户空间和内核空间,对于一个应用程序而言,可以运行在用户空间,也可以通过系统调用进入内核空间。在用户空间,使用的是用户栈,也就是我们软件工程师编写用户空间程序的时候,保存局部变量的stack。陷入内核后,当然不能用用户栈了,这时候就需要使用到内核栈。所谓内核栈其实就是处于SVC mode时候使用的栈。

    在linux最开始启动的时候,系统只有一个进程(更准确的说是kernel thread),就是PID等于0的那个进程,叫做swapper进程(或者叫做idle进程)。该进程的内核栈是静态定义的,如下:

    union thread_union init_thread_union __init_task_data = 
        { INIT_THREAD_INFO(init_task) };

    union thread_union { 
        struct thread_info thread_info; 
        unsigned long stack[THREAD_SIZE/sizeof(long)]; 
    };

    对于ARM平台,THREAD_SIZE是8192个byte,因此占据两个page frame。随着初始化的进行,user space的进程也会创建进程或者线程。Linux kernel在创建进程(包括用户进程和内核线程)的时候都会分配一个(或者两个,和配置相关)page frame,具体代码如下:

    static struct task_struct *dup_task_struct(struct task_struct *orig) 

        ...... 

        ti = alloc_thread_info_node(tsk, node); 
        if (!ti) 
            goto free_tsk; 

        ...... 
    }

    底部是struct thread_info数据结构,顶部(高地址)就是该进程的内核栈。当进程切换的时候,整个硬件和软件的上下文都会进行切换,这里就包括了svc mode的sp寄存器的值被切换到调度算法选定的新的进程的内核栈上来。

     

    3、异常向量表的准备

    对于ARM处理器而言,当发生异常的时候,处理器会暂停当前指令的执行,保存现场,转而去执行对应的异常向量处的指令,当处理完该异常的时候,恢复现场,回到原来的那点去继续执行程序。系统所有的异常向量(共计8个)组成了异常向量表。向量表(vector table)的代码如下:

    .section .vectors, "ax", %progbits 
    __vectors_start: 
        W(b)    vector_rst 
        W(b)    vector_und 
        W(ldr)    pc, __vectors_start + 0x1000 
        W(b)    vector_pabt 
        W(b)    vector_dabt 
        W(b)    vector_addrexcptn 
        W(b)    vector_irq ---------------------------IRQ Vector 
        W(b)    vector_fiq

    对于本文而言,我们重点关注vector_irq这个exception vector。异常向量表可能被安放在两个位置上:

    (1)异常向量表位于0x0的地址。这种设置叫做Normal vectors或者Low vectors。

    (2)异常向量表位于0xffff0000的地址。这种设置叫做high vectors

    具体是low vectors还是high vectors是由ARM的一个叫做的SCTLR寄存器的第13个bit (vector bit)控制的。对于启用MMU的ARM Linux而言,系统使用了high vectors。为什么不用low vector呢?对于linux而言,0~3G的空间是用户空间,如果使用low vector,那么异常向量表在0地址,那么则是用户空间的位置,因此linux选用high vector。当然,使用Low vector也可以,这样Low vector所在的空间则属于kernel space了(也就是说,3G~4G的空间加上Low vector所占的空间属于kernel space),不过这时候要注意一点,因为所有的进程共享kernel space,而用户空间的程序经常会发生空指针访问,这时候,内存保护机制应该可以捕获这种错误(大部分的MMU都可以做到,例如:禁止userspace访问kernel space的地址空间),防止vector table被访问到。对于内核中由于程序错误导致的空指针访问,内存保护机制也需要控制vector table被修改,因此vector table所在的空间被设置成read only的。在使用了MMU之后,具体异常向量表放在那个物理地址已经不重要了,重要的是把它映射到0xffff0000的虚拟地址就OK了,具体代码如下:

    static void __init devicemaps_init(const struct machine_desc *mdesc) 

        …… 
        vectors = early_alloc(PAGE_SIZE * 2); -----分配两个page的物理页帧

        early_trap_init(vectors); -------copy向量表以及相关help function到该区域

        …… 
        map.pfn = __phys_to_pfn(virt_to_phys(vectors)); 
        map.virtual = 0xffff0000; 
        map.length = PAGE_SIZE; 
    #ifdef CONFIG_KUSER_HELPERS 
        map.type = MT_HIGH_VECTORS; 
    #else 
        map.type = MT_LOW_VECTORS; 
    #endif 
        create_mapping(&map); ----------映射0xffff0000的那个page frame

        if (!vectors_high()) {---如果SCTLR.V的值设定为low vectors,那么还要映射0地址开始的memory 
            map.virtual = 0; 
            map.length = PAGE_SIZE * 2; 
            map.type = MT_LOW_VECTORS; 
            create_mapping(&map); 
        }


        map.pfn += 1; 
        map.virtual = 0xffff0000 + PAGE_SIZE; 
        map.length = PAGE_SIZE; 
        map.type = MT_LOW_VECTORS; 
        create_mapping(&map); ----------映射high vecotr开始的第二个page frame

    …… 
    }

    为什么要分配两个page frame呢?这里vectors table和kuser helper函数(内核空间提供的函数,但是用户空间使用)占用了一个page frame,另外异常处理的stub函数占用了另外一个page frame。为什么会有stub函数呢?稍后会讲到。

    在early_trap_init函数中会初始化异常向量表,具体代码如下:

    void __init early_trap_init(void *vectors_base) 

        unsigned long vectors = (unsigned long)vectors_base; 
        extern char __stubs_start[], __stubs_end[]; 
        extern char __vectors_start[], __vectors_end[]; 
        unsigned i;

        vectors_page = vectors_base;

        将整个vector table那个page frame填充成未定义的指令。起始vector table加上kuser helper函数并不能完全的充满这个page,有些缝隙。如果不这么处理,当极端情况下(程序错误或者HW的issue),CPU可能从这些缝隙中取指执行,从而导致不可知的后果。如果将这些缝隙填充未定义指令,那么CPU可以捕获这种异常。 
        for (i = 0; i < PAGE_SIZE / sizeof(u32); i++) 
            ((u32 *)vectors_base)[i] = 0xe7fddef1;

      拷贝vector table,拷贝stub function 
        memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); 
        memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start);

        kuser_init(vectors_base); ----copy kuser helper function

        flush_icache_range(vectors, vectors + PAGE_SIZE * 2); 
        modify_domain(DOMAIN_USER, DOMAIN_CLIENT); 

    }

    一旦涉及代码的拷贝,我们就需要关心其编译连接时地址(link-time address)和运行时地址(run-time address)。在kernel完成链接后,__vectors_start有了其link-time address,如果link-time address和run-time address一致,那么这段代码运行时毫无压力。但是,目前对于vector table而言,其被copy到其他的地址上(对于High vector,这是地址就是0xffff00000),也就是说,link-time address和run-time address不一样了,如果仍然想要这些代码可以正确运行,那么需要这些代码是位置无关的代码。对于vector table而言,必须要位置无关。B这个branch instruction本身就是位置无关的,它可以跳转到一个当前位置的offset。不过并非所有的vector都是使用了branch instruction,对于软中断,其vector地址上指令是“W(ldr)    pc, __vectors_start + 0x1000 ”,这条指令被编译器编译成ldr     pc, [pc, #4080],这种情况下,该指令也是位置无关的,但是有个限制,offset必须在4K的范围内,这也是为何存在stub section的原因了。

    4、中断控制器的初始化

    具体可以参考GIC代码分析

     

    三、ARM HW对中断事件的处理

    当一切准备好之后,一旦打开处理器的全局中断就可以处理来自外设的各种中断事件了。

    当外设(SOC内部或者外部都可以)检测到了中断事件,就会通过interrupt requestion line上的电平或者边沿(上升沿或者下降沿或者both)通知到该外设连接到的那个中断控制器,而中断控制器就会在多个处理器中选择一个,并把该中断通过IRQ(或者FIQ,本文不讨论FIQ的情况)分发给该processor。ARM处理器感知到了中断事件后,会进行下面一系列的动作:

    1、修改CPSR(Current Program Status Register)寄存器中的M[4:0]。M[4:0]表示了ARM处理器当前处于的模式( processor modes)。ARM定义的mode包括:

    处理器模式缩写对应的M[4:0]编码Privilege level
    Userusr10000PL0
    FIQfiq10001PL1
    IRQirq10010PL1
    Supervisorsvc10011PL1
    Monitormon10110PL1
    Abortabt10111PL1
    Hyphyp11010PL2
    Undefinedund11011PL1
    Systemsys11111PL1

    一旦设定了CPSR.M,ARM处理器就会将processor mode切换到IRQ mode。

    2、保存发生中断那一点的CPSR值(step 1之前的状态)和PC值

    ARM处理器支持9种processor mode,每种mode看到的ARM core register(R0~R15,共计16个)都是不同的。每种mode都是从一个包括所有的Banked ARM core register中选取。全部Banked ARM core register包括:

    UsrSystemHypSupervisorabortundefinedMonitorIRQFIQ
    R0_usr        
    R1_usr        
    R2_usr        
    R3_usr        
    R4_usr        
    R5_usr        
    R6_usr        
    R7_usr        
    R8_usr       R8_fiq
    R9_usr       R9_fiq
    R10_usr       R10_fiq
    R11_usr       R11_fiq
    R12_usr       R12_fiq
    SP_usr SP_hypSP_svcSP_abtSP_undSP_monSP_irqSP_fiq
    LR_usr  LR_svcLR_abtLR_undLR_monLR_irqLR_fiq
    PC        
    CPSR        
      SPSR_hypSPSR_svcSPSR_abtSPSR_undSPSR_monSPSR_irqSPSR_fiq
      ELR_hyp      

    在IRQ mode下,CPU看到的R0~R12寄存器、PC以及CPSR是和usr mode(userspace)或者svc mode(kernel space)是一样的。不同的是IRQ mode下,有自己的R13(SP,stack pointer)、R14(LR,link register)和SPSR(Saved Program Status Register)。

    CPSR是共用的,虽然中断可能发生在usr mode(用户空间),也可能是svc mode(内核空间),不过这些信息都是体现在CPSR寄存器中。硬件会将发生中断那一刻的CPSR保存在SPSR寄存器中(由于不同的mode下有不同的SPSR寄存器,因此更准确的说应该是SPSR-irq,也就是IRQ mode中的SPSR寄存器)。

    PC也是共用的,由于后续PC会被修改为irq exception vector,因此有必要保存PC值。当然,与其说保存PC值,不如说是保存返回执行的地址。对于IRQ而言,我们期望返回地址是发生中断那一点执行指令的下一条指令。具体的返回地址保存在lr寄存器中(注意:这个lr寄存器是IRQ mode的lr寄存器,可以表示为lr_irq):

    (1)对于thumb state,lr_irq = PC

    (2)对于ARM state,lr_irq = PC - 4

    为何要减去4?我的理解是这样的(不一定对)。由于ARM采用流水线结构,当CPU正在执行某一条指令的时候,其实取指的动作早就执行了,这时候PC值=正在执行的指令地址 + 8,如下所示:

    ----> 发生中断的指令

                   发生中断的指令+4

    -PC-->发生中断的指令+8

                   发生中断的指令+12

    一旦发生了中断,当前正在执行的指令当然要执行完毕,但是已经完成取指、译码的指令则终止执行。当发生中断的指令执行完毕之后,原来指向(发生中断的指令+8)的PC会继续增加4,因此发生中断后,ARM core的硬件着手处理该中断的时候,硬件现场如下图所示:


    ----> 发生中断的指令

                   发生中断的指令+4 <-------中断返回的指令是这条指令

                  发生中断的指令+8

    -PC-->发生中断的指令+12


    这时候的PC值其实是比发生中断时候的指令超前12。减去4之后,lr_irq中保存了(发生中断的指令+8)的地址。为什么HW不帮忙直接减去8呢?这样,后续软件不就不用再减去4了。这里我们不能孤立的看待问题,实际上ARM的异常处理的硬件逻辑不仅仅处理IRQ的exception,还要处理各种exception,很遗憾,不同的exception期望的返回地址不统一,因此,硬件只是帮忙减去4,剩下的交给软件去调整。

    3、mask IRQ exception。也就是设定CPSR.I = 1

    4、设定PC值为IRQ exception vector。基本上,ARM处理器的硬件就只能帮你帮到这里了,一旦设定PC值,ARM处理器就会跳转到IRQ的exception vector地址了,后续的动作都是软件行为了。

     

    四、如何进入ARM中断处理

    1、IRQ mode中的处理

    IRQ mode的处理都在vector_irq中,vector_stub是一个宏,定义如下:

    .macro    vector_stub, name, mode, correction=0 
        .align    5

    vector_\name: 
        .if \correction 
        sub    lr, lr, #\correction-------------(1) 
        .endif

        @ 
        @ Save r0, lr_ (parent PC) and spsr_ 
        @ (parent CPSR) 
        @ 
        stmia    sp, {r0, lr}        @ save r0, lr--------(2) 
        mrs    lr, spsr 
        str    lr, [sp, #8]        @ save spsr

        @ 
        @ Prepare for SVC32 mode.  IRQs remain disabled. 
        @ 
        mrs    r0, cpsr-----------------------(3) 
        eor    r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE) 
        msr    spsr_cxsf, r0

        @ 
        @ the branch table must immediately follow this code 
        @ 
        and    lr, lr, #0x0f---lr保存了发生IRQ时候的CPSR,通过and操作,可以获取CPSR.M[3:0]的值

                                这时候,如果中断发生在用户空间,lr=0,如果是内核空间,lr=3 
    THUMB( adr    r0, 1f            )----根据当前PC值,获取lable 1的地址 
    THUMB( ldr    lr, [r0, lr, lsl #2]  )-lr根据当前mode,要么是__irq_usr的地址 ,要么是__irq_svc的地址 
        mov    r0, sp------将irq mode的stack point通过r0传递给即将跳转的函数 
    ARM(    ldr    lr, [pc, lr, lsl #2]    )---根据mode,给lr赋值,__irq_usr或者__irq_svc 
        movs    pc, lr            @ branch to handler in SVC mode-----(4) 
    ENDPROC(vector_\name)

        .align    2 
        @ handler addresses follow this label 
    1: 
        .endm

    (1)我们期望在栈上保存发生中断时候的硬件现场(HW context),这里就包括ARM的core register。上一章我们已经了解到,当发生IRQ中断的时候,lr中保存了发生中断的PC+4,如果减去4的话,得到的就是发生中断那一点的PC值。

    (2)当前是IRQ mode,SP_irq在初始化的时候已经设定(12个字节)。在irq mode的stack上,依次保存了发生中断那一点的r0值、PC值以及CPSR值(具体操作是通过spsr进行的,其实硬件已经帮我们保存了CPSR到SPSR中了)。为何要保存r0值?因为随后的代码要使用r0寄存器,因此我们要把r0放到栈上,只有这样才能完完全全恢复硬件现场。

    (3)可怜的IRQ mode稍纵即逝,这段代码就是准备将ARM推送到SVC mode。如何准备?其实就是修改SPSR的值,SPSR不是CPSR,不会引起processor mode的切换(毕竟这一步只是准备而已)。

    (4)很多异常处理的代码返回的时候都是使用了stack相关的操作,这里没有。“movs    pc, lr ”指令除了字面上意思(把lr的值付给pc),还有一个隐含的操作(movs中‘s’的含义):把SPSR copy到CPSR,从而实现了模式的切换。

    2、当发生中断的时候,代码运行在用户空间

    Interrupt dispatcher的代码如下:

    vector_stub    irq, IRQ_MODE, 4 -----减去4,确保返回发生中断之后的那条指令

    .long    __irq_usr            @  0  (USR_26 / USR_32)   <---------------------> base address + 0 
    .long    __irq_invalid            @  1  (FIQ_26 / FIQ_32) 
    .long    __irq_invalid            @  2  (IRQ_26 / IRQ_32) 
    .long    __irq_svc            @  3  (SVC_26 / SVC_32)<---------------------> base address + 12 
    .long    __irq_invalid            @  4 
    .long    __irq_invalid            @  5 
    .long    __irq_invalid            @  6 
    .long    __irq_invalid            @  7 
    .long    __irq_invalid            @  8 
    .long    __irq_invalid            @  9 
    .long    __irq_invalid            @  a 
    .long    __irq_invalid            @  b 
    .long    __irq_invalid            @  c 
    .long    __irq_invalid            @  d 
    .long    __irq_invalid            @  e 
    .long    __irq_invalid            @  f

    这其实就是一个lookup table,根据CPSR.M[3:0]的值进行跳转(参考上一节的代码:and    lr, lr, #0x0f)。因此,该lookup table共设定了16个入口,当然只有两项有效,分别对应user mode和svc mode的跳转地址。其他入口的__irq_invalid也是非常关键的,这保证了在其模式下发生了中断,系统可以捕获到这样的错误,为debug提供有用的信息。

        .align    5 
    __irq_usr: 
        usr_entry---------请参考本章第一节(1)保存用户现场的描述 
        kuser_cmpxchg_check---和本文描述的内容无关,这些不就介绍了 
        irq_handler----------核心处理内容,请参考本章第二节的描述 
        get_thread_info tsk------tsk是r9,指向当前的thread info数据结构 
        mov    why, #0--------why是r8 
        b    ret_to_user_from_irq----中断返回,下一章会详细描述

    why其实就是r8寄存器,用来传递参数的,表示本次放回用户空间相关的系统调用是哪个?中断处理这个场景和系统调用无关,因此设定为0。

    (1)保存发生中断时候的现场。所谓保存现场其实就是把发生中断那一刻的硬件上下文(各个寄存器)保存在了SVC mode的stack上。

        .macro    usr_entry 
        sub    sp, sp, #S_FRAME_SIZE--------------A 
        stmib    sp, {r1 - r12} -------------------B

        ldmia    r0, {r3 - r5}--------------------C 
        add    r0, sp, #S_PC-------------------D 
        mov    r6, #-1----orig_r0的值

        str    r3, [sp] ----保存中断那一刻的r0


        stmia    r0, {r4 - r6}--------------------E 
        stmdb    r0, {sp, lr}^-------------------F 
        .endm

    A:代码执行到这里的时候,ARM处理已经切换到了SVC mode。一旦进入SVC mode,ARM处理器看到的寄存器已经发生变化,这里的sp已经变成了sp_svc了。因此,后续的压栈操作都是压入了发生中断那一刻的进程的(或者内核线程)内核栈(svc mode栈)。具体保存多少个寄存器值?S_FRAME_SIZE已经给出了答案,这个值是18个寄存器。r0~r15再加上CPSR也只有17个而已。先保留这个疑问,我们稍后回答。

    B:压栈首先压入了r1~r12,这里为何不处理r0?因为r0在irq mode切到svc mode的时候被污染了,不过,原始的r0被保存的irq mode的stack上了。r13(sp)和r14(lr)需要保存吗,当然需要,稍后再保存。执行到这里,内核栈的布局如下图所示:

    ir1

    stmib中的ib表示increment before,因此,在压入R1的时候,stack pointer会先增加4,重要是预留r0的位置。stmib    sp, {r1 - r12}指令中的sp没有“!”的修饰符,表示压栈完成后并不会真正更新stack pointer,因此sp保持原来的值。

    C:注意,这里r0指向了irq stack,因此,r3是中断时候的r0值,r4是中断现场的PC值,r5是中断现场的CPSR值。

    D:把r0赋值为S_PC的值。根据struct pt_regs的定义(这个数据结构反应了内核栈上的保存的寄存器的排列信息),从低地址到高地址依次为:

    ARM_r0 
    ARM_r1 
    ARM_r2 
    ARM_r3 
    ARM_r4 
    ARM_r5 
    ARM_r6 
    ARM_r7 
    ARM_r8 
    ARM_r9 
    ARM_r10 
    ARM_fp 
    ARM_ip 
    ARM_sp  
    ARM_lr 
    ARM_pc<---------add    r0, sp, #S_PC指令使得r0指向了这个位置 
    ARM_cpsr 
    ARM_ORIG_r0

    为什么要给r0赋值?因此kernel不想修改sp的值,保持sp指向栈顶。

    E:在内核栈上保存剩余的寄存器的值,根据代码,依次是r0,PC,CPSR和orig r0。执行到这里,内核栈的布局如下图所示:

    ir2

    R0,PC和CPSR来自IRQ mode的stack。实际上这段操作就是从irq stack就中断现场搬移到内核栈上。

    F:内核栈上还有两个寄存器没有保持,分别是发生中断时候sp和lr这两个寄存器。这时候,r0指向了保存PC寄存器那个地址(add    r0, sp, #S_PC),stmdb    r0, {sp, lr}^中的“db”是decrement before,因此,将sp和lr压入stack中的剩余的两个位置。需要注意的是,我们保存的是发生中断那一刻(对于本节,这是当时user mode的sp和lr),指令中的“^”符号表示访问user mode的寄存器。

    (2)核心处理

    irq_handler的处理有两种配置。一种是配置了CONFIG_MULTI_IRQ_HANDLER。这种情况下,linux kernel允许run time设定irq handler。如果我们需要一个linux kernel image支持多个平台,这是就需要配置这个选项。另外一种是传统的linux的做法,irq_handler实际上就是arch_irq_handler_default,具体代码如下:

        .macro    irq_handler 
    #ifdef CONFIG_MULTI_IRQ_HANDLER 
        ldr    r1, =handle_arch_irq 
        mov    r0, sp--------设定传递给machine定义的handle_arch_irq的参数 
        adr    lr, BSYM(9997f)----设定返回地址 
        ldr    pc, [r1] 
    #else 
        arch_irq_handler_default 
    #endif 
    9997: 
        .endm

    对于情况一,machine相关代码需要设定handle_arch_irq函数指针,这里的汇编指令只需要调用这个machine代码提供的irq handler即可(当然,要准备好参数传递和返回地址设定)。

    情况二要稍微复杂一些(而且,看起来kernel中使用的越来越少),代码如下:

        .macro    arch_irq_handler_default 
        get_irqnr_preamble r6, lr 
    1:    get_irqnr_and_base r0, r2, r6, lr 
        movne    r1, sp 
        @ 
        @ asm_do_IRQ 需要两个参数,一个是 irq number(保存在r0) 
        @                                          另一个是 struct pt_regs *(保存在r1中) 
        adrne    lr, BSYM(1b)-------返回地址设定为符号1,也就是说要不断的解析irq状态寄存器

                                           的内容,得到IRQ number,直到所有的irq number处理完毕 
        bne    asm_do_IRQ  
        .endm

    这里的代码已经是和machine相关的代码了,我们这里只是简短描述一下。所谓machine相关也就是说和系统中的中断控制器相关了。get_irqnr_preamble是为中断处理做准备,有些平台根本不需要这个步骤,直接定义为空即可。get_irqnr_and_base 有四个参数,分别是:r0保存了本次解析的irq number,r2是irq状态寄存器的值,r6是irq controller的base address,lr是scratch register。

    对于ARM平台而言,我们推荐使用第一种方法,因为从逻辑上讲,中断处理就是需要根据当前的硬件中断系统的状态,转换成一个IRQ number,然后调用该IRQ number的处理函数即可。通过get_irqnr_and_base这样的宏定义来获取IRQ是旧的ARM SOC系统使用的方法,它是假设SOC上有一个中断控制器,硬件状态和IRQ number之间的关系非常简单。但是实际上,ARM平台上的硬件中断系统已经是越来越复杂了,需要引入interrupt controller级联,irq domain等等概念,因此,使用第一种方法优点更多。

     

    3、当发生中断的时候,代码运行在内核空间

    如果中断发生在内核空间,代码会跳转到__irq_svc处执行:

        .align    5 
    __irq_svc: 
        svc_entry----保存发生中断那一刻的现场保存在内核栈上 
        irq_handler ----具体的中断处理,同user mode的处理。

    #ifdef CONFIG_PREEMPT--------和preempt相关的处理 
        get_thread_info tsk 
        ldr    r8, [tsk, #TI_PREEMPT]        @ get preempt count 
        ldr    r0, [tsk, #TI_FLAGS]        @ get flags 
        teq    r8, #0                @ if preempt count != 0 
        movne    r0, #0                @ force flags to 0 
        tst    r0, #_TIF_NEED_RESCHED 
        blne    svc_preempt 
    #endif

        svc_exit r5, irq = 1            @ return from exception

    一个task的thread info数据结构定义如下(只保留和本场景相关的内容):

    struct thread_info { 
        unsigned long        flags;        /* low level flags */ 
        int            preempt_count;    /* 0 => preemptable, <0 => bug */ 
        …… 
    };

    flag成员用来标记一些low level的flag,而preempt_count用来判断当前是否可以发生抢占,如果preempt_count不等于0(可能是代码调用preempt_disable显式的禁止了抢占,也可能是处于中断上下文等),说明当前不能进行抢占,直接进入恢复现场的工作。如果preempt_count等于0,说明已经具备了抢占的条件,当然具体是否要抢占当前进程还是要看看thread info中的flag成员是否设定了_TIF_NEED_RESCHED这个标记(可能是当前的进程的时间片用完了,也可能是由于中断唤醒了优先级更高的进程)。

    保存现场的代码和user mode下的现场保存是类似的,因此这里不再详细描述,只是在下面的代码中内嵌一些注释。

        .macro    svc_entry, stack_hole=0 
        sub    sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)----sp指向struct pt_regs中r1的位置 
        stmia    sp, {r1 - r12} ------寄存器入栈。

        ldmia    r0, {r3 - r5} 
        add    r7, sp, #S_SP - 4 ------r7指向struct pt_regs中r12的位置 
        mov    r6, #-1 ----------orig r0设为-1 
        add    r2, sp, #(S_FRAME_SIZE + \stack_hole - 4)----r2是发现中断那一刻stack的现场 
        str    r3, [sp, #-4]! ----保存r0,注意有一个!,sp会加上4,这时候sp就指向栈顶的r0位置了

        mov    r3, lr ----保存svc mode的lr到r3 
        stmia    r7, {r2 - r6} ---------压栈,在栈上形成形成struct pt_regs 
        .endm

     

    至此,在内核栈上保存了完整的硬件上下文。实际上不但完整,而且还有些冗余,因为其中有一个orig_r0的成员。所谓original r0就是发生中断那一刻的r0值,按理说,ARM_r0和ARM_ORIG_r0都应该是用户空间的那个r0。 为何要保存两个r0值呢?为何中断将-1保存到了ARM_ORIG_r0位置呢?理解这个问题需要跳脱中断处理这个主题,我们来看ARM的系统调用。对于系统调用,它 和中断处理虽然都是cpu异常处理范畴,但是一个明显的不同是系统调用需要传递参数,返回结果。如果进行这样的参数传递呢?对于ARM,当然是寄存器了, 特别是返回结果,保存在了r0中。对于ARM,r0~r7是各种cpu mode都相同的,用于传递参数还是很方便的。因此,进入系统调用的时候,在内核栈上保存了发生系统调用现场的所有寄存器,一方面保存了hardware context,另外一方面,也就是获取了系统调用的参数。返回的时候,将返回值放到r0就OK了。 
    根据上面的描述,r0有两个作用,传递参数,返回结果。当把系统调用的结果放到r0的时候,通过r0传递的参数值就被覆盖了。本来,这也没有什么,但是有些场合是需要需要这两个值的: 
    1、ptrace (和debugger相关,这里就不再详细描述了)
    2、system call restart (和signal相关,这里就不再详细描述了)
    正因为如此,硬件上下文的寄存器中r0有两份,ARM_r0是传递的参数,并复制一份到ARM_ORIG_r0,当系统调用返回的时候,ARM_r0是系统调用的返回值。 
    OK,我们再回到中断这个主题,其实在中断处理过程中,没有使用ARM_ORIG_r0这个值,但是,为了防止system call restart,可以赋值为非系统调用号的值(例如-1)。


    五、中断退出过程

    无论是在内核态(包括系统调用和中断上下文)还是用户态,发生了中断后都会调用irq_handler进行处理,这里会调用对应的irq number的handler,处理softirq、tasklet、workqueue等(这些内容另开一个文档描述),但无论如何,最终都是要返回发生中断的现场。

    1、中断发生在user mode下的退出过程,代码如下:

    ENTRY(ret_to_user_from_irq) 
        ldr    r1, [tsk, #TI_FLAGS] 
        tst    r1, #_TIF_WORK_MASK---------------A 
        bne    work_pending 
    no_work_pending: 
        asm_trace_hardirqs_on ------和irq flag trace相关,暂且略过

        /* perform architecture specific actions before user return */ 
        arch_ret_to_user r1, lr----有些硬件平台需要在中断返回用户空间做一些特别处理 
        ct_user_enter save = 0 ----和trace context相关,暂且略过

        restore_user_regs fast = 0, offset = 0------------B 
    ENDPROC(ret_to_user_from_irq) 
    ENDPROC(ret_to_user)

    A:thread_info中的flags成员中有一些low level的标识,如果这些标识设定了就需要进行一些特别的处理,这里检测的flag主要包括:

    #define _TIF_WORK_MASK   (_TIF_NEED_RESCHED | _TIF_SIGPENDING | _TIF_NOTIFY_RESUME)

    这三个flag分别表示是否需要调度、是否有信号处理、返回用户空间之前是否需要调用callback函数。只要有一个flag被设定了,程序就进入work_pending这个分支(work_pending函数需要传递三个参数,第三个是参数why是标识哪一个系统调用,当然,我们这里传递的是0)。

    B:从字面的意思也可以看成,这部分的代码就是将进入中断的时候保存的现场(寄存器值)恢复到实际的ARM的各个寄存器中,从而完全返回到了中断发生的那一点。具体的代码如下:

        .macro    restore_user_regs, fast = 0, offset = 0 
        ldr    r1, [sp, #\offset + S_PSR] ----r1保存了pt_regs中的spsr,也就是发生中断时的CPSR 
        ldr    lr, [sp, #\offset + S_PC]!    ----lr保存了PC值,同时sp移动到了pt_regs中PC的位置 
        msr    spsr_cxsf, r1 ---------赋值给spsr,进行返回用户空间的准备 
        clrex                    @ clear the exclusive monitor 

        .if    \fast 
        ldmdb    sp, {r1 - lr}^            @ get calling r1 - lr 
        .else 
        ldmdb    sp, {r0 - lr}^ ------将保存在内核栈上的数据保存到用户态的r0~r14寄存器 
        .endif 
        mov    r0, r0   ---------NOP操作,ARMv5T之前的需要这个操作 
        add    sp, sp, #S_FRAME_SIZE - S_PC----现场已经恢复,移动svc mode的sp到原来的位置 
        movs    pc, lr               --------返回用户空间 
        .endm

    2、中断发生在svc mode下的退出过程。具体代码如下:

        .macro    svc_exit, rpsr, irq = 0 
        .if    \irq != 0 
        @ IRQs already off 
        .else 
        @ IRQs off again before pulling preserved data off the stack 
        disable_irq_notrace 
        .endif 
        msr    spsr_cxsf, \rpsr-------将中断现场的cpsr值保存到spsr中,准备返回中断发生的现场 

        ldmia    sp, {r0 - pc}^ -----这条指令是ldm异常返回指令,这条指令除了字面上的操作,

                                           还包括了将spsr copy到cpsr中。 

        .endm

     

    转发自蜗窝科技http://www.wowotech.net/irq_handler.html

    展开全文
  • 中断及中断处理过程

    千次阅读 2017-12-08 12:08:20
    中断及中断处理过程 1. 中断和异常的概念区别  Intel的官方文档里将中断和异常理解为两种中断当前程序执行的不同机制。这是中断和异常的共同点。不同点在于:  中断(interrupt)是异步的事件,典型的比如...
    (From:http://www.cppblog.com/aaxron/archive/2011/11/16/160280.html)
    中断及中断处理过程
    1. 中断和异常的概念区别
       Intel的官方文档里将中断和异常理解为两种中断当前程序执行的不同机制。这是中断和异常的共同点。不同点在于:
       中断(interrupt)是异步的事件,典型的比如由I/O设备触发;异常(exception)是同步的事件,典型的比如处理器执行某条指令时发现出错了等等。
       中断又可以分为可屏蔽中断和非可屏蔽中断,异常又分为故障、陷阱和异常中止3种,它们的具体区别很多书籍和官方文档都解释的比较清楚这里不再赘述。
    关于它们的区别有两点是需要注意的:
    1)平常所说的屏蔽中断是不包括异常的,即异常不会因为CPU的IF位被清(关中断,指令:cli)而受影响,比如缺页异常,即使关了中断也会触发CPU的处理。
    2)通常说的int 80h这种系统调用使用的中断方式实际上硬件上是理解为异常处理的,因此也不会被屏蔽掉,这也很好理解,int 80h这种中断方式是程序里主动触发的,对于CPU来说属于同步事件,因此也就属于异常的范畴。
    2. 中断(异常)处理过程
       需要明确的一点是CPU对于中断和异常的具体处理机制本质上是完全一致的,即:
    当CPU收到中断或者异常的信号时,它会暂停执行当前的程序或任务,通过一定的机制跳转到负责处理这个信号的相关处理程序中,在完成对这个信号的处理后再跳回到刚才被打断的程序或任务中。这里只描述保护模式下的处理过程,搞清楚了保护模式下的处理过程(更复杂),实模式下的处理机制也就容易理解了。
    具体的处理过程如下:
    0)中断响应的事前准备:
    系统要想能够应对各种不同的中断信号,总的来看就是需要知道每种信号应该由哪个中断服务程序负责以及这些中断服务程序具体是如何工作的。系统只有事前对这两件事都知道得很清楚,才能正确地响应各种中断信号和异常。
    [a]系统将所有的中断信号统一进行了编号(一共256个:0~255),这个号称为中断向量,具体哪个中断向量表示哪种中断有的是规定好的,也有的是在给定范围内自行设定的。  
    中断向量和中断服务程序的对应关系主要是由IDT(中断向量表)负责。操作系统在IDT中设置好各种中断向量对应的中断描述符(一共有三类中断门描述符:任务门、中断门和陷阱门),留待CPU查询使用。而IDT本身的位置是由idtr保存的,当然这个地址也是由OS填充的。
    [b]中断服务程序具体负责处理中断(异常)的代码是由软件,也就是操作系统实现的,这部分代码属于操作系统内核代码。也就是说从CPU检测中断信号到加载中断服务程序以及从中断服务程序中恢复执行被暂停的程序,这个流程基本上是硬件确定下来的,而具体的中断向量和服务程序的对应关系设置和中断服务程序的内容是由操作系统确定的。
    1)CPU检查是否有中断/异常信号
       CPU在执行完当前程序的每一条指令后,都会去确认在执行刚才的指令过程中中断控制器(如:8259A)是否发送中断请求过来,如果有那么CPU就会在相应的时钟脉冲到来时从总线上读取中断请求对应的中断向量[2]。
    对于异常和系统调用那样的软中断,因为中断向量是直接给出的,所以和通过IRQ(中断请求)线发送的硬件中断请求不同,不会再专门去取其对应的中断向量。
    2)根据中断向量到IDT表中取得处理这个向量的中断程序的段选择符
       CPU根据得到的中断向量到IDT表里找到该向量对应的中断描述符,中断描述符里保存着中断服务程序的段选择符。
    3)根据取得的段选择符到GDT中找相应的段描述符
       CPU使用IDT查到的中断服务程序的段选择符从GDT中取得相应的段描述符,段描述符里保存了中断服务程序的段基址和属性信息,此时CPU就得到了中断服务程序的起始地址。
       这里,CPU会根据当前cs寄存器里的CPL和GDT的段描述符的DPL,以确保中断服务程序是高于当前程序的,如果这次中断是编程异常(如:int 80h系统调用),那么还要检查CPL和IDT表中中断描述符的DPL,以保证当前程序有权限使用中断服务程序,这可以避免用户应用程序访问特殊的陷阱门和中断门[3]。
    4)CPU根据特权级的判断设定即将运行的中断服务程序要使用的栈的地址
       CPU会根据CPL和中断服务程序段描述符的DPL信息确认是否发生了特权级的转换,比如当前程序正运行在用户态,而中断程序是运行在内核态的,则意味着发生了特权级的转换,这时CPU会从当前程序的TSS信息(该信息在内存中的首地址存在TR寄存器中)里取得该程序的内核栈地址,即包括ss和esp的值,并立即将系统当前使用的栈切换成新的栈。这个栈就是即将运行的中断服务程序要使用的栈。紧接着就将当前程序使用的ss,esp压到新栈中保存起来。
    6)保护当前程序的现场
       CPU开始利用栈保护被暂停执行的程序的现场:依次压入当前程序使用的eflags,cs,eip,errorCode(如果是有错误码的异常)信息。
    官方文档[1]给出的栈变化的示意图如下:
    7)跳转到中断服务程序的第一条指令开始执行
       CPU利用中断服务程序的段描述符将其第一条指令的地址加载到cs和eip寄存器中,开始执行中断服务程序。这意味着先前的程序被暂停执行,中断服务程序正式开始工作。
    8)中断服务程序处理完毕,恢复执行先前中断的程序
       在每个中断服务程序的最后,必须有中断完成返回先前程序的指令,这就是iret(或iretd)。程序执行这条返回指令时,会从栈里弹出先前保存的被暂停程序的现场信息,即eflags,cs,eip重新开始执行。
    展开全文
  • 计算机组成原理(中断处理过程

    千次阅读 多人点赞 2018-06-30 19:31:42
    中断处理过程 Created with Raphaël 2.1.2关中断保存断点,保存现场判别中断条件,转入中断服务程序开中断执行中断服务程序 关中断恢复现场,恢复断点开中断返回返回断点 关中断 进入不可再次响应中断的状态,...
  •  中断处理过程:一次完整的中断过程由中断请求、中断响应和中断处理三个阶段组成。  中断处理流程:关中断->保存断电保护现场->判断中断源转到相应的中断服务->开中断->执行相应的中断服务程序(ISR)->关中断->...
  • 中断处理过程(含8086)

    千次阅读 2019-07-13 12:46:19
    中断处理过程 中断的相关定义 中断:在CPU执行程序的过程中,出现了某种紧急情况或异常的事件时,暂停正在执行的程序,转去处理该事件,并在处理完该事件之后返回断点处(指返回主程序时执行的第一条指令的地址)...
  • 中断的概念和中断处理过程

    千次阅读 2013-05-30 22:17:06
    中断的概念和中断处理过程 (1)中断: 在运行一个程序的过程中,断续地以“插入”方式执行一些完成特定处理功能的程序段,这种处理方式称为中断。 (2)中断的作用: ◎并行操作 ◎硬件故障报警与处理 ◎...
  • 1. 缺页中断 2. 缺页中断的断点 缺页中断是指令执行过程中产生的中断,而非(一般...4. 缺页中断处理过程 (1) 保留进程上下文 (2)判断内存是否有空闲可用帧?若有,则获取一个帧号No,转(4) 启动I/O过程。若无,继续
  • x86中断处理过程

    千次阅读 2019-06-18 14:10:29
    文章目录了解x86中的中断源中断interrupts异常Exceptions了解CPU与操作系统如何处理中断x86如何通过中断处理来完成系统调用能够对中断向量表(中断描述表,简称IDT)进行初始化 了解x86中的中断源 中断、异常在具体...
  • 8086中断处理过程

    千次阅读 2020-04-09 09:25:53
    当外设向CPU发送可屏蔽中断请求: 1)8259中断控制器通过INTR信号线发送高电屏请求信号 2)CPU在每执行一条指令的最后一个时钟周期时会采样INTR信号线判断有无新的中断请求,如果INTR标志为1(也就是有中断请求)...
  • 分析system_call中断处理过程

    千次阅读 2015-07-19 09:40:52
    分析system_call中断处理过程使用gdb跟踪分析一个系统调用内核函数(以 sys_chmod为例) 启动调试内核 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S gdb 调试 另开 shellgdb (gdb) ...
  • 有另一个优先级更高的中断提出中断请求,这时会暂时终止当前正在执行的级别较低的中断源的服务程序,去处理级别更高的中断源,待处理完毕,再返回到被中断了的中断服务程序继续执行,这个过程就是中断嵌套 ...
  • 完整的中断处理过程分为 1)中断响应的事前准备: 系统要想能够应对各种不同的中断信号,总的来看就是需要知道每种信号应该由哪个中断服务程序负责以及这些中断服务程序具体是如何工作的。系统只有事前对这两件事都...
  • uCOS2中的中断处理过程

    千次阅读 2014-03-28 19:57:48
    一、uCOS2中的中断处理过程。 二、在一指的情况下,要注意的问题。 三、在一指的情况下,代码可能的执行路径。 四、贴一些网上还好的关于ucos中断处理的帖子。   (提醒下,以下细节分析部分,是基于凌阳SPCE...
  • LINUX中断处理过程

    千次阅读 2015-11-15 15:32:40
    一般申请中断的时候都...这是普通的中断请求过程。对于这种一般情况,只要发生中断,就可以抢占内核,即使内核正在执行其他中断函数。这里有两点说明:一是因为linux不支持中断优先级,因此任何中断都可以抢占其他中断
  • linux中断系统那些事之----中断处理过程 以外部中断irq为例来说明,当外部硬件产生中断时,linux的处理过程。首先先说明当外部中断产生时,硬件处理器所做的工作如下: R14_irq = address of next instruction ...
  • 异常和中断处理过程是每个体系结构和OS都要面对的重要问题,本文从硬件角度以及软件角度来分析PowerPc这个过程,文字水平有限,将就着看。 PowerPC定义了十几种异常,其中常见的如 Data TLB miss, Instruction TLB ...
  • ARM Linux外部中断处理过程

    千次阅读 2008-08-04 10:39:00
    ARM Linux外部中断处理过程http://www.linuxforum.net/forum/showflat.php?Cat=&Board=linuxK&Number=652682&page=12&view=collapsed&sb=7&o=all&fpart=1&vc=1作者:muxiaowei 整理:Nathan.Yu 最近在学习arm linux...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 330,056
精华内容 132,022
关键字:

中断处理的过程