精华内容
下载资源
问答
  • 首先说明,答案是否定的,内核中断号可以与硬件中断不一致,但是这是个无聊的问题。。实用价值不大。但是却可以引起对内核软件中断与硬件中断关系的思考。 两者的关系我觉得可以从中断的初始化和分发过程来一...
    首先说明,答案是否定的,内核中断号可以与硬件中断号不一致,但是这是个无聊的问题。。实用价值不大。但是却可以引起对内核软件中断号与硬件中断号关系的思考。
    
    两者的关系我觉得可以从中断的初始化和分发过程来一探究竟。
    这里就从ARM PPC MIPS 3款主流嵌入式处理器架构的内核代码框架中来分析下他们中断的初始化和分发过程。

    一 中断的初始化
    对于中断初始化,在系统启动过程中,这3款处理器架构的内核软件框架中都会有相应的中断初始化函数.

    内核启动函数start_kernel中会调用init_IRQ来进行中断初始化,该函数在不同处理器平台代码有不同实现。实现在arch/xxx/kernel/irq.c中。
    对于arm处理器,init_IRQ调用对应设备描述符machine_desc的init_irq。
    对于ppc处理器,init_IRQ调用对应设备描述符machdep_calls的init_IRQ。
    对于mips处理器,init_IRQ调用arch_init_irq。
    最终调用的中断初始化函数是在板级支持包中实现,因为中断控制器属于处理器核的外设。

    所以可以看出,中断初始化的调用关系:
    通用函数start_kernel -----> 处理器平台级函数init_IRQ -----> 板级中断初始化函数init_irq等


    板级中断初始化函数完成2件事情,其一,中断控制器初始化。其二,中断描述符irq_desc的初始化。
    中断控制器初始化就不细说了。这里我们主要关心软件上中断描述符的处理。

    内核对于中断的管理,最关键的数据结构就是irq_desc,在kernel/irq/irqdesc.c中:

    struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
        [0 ... NR_IRQS-1] = {
            .handle_irq = handle_bad_irq,
            .depth      = 1,
            .lock       = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
        }
    };


    NR_IRQS由不同处理器平台的板级支持包来定义,irq_desc数组成员代表每一个中断号和相应的处理函数。
    所以irq_desc[NR_IRQS]就是硬件中断控制器中断号表在内核的表征。

    有个前提,这里是在不配置CONFIG_SPARSE_IRQ内核选项的情况下,irq_desc[NR_IRQS]用数组形式静态分配中断描述符表,编译时即确定irq_desc数组大小。
    如果配置CONFIG_SPARSE_IRQ则动态分配irq_desc以节省内存。这是另一套机制,这里就不细说了。

    板级中断初始化函数中完成其所使用所有中断号对应的irq_desc的初始化,主要是设置中断的一级处理函数。一般是handle_level_irq。函数实现在kernel/irq/chip.c中。
    handle_level_irq遍历该irq_desc的action链表,依次执行其action->handler。

    各个driver中调用request_irq注册的中断处理函数就是irq_desc各个action的handler成员。这里我们也就明白了内核下共享中断的实现机制了。

    内核中断的初始化就是这样。从这里可以看出,irq_desc[NR_IRQS]内核中断号表与硬件中断号表对应。

    那么问题来了,我不让他们一一对应,硬件中断号35的处理函数,我想放在irq_desc数组的30号可不可以?
    从中断的初始化来看这样的修改是没什么问题,就是数组成员内容换一下。
    但是我们要想到中断是来干什么的,中断的初始化以及注册,都是为了能够正确的响应中断进行处理。
    所以这个问题的关键在于,这样修改,当产生35号中断时,内核能不能正常找到在30号irq-desc上的处理函数呢。
    也就是在中断分发过程中内核如何确定软件中断号,由硬件35号中断找到30号irq_desc。

    这个我们就需要来看下内核的中断分发过程了。


    二 中断的分发
    中断是处理器核异常的一种,所以处理器设计中,外设中断引起处理器异常,处理器跳转到异常向量表的相应异常入口取指执行。
    处理器的异常向量表也是软硬件结合很有意思的东西,有时间专门写一篇来记录,这里不详说了。


    我们主要来看产生中断后,处理器跳转到中断异常入口后的执行流程。


    (1)对于arm处理器,执行流程如下:

    vector_irq ---> irq_handler ---> arch_irq_handler_default ---> asm_do_IRQ ---> handle_IRQ
    在arch_irq_handler_default中调用get_irqnr_and_base获取中断号,传给asm_do_IRQ作为参数。
    get_irqnr_and_base由板级支持包实现。


    (2)对于ppc处理器,执行流程如下:

    do_IRQ ---> ppc_md.get_irq ---> handle_one_irq
    ppc_md.get_irq是由板级支持包中实现的设备描述符的获取中断号函数。


    (3)对于mips处理器,执行流程如下:

    handle_int ---> plat_irq_dispatch ---> do_IRQ
    plat_irq_dispatch由班级支持包中实现。


    上述的流程表示由异常入口函数开始,到调用handle_level_irq结束。
    对于3款处理器平台,内核在中断分发上没有像中断初始化那样由通用函数到处理器平台函数最后到板级支持函数,而是每种处理器平台都不一样。上述函数的实现都在arch/xxx/kernel下,具体实现可以参考代码。

    根据上面的分析可以看出,ARM MIPS PPC在中断分发中中断号的获取都是留给板级支持包来实现的。板级支持包中会读取中断控制器中相关寄存器来获取当前产生的中断情况,进而返回中断号。


    所以结合中断初始化部分提出的问题,不管哪款处理器平台,如果我们想将35号中断的中断处理函数在注册时放在30号irq_desc中(方法是request_irq时中断号写30)。

    那么在中断分发时,获取中断号函数中我们也需要进行修改,查询到中断控制器寄存器状态是产生35号中断,我们返回的中断号应该是30号!

    但是,这样做并没有实际的应用意义,因为在实际开发中还是要尽量保证内核下irq_desc数组与硬件中断号表一一对应,这样驱动开发者在操作中断时就不需要关心内核中断号和硬件中断号的关系,而是直接使用硬件中断号来注册就可以了。

    如果内核中断号和硬件中断号不一一对应,驱动开发者在编写驱动时还需要查找硬件中断号和内核中断号的映射表,增大了开发难度。

    无论如何,借这个无聊的问题,还是搞清了内核中断的初始化和分发过程,也是很值得的


    但求好事,莫问前程!

    展开全文
  • 内核自动探测中断号

    千次阅读 2015-12-03 22:09:11
    我们来看short_kernelprobe函数如何实现由内核自动探测中断号的:[cpp] view plaincopy466void short_kernelprobe(void) 467{ 468 int count = 0; 469 do { 470 unsigned long mask; 471 472 mask...
    我们来看short_kernelprobe函数如何实现由内核自动探测中断号的:
    
     
    1. 466void short_kernelprobe(void)  
    2. 467{  
    3. 468    int count = 0;  
    4. 469    do {  
    5. 470        unsigned long mask;  
    6. 471  
    7. 472        mask = probe_irq_on();  
    8. 473        outb_p(0x10,short_base+2); /* enable reporting */  
    9. 474        outb_p(0x00,short_base);   /* clear the bit */  
    10. 475        outb_p(0xFF,short_base);   /* set the bit: interrupt! */  
    11. 476        outb_p(0x00,short_base+2); /* disable reporting */  
    12. 477        udelay(5);  /* give it some time */  
    13. 478        short_irq = probe_irq_off(mask);  
    14. 479  
    15. 480        if (short_irq == 0) { /* none of them? */  
    16. 481            printk(KERN_INFO "short: no irq reported by probe\n");  
    17. 482            short_irq = -1;  
    18. 483        }  
    19. 484        /* 
    20. 485         * if more than one line has been activated, the result is 
    21. 486         * negative. We should service the interrupt (no need for lpt port) 
    22. 487         * and loop over again. Loop at most five times, then give up 
    23. 488         */  
    24. 489    } while (short_irq < 0 && count++ < 5);  
    25. 490    if (short_irq < 0)  
    26. 491        printk("short: probe failed %i times, giving up\n", count);  
    27. 492}  
    Linux内核提供了探测可用中断号的接口,但这种接口只能在非共享中断模式下使用。内核提供的接口由两个函数组成:
    unsigned long probe_irq_on(void);
    这个函数返回一个未分配中断的位掩码,驱动程序必须保存返回的位掩码,并将它传递给probe_irq_off函数。
    调用probe_irq_on函数之后,驱动程序要安排设备产生至少一次中断。
    
    
    int probe_irq_off(unsigned long);
    在请求设备产生中断之后,驱动程序要调用这个函数,并将前面probe_irq_on返回的位掩码作为参数传递给它。probe_irq_off返回probe_irq_on之后发生的中断编号。如果没有中断发生,就返回0。如果产生了多次中断,出现了二义性,就返回负数。
    使用内核提供的接口探测中断号时,需要注意在调用probe_irq_on之后启用设备中断,在调用probe_irq_off之前禁用中断。另外,在probe_irq_off之后,需要处理设备上待处理的中断。
    
    
    472行,调用probe_irq_on函数。
    473行,将2号端口的第4位(0x10)设置为1,启用中断。
    474行,将0号端口清0。
    475行,将0号端口置1,触发中断。
    476行,将2号端口的第4位(0x10)设置为0,禁用中断。
    477行,延时一会,以保证中断的传递时间。
    478行,调用probe_irq_off函数,并把472行probe_irq_on函数返回的位掩码传递给它。
    480行,probe_irq_off函数返回0,说明没有中断发生。
    489行,probe_irq_off函数返回负值,说明发生了不止一个中断,需要重新探测,这里限定最多探测5次。
    展开全文
  • 如何在内核中断上下文中分配内存

    千次阅读 2013-05-14 10:59:22
    判断一个内存分配函数能否用在中断上下文中,主要看这个内存函数最终调用的伙伴系统算法的函数接口page_alloc(gfp_mask,order)的gfp_mask的内容,用在中断上下文中一般是GFP_ATOMIC,否则通常GFP_KERNEL作为内存...

    判断一个内存分配函数能否用在中断上下文中,主要看这个内存函数最终调用的伙伴系统算法的函数接口page_alloc(gfp_mask,order)的gfp_mask的内容,用在中断上下文中一般是GFP_ATOMIC,否则通常GFP_KERNEL作为内存分配标志。
    get_free_pages()也是根据传递的参数gfp_mask,决定这个分配函数是否能睡眠,所以传递的gfp_mask参数决定了是否能用在中断上下文中。

    内核中函数分配根据分类方式的不同:
    1 根据分配时是否睡眠分为两类:
    使用GFP_ATOMIC标志分配内存,告诉分配函数不能睡眠,一般仅仅在中断上下文,或者持有锁的代码中使用。
    使用GFP_KERNEL标志分配内存,在分配过程中,可能会阻塞,一般用在进程上下文。
    对于常规内存的分配,是用get_free_pages(),是用这个标志作为分配的参数时,可以使用在中断上下文,或者持有锁的代码段中。
    对于高端物理内存使用alloc_pages()(用在中断上下文中应该传递GFP_ATOMIC标志)配合持久映射函数kmap()或者临时映射函数kmap_atomic(),其中kmap()用在进程上下文中,而kmap_atomic()用在中断上下文中。

    2 根据分配的物理内存的位置可以分为高端物理内存(high zone)和非高端物理内存的分配(dma zone or normal zone)。都有可以睡眠的分配方式和不可睡眠的内存分配方式
    上面两种非配方式是交叉,睡眠(或者不可睡眠)的内存分配方式都可以在高端和低端分配

    GFP_KERNEL标志的作用:
    不管是连续的还是非连续的,可睡眠的还是不可睡眠的分配方式,最终都要使用伙伴系统算法的接口函数,核心就是alloc_pages(),前面提到的GFP_KERNEL以及其他的内存标志最终都会传递给这个函数。所以看一个内存分配函数是否可以用在中断上下文中,只要看这个函数最终使用的伙伴系统的接口函数的flag参数。
    看vmalloc()代码就知道,这个函数分配物理内存分配函数page_alloc()传递的参数是GFP_KERNEL|GFP_HIGHMEM,所以自然不能用在中断上下文中。


    展开全文
  • linux中断--内核中断编程

    千次阅读 2014-04-14 19:24:44
    在前面分析了中断的基本原理后,就可以写一个内核中断程序来体验以下,也可以借此程序继续深入来了解内核中断的执行过程 一.内核中断程序: 我们还是来看一看成程序: 在看程序之前,要熟悉如何进行模块编程,和了解...

    Linux中断内核编程

    前言

    在前面分析了中断的基本原理后,就可以写一个内核中断程序来体验以下,也可以借此程序继续深入来了解内核中断的执行过程

    一.内核中断程序

    我们还是来看一看成程序:

    在看程序之前,要熟悉如何进行模块编程,和了解module_pararm()的用法。如果不熟悉的话请大家看,module_param()的学习和Linux内核模块编程,在此不作解释。

    1.程序interrupt.c

    [c-sharp] view plain copy
    1. /* 
    2.  2 *file name :interrupt.c 
    3.  3 *atuthor   : john  
    4.  4 */  
    5.  5 #include<linux/init.h>  
    6.  6 #include<linux/module.h>  
    7.  7 #include<linux/kernel.h>  
    8.  8 #include<linux/interrupt.h>  
    9.  9   
    10. 10 MODULE_LICENSE("GPL");  
    11. 11 static int irq;  
    12. 12 char *interface;  
    13. 13 static irqreturn_t myirq_handler(int irq,void *dev);  
    14. 14   
    15. 15 static int __init myirq_init(void)  
    16. 16 {  
    17. 17         printk("the module is working!/n");  
    18. 18         printk("the irq is ready for working!/n");  
    19. 19         if(request_irq(irq,myirq_handler,IRQF_SHARED,interface,&irq)){  
    20. 20         printk(KERN_ERR "%s interrrupt can't register %d IRQ /n",interface,irq);  
    21. 21         return -EIO;  
    22. 22         }  
    23. 23         printk("%s request %d IRQ/n",interface,irq);  
    24. 24         return 0;  
    25. 25 }  
    26. 26 static irqreturn_t myirq_handler(int irq,void *dev)  
    27. 27 {  
    28. 28         printk("%d IRQ is working/n",irq);  
    29. 29         return IRQ_NONE;  
    30. 30 }  
    31. 31 static void  __exit myirq_exit(void)  
    32. 32 {  
    33. 33         printk("the module is leaving!/n");  
    34. 34         printk("the irq is bye bye!/n");  
    35. 35         free_irq(irq,&irq);  
    36. 36         printk("%s interrupt free %d IRQ/n",interface,irq);  
    37. 37   
    38. 38 }  
    39. 39 module_init(myirq_init);  
    40. 0 module_exit(myirq_exit);  
    41. 41 module_param(interface,charp,0644);  
    42. 42 module_param(irq,int,0644);  
    43. 43   
     

    2.Makefile的编写

    [c-sharp] view plain copy
    1.  1 obj-m:=tiger.o  
    2.  2   
    3.  3 CURRENT_PATH:=$(shell pwd)  
    4.  4 VERSION_NUM:=$(shell uname -r)  
    5.  5 LINUX_PATH:=/usr/src/linux-headers-$(VERSION_NUM)  
    6.  6   
    7.  7   
    8.  8 all :  
    9.  9         make -C $(LINUX_PATH) M=$(CURRENT_PATH) modules  
    10. 10 clean:  
    11. 11         make -C $(LINUX_PATH) M=$(CURRENT_PATH) clean  
     

    (程序的调试,加载和运行,在此不进行说明)

    3.首先我们来分析下内核加载模块

    在内核加载模块中最重要的的action就是注册中断处理程序。很明显,这一动作是通过request_irq()函数来完成的。

    int request_irq(unsigned int irq,  irq_handler_t handler,unsigned long flags, const char *devname, void *dev_id)

    A.先来分析形参:

    第一个参数irq: 表示要分配的中断号。对于一些设备(系统时钟或键盘)它的值是预先固定的,而对于大多数设备来说,这个值是动态确定的。

    第二个参数handler:表示要挂入到中断请求对列中的中断服务例程,这个中断服务函数的原型是static irqreturn_t handler(int , void *);

    中断处理程序的前缀为static,因为它从来不会被别的文件中的代码直接调用。

    第三个参数flags:为标志位。可以取IRQF_DISABLED、IRQF_SHARED和IRQF_SAMPLE_RANDOM之一。在本实例程序中取 IRQF_SHARED,该标志表示多个中断处理程序共享irq中断线。一般某个中断线上的中断服务程序在执行时会屏蔽请求该线的其他中断,如果取 IRQF_DISABLED标志,则在执行该中断服务程序时会屏蔽所有其他的中断。取IRQF_SAMPLE_RANDOM则表示设备可以被看做是事件随见的发生源。

    以下是官方解释:

    [c-sharp] view plain copy
    1. /*  
    2. * These flags used only by the kernel as part of the  
    3. * irq handling routines.  
    4.  
    5. * IRQF_DISABLED - keep irqs disabled when calling the action handler  
    6. * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator  
    7. * IRQF_SHARED - allow sharing the irq among several devices  
    8. * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur  
    9. * IRQF_TIMER - Flag to mark this interrupt as timer interrupt  
    10. * IRQF_PERCPU - Interrupt is per cpu  
    11. * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing  
    12. * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is  
    13. *                registered first in an shared interrupt is considered for  
    14. *                performance reasons)  
    15. */   
    16. #define IRQF_DISABLED           0x00000020   
    17. #define IRQF_SAMPLE_RANDOM       0x00000040   
    18. #define IRQF_SHARED             0x00000080   
    19. #define IRQF_PROBE_SHARED       0x00000100   
    20. #define IRQF_TIMER               0x00000200   
    21. #define IRQF_PERCPU             0x00000400   
    22. #define IRQF_NOBALANCING         0x00000800   
    23. #define IRQF_IRQPOLL             0x00001000    

    第四个参数devname:是请求中断的设备的名称。当你加载模块成功后可以在/proc/interrupts中查看到具体设备的名称,与此同时也可以看到这个设备对应的中断号以及请求次数。

    第五个参数dev_id:为一个指针型变量。注意该参数为void型,也就是说通过强制转换可以转换为任意类型。dev_id主要用于共享中断线,对每个注册的中断处理程序来说,( Dev_id must be globally unique. Normally the address of the  device data structure is used as the cookie.)dev_id参数必须唯一(指向任一设备结构的指针就可以满足此要求,选择设备结构因为它是唯一的,而且中断处理程序可能会用到它)如果无需共享中断线,则将该参数赋值为NULL。

    B:函数返回值

    requset_irq()函数成功执行后返回0。如果返回非0值,就表示错误发生。此时,指定的中断处理程序不会被注册。

    这里面有几个疑问:

    为什么要注册中断函数

    共享中断线的概念,参数dev_id的作用是什么

    看一个图进行说明:


    1>由图可知:有16个中断线。要使用中断线,就要进行中断线的申请 ,也常把申请一条中断线称为申请一个中断号,这就 与request_irq()函数中的第一个形参irq有关系

    2>Linux有256个中断向量,而外部中中断向量只有16个(32~47)。由于硬件上的限制,很多外部设备不得不共享中断线。

    (例如:一些PC机所用的网卡和图形卡可以把它们分配到一条中断线上)

    让每个中断源独自占用一条中断线是不实现的。

    3>共享中断线的话虽然解决了中断资源的问题,但是,此时引出了另一个问题(任何事物都有其两面性),此时仅仅用中断描述符并不能提供中断产生的所有信息。为了解决这个问题,内核必须对中断线给出近一步的描述,所以在Linux设计中,为每个中断请求IRQ设置了一个专用队列(中断请求队列)
    4>中断服例程序和中断处理程序的区别:
    a.中断服务例程(interrupt service routine):

    Linux中,15条中断线对应15个中断处理程序,依次命名是IRQ0x00_interrupt(),IRQ0x01_interrupt().....IRQ0X1f_interrupt().

    中断处理程序相当于某个中断向量的总处理程序。
    eg:IRQ0X05_interupt()是5号中断(向量为37)的总处理程序。

    b.中断服务例程是针对一个具体设备的中断。
    5>.注册中断服务例程:
    在IDT表完成初始化时,每个中断服务队列还为空。此时即使打开中断且某个外设的中断真的发生了,也得不到实际的服务。因为CPU虽然通过中断门进入了某个中断向量的总处理程序。但是,具体的中断服务例程还没有挂入中断请求队列。所以,在设备驱动程序的初始化阶段,必须通过request_irq()函数将响应的中断服务例程挂入中断请求队列,也就是进行注册。

    6>分析一下中断服务程序,即request_irq()函数中第二个参数所对应的函数

    static irqreturn_t myirq_handler(int irq,void *dev_id)
    {
     
             printk("ISR is Working/n");
             return IRQ_HANDLED;

    }

    中断服务例程的形参:

    a.int irq :中断号。
    b.void *dev_id :与request_irq()的参数dev_id一致,可以根据这个设备id号得到相应设备的数据结构,进而得到相应设备的信息和相关数据。
    c.返回值:中断程序的返回值是一个特殊类型 rqreturn_t。但是中断程序的返回值却只有两个值IRQ_NONE和IRQ_HANDLED。
    IRQ_NONE:中断程序接收到中断信号后发现这并不是注册时指定的中断原发出的中断信号。
    IRQ_HANDLED:接收到了准确的中断信号,并且作了相应正确的处理。

    PS:当一个中断到达,通过IRQ进入确定中断向量,由中断向量找到中断描述表(也就是各种门描述符的信息,这里使用中断门),由中断门描述符中的信息就可以知道中断处理程序的入口地址,进入中断处理程序后就需要执行中断服务例程,由于一个中断处理程序对应多个中断服务例程,就可以通过IRQ_NONE/IRQ_HANDLED来判断是否执行这个中断服务例程

    一般中断处理程序要做什么service,主要取决于产生的设备和该设备为什么要发送中断。

    John哥说明:

    1.当一个给定的中断处理程序正在执行时,这条中断线上的其它中断都会被屏蔽。but,所有其他中断线上的中断都是打开的。因此这些不同中断线上的其他中断都能被处理。

    PS:这个问题也就引发了中断嵌套和中断请求丢失的问题,在后续的文章中会有介绍

    2.request_irq()函数可能会睡眠,所以,不能在中断上下文或其它不允许阻塞的代码中调用该函数。

    4.在深入分析request_irq()函数之前,先来看几个重要的数据结构。

    A.irqaction的数据结构(用irqaction结构体来描述一个具体的中断服务例程)

    [c-sharp] view plain copy
    1. 113struct irqaction {  
    2. 114        irq_handler_t handler;  
    3. 115        unsigned long flags;  
    4. 116        const char *name;  
    5. 117        void *dev_id;  
    6. 118        struct irqaction *next;  
    7. 119        int irq;  
    8. 120        struct proc_dir_entry *dir;  
    9. 121        irq_handler_t thread_fn;  
    10. 122        struct task_struct *thread;  
    11. 123        unsigned long thread_flags;  
    12. 124};  
    13. 125  

    1>handler:指向具体的一个中断服务例程。

    2>flags:表示中断标志位,对应于request_irq()函数中所传递的第三个参数,可取IRQF_DISABLED、IRQF_SAMPLE_RANDOM和IRQF_SHARED其中之一。

    3>name:请求中断的设备名称,对应request_irq()函数中所传递的第四个参数

    4>dev_id:共享中断时有用。对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。

    5>strct irqaction *next:指向irqaction描述符的下一个元素。用一条链表将共享同一条中断线上的中断服务例程链接起来。

    6>irq:所申请的中断号

    7>dir:指向proc/irq/NN/name entry

    8>thread_fn:指向具体的一个线程化的中断。

    9>thread:指向线程中断的指针。

    10>thread_flags:线程中断的标志。

    PS:平常在有的解释中,很随意的说注册中断处理程序,其实我觉得,对于用户来说,特别是I/O中断,注册都是中断服务例程,也就是上面的这个结构体)

    B.irq_desc的数据结构体

    每个中断向量都有它自己的irq_desc 描述符。即用irq_desc来描述中断向量。所有的这些中断描述符组织在一起就形成了irq_desc irq_desc[NR_IRQS]数组

    [c-sharp] view plain copy
    1. 175struct irq_desc {  
    2. 176        unsigned int            irq;  
    3. 177        struct timer_rand_state *timer_rand_state;  
    4. 178        unsigned int            *kstat_irqs;  
    5. 179#ifdef CONFIG_INTR_REMAP  
    6. 180        struct irq_2_iommu      *irq_2_iommu;  
    7. 181#endif  
    8. 182        irq_flow_handler_t      handle_irq;  
    9. 183        struct irq_chip         *chip;  
    10. 184        struct msi_desc         *msi_desc;  
    11. 185        void                    *handler_data;  
    12. 186        void                    *chip_data;  
    13. 187        struct irqaction        *action;        /* IRQ action list */  
    14. 188        unsigned int            status;         /* IRQ status */  
    15. 189  
    16. 190        unsigned int            depth;          /* nested irq disables */  
    17. 191        unsigned int            wake_depth;     /* nested wake enables */  
    18. 192        unsigned int            irq_count;      /* For detecting broken IRQs */  
    19. 193        unsigned long           last_unhandled; /* Aging timer for unhandled count */  
    20. 194        unsigned int            irqs_unhandled;  
    21. 195        raw_spinlock_t          lock;  
    22. 196#ifdef CONFIG_SMP  
    23. 197        cpumask_var_t           affinity;  
    24. 198        const struct cpumask    *affinity_hint;  
    25. 199        unsigned int            node;  
    26. 200#ifdef CONFIG_GENERIC_PENDING_IRQ  
    27. 201        cpumask_var_t           pending_mask;  
    28. 202#endif  
    29. 203#endif  
    30. 204        atomic_t                threads_active;  
    31. 205        wait_queue_head_t       wait_for_threads;  
    32. 206#ifdef CONFIG_PROC_FS  
    33. 207        struct proc_dir_entry   *dir;  
    34. 208#endif  
    35. 209        const char              *name;  
    36. 210} ____cacheline_internodealigned_in_smp;  
    37. 211  
    38. 212extern void arch_init_copy_chip_data(struct irq_desc *old_desc,  
    39. 213                                        struct irq_desc *desc, int node);  
    40. 214extern void arch_free_chip_data(struct irq_desc *old_desc, struct irq_desc *desc);  
    41. 215  
    42. 216#ifndef CONFIG_SPARSE_IRQ  
    43. 217extern struct irq_desc irq_desc[NR_IRQS];  
     

    1>irq:表示这个描述符所对应的中断号。

    2>handle_irq:指向该IRQ线的公共服务程序(即该IRQ所对应的中断处理程序。

    3>chip:它是一个struct irq_chip类型的指针,是中断控制器的描述符 。在2.6以前的版本中它是hw_irq_controller。
    4>handler_data:是handler_irq的参数。
    5>chip_data:是指向irq_chip的指针。
    6>atcion:一个struct irqaction类型的指针,它指向一个单链表。该链表是由该中断线上所有中断服务例程链接起来的。
    7>status:表示中断线当前的状态。
    8>depth:中断线被激活时,值为0;当值为正数时,表示被禁止的次数。
    9>irq_count:表示该中断线上发生中断的次数
    10>irqs_unhandled:该IRQ线上未处理中断发生的次数
    11>name:申请中断设备的名字。

    C.struct irq_chip结构体:

    struct irq_chip是一个中断控制器的描述符。Linux支持N种可编程中断控制器PIC(中断控制器),通常不同的体系结构就有一套自己的中断处理方式。内核为了统一的处理中断,提供了底层的中断处理抽象接口,对于每个平台都需要实现底层的接口函数。这样对于上层的中断通用处理程序就无需任何改动。

    struct irq_chip的具体代码如下:

    [c-sharp] view plain copy
    1. 111struct irq_chip {  
    2. 112        const char      *name;  
    3. 113        unsigned int    (*startup)(unsigned int irq);  
    4. 114        void            (*shutdown)(unsigned int irq);  
    5. 115        void            (*enable)(unsigned int irq);  
    6. 116        void            (*disable)(unsigned int irq);  
    7. 117  
    8. 118        void            (*ack)(unsigned int irq);  
    9. 119        void            (*mask)(unsigned int irq);  
    10. 120        void            (*mask_ack)(unsigned int irq);  
    11. 121        void            (*unmask)(unsigned int irq);  
    12. 122        void            (*eoi)(unsigned int irq);  
    13. 123  
    14. 124        void            (*end)(unsigned int irq);  
    15. 125        int             (*set_affinity)(unsigned int irq,  
    16. 126                                        const struct cpumask *dest);  
    17. 127        int             (*retrigger)(unsigned int irq);  
    18. 128        int             (*set_type)(unsigned int irq, unsigned int flow_type);  
    19. 129        int             (*set_wake)(unsigned int irq, unsigned int on);  
    20. 130  
    21. 131        void            (*bus_lock)(unsigned int irq);  
    22. 132        void            (*bus_sync_unlock)(unsigned int irq);  
    23. 133  
    24. 134        /* Currently used only by UML, might disappear one day.*/  
    25. 135#ifdef CONFIG_IRQ_RELEASE_METHOD  
    26. 136        void            (*release)(unsigned int irq, void *dev_id);  
    27. 137#endif  
    28. 138        /* 
    29. 139         * For compatibility, ->typename is copied into ->name. 
    30. 140         * Will disappear. 
    31. 141         */  
    32. 142        const char      *typename;  
    33. 143};  
    34. 144  

    name:中断控制器的名字;
    Startup:启动中断线;
    Shutdown:关闭中断线;
    Enable:允许中断;
    Disable:禁止中断;

    分析了struct irq_desc,struct irq_chip和irqaction的数据结构之后我们来看看他们之间的关系

    现在深入分析request_irq()内部是如何实现的。

    [c-sharp] view plain copy
    1. 135request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,  
    2. 136            const char *name, void *dev)  
    3. 137{  
    4. 138        return request_threaded_irq(irq, handler, NULL, flags, name, dev);  
    5. 139}  
    6. 140  

    可以看到request_irq()函数里面有封装了request_threaded_irq(irq, handler, NULL, flags, name, dev)函数。

    先看一下官方的解释

    [c-sharp] view plain copy
    1. 1006/** 
    2. 1007 *      request_threaded_irq - allocate an interrupt line 
    3. 1008 *      @irq: Interrupt line to allocate 
    4. 1009 *      @handler: Function to be called when the IRQ occurs. 
    5. 1010 *                Primary handler for threaded interrupts 
    6. 1011 *                If NULL and thread_fn != NULL the default 
    7. 1012 *                primary handler is installed 
    8. 1013 *      @thread_fn: Function called from the irq handler thread 
    9. 1014 *                  If NULL, no irq thread is created 
    10. 1015 *      @irqflags: Interrupt type flags 
    11. 1016 *      @devname: An ascii name for the claiming device 
    12. 1017 *      @dev_id: A cookie passed back to the handler function 
    13. 1018 * 
    14. 1019 *      This call allocates interrupt resources and enables the 
    15. 1020 *      interrupt line and IRQ handling. From the point this 
    16. 1021 *      call is made your handler function may be invoked. Since 
    17. 1022 *      your handler function must clear any interrupt the board 
    18. 1023 *      raises, you must take care both to initialise your hardware 
    19. 1024 *      and to set up the interrupt handler in the right order. 
    20. 1025 * 
    21. 1026 *      If you want to set up a threaded irq handler for your device 
    22. 1027 *      then you need to supply @handler and @thread_fn. @handler ist 
    23. 1028 *      still called in hard interrupt context and has to check 
    24. 1029 *      whether the interrupt originates from the device. If yes it 
    25. 1030 *      needs to disable the interrupt on the device and return 
    26. 1031 *      IRQ_WAKE_THREAD which will wake up the handler thread and run 
    27. 1032 *      @thread_fn. This split handler design is necessary to support 
    28. 1033 *      shared interrupts. 
    29. 1034 * 
    30. 1035 *      Dev_id must be globally unique. Normally the address of the 
    31. 1036 *      device data structure is used as the cookie. Since the handler 
    32. 1037 *      receives this value it makes sense to use it. 
    33. 1038 * 
    34. 1039 *      If your interrupt is shared you must pass a non NULL dev_id 
    35. 1040 *      as this is required when freeing the interrupt. 
    36. 1041 * 
    37. 1042 *      Flags: 
    38. 1043 * 
    39. 1044 *      IRQF_SHARED             Interrupt is shared 
    40. 1045 *      IRQF_SAMPLE_RANDOM      The interrupt can be used for entropy 
    41. 1046 *      IRQF_TRIGGER_*          Specify active edge(s) or level 
    42. 1047 * 
    43. 1048 */  

    5.首先分析request_threaded_irq()函数中的各个形参
    1>:irq:表示申请的中断号。
    2>:handler:表示中断服务例程
    3.> thread_fn:中断线程化,此处传递的是NULL。NULL表示没有中断线程化。
    此参数是最新版本中才出现的。为什么要提出中断线程化?
    在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断
    处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不
    到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以
    有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍
    有实时性保证。but,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器
    等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当
    被线程化。

    4>.irqflags:表示中断标志位。
    5>.devname:表示请求中断的设备的名称。

    6>.dev_id:对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。共享中断时所用。

    现在继续迭代深入request_threaded_irq()内部是如何实现的。

    [c-sharp] view plain copy
    1. 1049int request_threaded_irq(unsigned int irq, irq_handler_t handler,  
    2. 1050                         irq_handler_t thread_fn, unsigned long irqflags,  
    3. 1051                         const char *devname, void *dev_id)  
    4. 1052{  
    5. 1053        struct irqaction *action;  
    6. 1054        struct irq_desc *desc;  
    7. 1055        int retval;  
    8. 1056  
    9. 1057        /* 
    10. 1058         * Sanity-check: shared interrupts must pass in a real dev-ID, 
    11. 1059         * otherwise we'll have trouble later trying to figure out 
    12. 1060         * which interrupt is which (messes up the interrupt freeing 
    13. 1061         * logic etc). 
    14. 1062         */  
    15. 1063        if ((irqflags & IRQF_SHARED) && !dev_id)  
    16. 1064                return -EINVAL;  
    17. 1065  
    18. 1066        desc = irq_to_desc(irq);  
    19. 1067        if (!desc)  
    20. 1068                return -EINVAL;  
    21. 1069  
    22. 1070        if (desc->status & IRQ_NOREQUEST)  
    23. 1071                return -EINVAL;  
    24. 1072  
    25. 1073        if (!handler) {  
    26. 1074                if (!thread_fn)  
    27. 1075                        return -EINVAL;  
    28. 1076                handler = irq_default_primary_handler;  
    29. 1077        }  
    30. 1078  
    31. 1079        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);  
    32. 1080        if (!action)  
    33. 1081                return -ENOMEM;  
    34. 1082  
    35. 1083        action->handler = handler;  
    36. 1084        action->thread_fn = thread_fn;  
    37. 1085        action->flags = irqflags;  
    38. 1086        action->name = devname;  
    39. 1087        action->dev_id = dev_id;  
    40. 1088  
    41. 1089        chip_bus_lock(irq, desc);  
    42. 1090        retval = __setup_irq(irq, desc, action);  
    43. 1091        chip_bus_sync_unlock(irq, desc);  
    44. 1092  
    45. 1093        if (retval)  
    46. 1094                kfree(action);  
    47. 1095  
    48. 1096#ifdef CONFIG_DEBUG_SHIRQ  
    49. 1097        if (!retval && (irqflags & IRQF_SHARED)) {  
    50. 1098                /* 
    51. 1099                 * It's a shared IRQ -- the driver ought to be prepared for it 
    52. 1100                 * to happen immediately, so let's make sure.... 
    53. 1101                 * We disable the irq to make sure that a 'real' IRQ doesn't 
    54. 1102                 * run in parallel with our fake. 
    55. 1103                 */  
    56. 1104                unsigned long flags;  
    57. 1105  
    58. 1106                disable_irq(irq);  
    59. 1107                local_irq_save(flags);  
    60. 1108  
    61. 1109                handler(irq, dev_id);  
    62. 1110  
    63. 1111                local_irq_restore(flags);  
    64. 1112                enable_irq(irq);  
    65. 1113        }  
    66. 1114#endif  
    67. 1115        return retval;  
    68. 1116}  

    程序的第一行和第二行分别定义了:

    (1) struct irqaction *action;

    (2)2struct irq_desc *desc;

    两个指针action和desc,它们分别指向了结构体irqaction和 irq_desc。

    (3)    if ((irqflags & IRQF_SHARED) && !dev_id)
                  return -EINVAL;

    作用是:判断中断标志位,如果是共享中断的话就必须要有一个唯一的dev_id,否则返回一个错误。

    (4)      desc = irq_to_desc(irq);

    irq_to_desc(irq):根据中断号irq在 irq_desc[NR_IRQS]数组中返回一个具体的irq_desc。即根据irq找到它的中断处理程序。

    (5)    if (!desc)

            return -EINVAL;

    当返回一个空值时返回一个错误。说明申请中断号失败。

    (6)if (desc->status & IRQ_NOREQUEST)
                   return -EINVAL;

    判断中断线的状态,若为IRQ_NOREQUEST时(IRQ_NOREQUEST表示 IRQ 不能被申请)

    (7)        if (!handler) {
                            if (!thread_fn)
                            return -EINVAL;
                   handler = irq_default_primary_handler;
                  }

    判断中断服务例程是否为空,如果handler为空,则判断线程中断服务例程,若线程中断服务例程也为空,则返回一个错误值。否则中断服务例程指向:rq_default_primary_handler。

    (8)

    1079        action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
    1080        if (!action)
    1081                return -ENOMEM;
    1082
    1083        action->handler = handler;
    1084        action->thread_fn = thread_fn;
    1085        action->flags = irqflags;
    1086        action->name = devname;
    1087        action->dev_id = dev_id;

    从1079~1087:根据requst_irq()函数中传递的参数生成一个irqaction.

    1097        if (!retval && (irqflags & IRQF_SHARED)) {
    1098                /*
    1099                 * It's a shared IRQ -- the driver ought to be prepared for it
    1100                 * to happen immediately, so let's make sure....
    1101                 * We disable the irq to make sure that a 'real' IRQ doesn't
    1102                 * run in parallel with our fake.
    1103                 */
    1104                unsigned long flags;
    1105
    1106                disable_irq(irq);
    1107                local_irq_save(flags);
    1108
    1109                handler(irq, dev_id);
    1110
    1111                local_irq_restore(flags);
    1112                enable_irq(irq);
    1113        }

    1097~1113:如果为共享中断的话,在执行中断服务例程之前,要先把这条中断线上的中断屏蔽,让后在执行,执行完之后打开中断。

    6.有注册中断服务函数,那必然有相应的释放中断函数。

    可以调用void free_irq(unsigned int irq, void *dev_id)来释放我们申请的中断线。

    函数形参:

    1>unsigned int riq:表示申请的中断号与request_irq()函数中的第一个形参对应。

    2>void *dev_id:与request_irq()函数中的最后一个形参含义和用法相同,在此不再说明。

    函数功能:

    如果指定的中断线不是共享的,那么,该函数删除处理程序的同时将禁用这条中断线。如果中断线是共享的,则仅删除dev_id所对应的处理程序,而这条中断线本省只有在删除了最后一个处理程序时才会被禁止。

    切记:This function must not be called from interrupt context

    freee_irq()函数不能在中断上下文中被调用。

    3>深入分析下free_irq()函数内部是如何实现的

    [c-sharp] view plain copy
    1.  993void free_irq(unsigned int irq, void *dev_id)  
    2.  994{  
    3.  995        struct irq_desc *desc = irq_to_desc(irq);  
    4.  996  
    5.  997        if (!desc)  
    6.  998                return;  
    7.  999  
    8. 1000        chip_bus_lock(irq, desc);  
    9. 1001        kfree(__free_irq(irq, dev_id));  
    10. 1002        chip_bus_sync_unlock(irq, desc);  
    11. 1003}  

    可以看到free_irq()函数了封装了_free_irq(irq,dev_id)函数。

    free_irq()调用_free_irq()把每一个具体的中断服务例程()释放。

    转载:http://blog.csdn.net/tigerjibo/article/details/6069516

    添加了自己的理解认识

    展开全文
  • Linux内核中断系统处理机制-详细分析

    万次阅读 多人点赞 2018-08-23 23:09:24
    Linux内核中断 一、中断概述 中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再...
  • Linux内核中断系统

    千次阅读 2020-07-20 13:18:15
    点击上方蓝色字关注我们!前言 中断在驱动中是非常常用的,无论是外部的GPIO中断,还是SPI,I2C等发送或接收中断,都是必不可少的。所以今天来看看Linux中的中断处理。中断分类...
  • Linux 内核中断内幕

    千次阅读 2016-03-16 18:00:50
    本文对中断系统进行了全面的分析与探讨,主要包括中断控制器、中断分类、中断亲和力、中断线程化与 SMP 中的中断迁徙等。首先对中断工作原理进行了简要分析,接着详细探讨了中断亲和力的实现原理,最后对中断线程化...
  • 内核中断机制

    千次阅读 2011-09-17 10:18:43
    中断是Linux内核驱动程序中非常重要的地方,但实际上,中断处理程序也没有什么与众不同的地方,它们也就是普通的C程序。  唯一独特的地方就是处理程序是在中断时间内运行的,因此它的行为会受到一些限制。这些限制...
  • cortex-m3内核中断

    2020-03-07 12:32:33
    m3内核中有一个R/W“中断寄存器阵列”,该阵列记录了外部从0到239个中断的名字PRI_0-239,中断寄存器地址,中断的优先级(8位,stm32用了其中的高4位,复位之后是0)。如下: 另外还有一个应用中断控制与复位寄存器R...
  • 内核中断处理

    千次阅读 2012-04-02 19:49:35
    中断服务程序(中断处理函数)是一种处理中断响应的函数,它是一种遵循特定原型声明的C函数,它运行在中断上下文中,也称为原子上下文,代码运行在此上下文中是不能被阻塞的。中断服务程序必须运行非常快,
  • 细说内核中断机制

    千次阅读 2018-05-01 20:25:58
     中断通常分为同步中断和异步中断:2 同步中断是当指令执行时由CPU控制单元产生的,之所以称为同步,是因为只有在一条指令终止执行后CPU才会发出中断。 ◎ 异步中断是由其他硬件设备依照CPU时钟信号随机产生的。...
  • 细说Linux内核中断架构

    千次阅读 2012-06-14 17:59:28
    中断和异常 一、什么是中断?  中断通常被定义为一个事件,该事件改变处理器执行的指令顺序。这样的事件与CPU芯片内外部硬件电路产生的电信号相对应。  中断通常分为同步中断和异步中断: 2 同步中断是当指令...
  • linux内核中断、异常

    千次阅读 2011-12-23 10:23:01
    中断: 可屏蔽中断:所有有I/O设备请求的中断都是,被屏蔽的中断会一直被CPU 忽略,直到屏蔽位被重置。 不可屏蔽中断:非常危险的事件引起(如硬件失败)。 异常: 处理器产生的(Fault,Trap,Abort)异常 ...
  • linux内核中断实现机制

    千次阅读 2013-01-13 22:37:53
    一、什么是中断 中断分两种: 1)中断,又叫外部中断或异步中断,它的产生是由于外设向处理器发出中断请求。其中外部中断也有两种,这是由配置寄存器设定的:普通中断请求(IRQ)和快速中断请求(FIQ)...
  • Linux内核中的中断

    2021-01-20 14:51:16
    中断处理程序是被内核调用来响应中断的,它运行在中断上下文,中断处理程序是上半部,当接收到一个中断,它立即开始执行,但只做有严格时限的工作,例如对接收的中断进行应答或复位硬件,这些工作都是在所有中断被...
  • 第五课. 内核中断系统中的设备树

    千次阅读 2018-11-22 14:30:57
    中断体系在4.x内核中变化很大,中断体系又跟pinctrl系统密切相关,pinctrl中又涉及GPIO子系统,这样讲下去的话,设备树课程就变成驱动专题了,所以我打算只讲中断体系统,对于pinctrl、gpio等系统留待以后在驱动课程...
  • Linux内核中断以及中断处理程序

    千次阅读 2017-04-16 12:13:23
    人生若只如初见,何事秋风悲画扇。  --------纳兰容若 《木兰花令·拟...一、什么是中断:   linux管理所有的硬件设备,要做的第一件事先是通信。然而CPU的处理速度要远快于外围设备,总不能让CPU一直在等待外围设备
  • linux内核-中断处理程序

    千次阅读 2014-07-03 11:47:20
    linux内核--中断处理程序 时间2013-09-30 07:31:11 CSDN博客 相似文章 (1) 原文 http://blog.csdn.net/yusiguyuan/article/details/12183641 一个设备的中断处理程序是它设备驱动程序的一部分--设备...
  • linux内核中断

    千次阅读 2013-02-25 20:00:41
    Linux内核学习从零单排之三:中断 1.不同的设备对应的中断不同,每个中断都有一个唯一的数字标识,这些中断值通常称为中断请求线(IRQ)。 2.在响应一个中断的时候,内核会执行一个函数,就叫做中断处理程序。中断...
  • linux内核-中断和异常

    千次阅读 2013-09-30 10:02:30
    中断通常定义为一个事件,该事件改变处理器执行的指令顺序。这个事件与cpu芯片内外部...同步中断和异步中断通常成为异常和中断,异常是有程序的错误产生的,或者是又内核必须处理的异常条件产生的。第一种情况下,内
  • Linux内核——中断机制

    千次阅读 2014-08-06 13:57:23
    中断机制 为什么需要中断? 如果让内核定期对设备进行轮询,以便处理设备,那会做很多无用功,因为外设的处理速度...中断处理程序与其他内核函数的区别在于,中断处理程序是被内核调用来响应中断的,而它们运行于我们称
  • 同时,Linux内核也在不断发展,它在中断上的实现也越来越复杂,在这里我来讨论介绍一下Linux x86 架构下的中断初始化过程。   在start_kernel()之前的中断门初始化就不多啰嗦了,在随便的内核教科书里都...
  • 报告内容 中断是由间隔定时器和和I/O设备产生的。 异常则是由程序的错误产生,或者由内核必须处理的异常条件产生。第一种情况下,内核通过发送一个信号来处理异常;第二种情况下,内核执行恢复异常...
  • x86体系中断概念在《深入理解Linux内核》第四章中提到,Intel文档提出了两个 概念。 第一个概念是把同步和异步中断分别称为异常(exception)和中断(interrupt); 第二个概念是进一步的,对于中断和异常,Intel...
  • platform: imx6q os: Android Lollipop 5.1 kernel branch: 3.0.35 初始化: ... early_irq_init irqdesc.c //没有定义CONFIG_SPARSE_IRQ,使用的是静态分配irq_desc,并且初始化。  init_IRQ
  • 中断有两种,一种是由CPU外部产生的,对于执行中的软件来说,这种中断的发生完全是“异步”的,根本无法预料此类中断会在什么时候发生,一般由其他硬件设备产生(例如键盘中断);另一种是由CPU本身在执行程序的过程...
  • Linux内核中断子系统

    千次阅读 2013-04-12 14:58:13
    Linux内核的中断子系统 ...第二部分是内核的中断系统框架层,这部分为内核中断处理提供了一个统一的框架,对于靠上层的驱动程序,它提供中断程序注册的接口。对于体系结构相关的底层,它提供一

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 104,644
精华内容 41,857
关键字:

内核中断号分配