精华内容
下载资源
问答
  • 内核中断处理内核中断处理内核中断处理 内核中断处理
  • linux中断--内核中断编程.docx
  • 任务切换就是靠的内核中断。 我是看这的时候知道有内核中断的 搜了下 STM32目前支持的中断共为84个(16个内核+68个外部),16级可编程中断优先级的设置(仅使用中断优先级设置8bit中的高4位)和16个抢占...

    任务切换就是靠的内核中断,

     

    中断优先级序号越大,优先级越低。

     

    内核中断好像直接操作寄存器就可以了。

    我是看这的时候知道有内核中断的

     

    感觉正点原子的讲得好些,先跟你讲cortex-M的中断再给你讲freertos的中断。

    使用freertos时最关心的是14 和15 这两个异常也就是中断。

     

    怪不得原子的STM32书上说外部中断

     

     

    搜了下

    STM32 目前支持的中断共为 84 个(16 个内核+68 个外部), 16 级可编程中断优先级的设置(仅使用中断优先级设置 8bit 中的高 4 位)和16个抢占优先级(因为抢占优先级最多可以有四位数)。

    http://www.openedv.com/posts/list/43099.htm

    展开全文
  • Linux内核中断分析.pdf

    2021-09-06 23:35:54
    Linux内核中断分析.pdf
  • Linux内核中断系统处理机制-详细分析

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

    X86

    BystanderLinux内核中断
    • 一、中断概述

    中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。

    1.1中断类型

    同步中断由CPU本身产生,又称为内部中断。这里同步是指中断请求信号与代码指令之间的同步执行,在一条指令执行完毕后,CPU才能进行中断,不能在执行期间。所以也称为异常(exception)。

    异步中断是由外部硬件设备产生,又称为外部中断,与同步中断相反,异步中断可在任何时间产生,包括指令执行期间,所以也被称为中断(interrupt)。

    异常又可分为可屏蔽中断(Maskable interrupt)和非屏蔽中断(Nomaskable interrupt)。而中断可分为故障(fault)、陷阱(trap)、终止(abort)三类。

    从广义上讲,中断又可分为四类:中断故障陷阱终止。这些类别之间的异同点请参考 表 1。

    表 1:中断类别及其行为

    类别

    原因

    异步/同步

    返回行为

    中断

    来自I/O设备的信号

    异步

    总是返回到下一条指令

    陷阱

    有意的异常

    同步

    总是返回到下一条指令

    故障

    潜在可恢复的错误

    同步

    返回到当前指令

    终止

    不可恢复的错误

    同步

    不会返回

     

    有些参考资料中按照中断来源进行分类,如下图所示(个人建议不采用这种方式):

                                                                         图1-1

    1.2区分中断号与中断向量

    I/O设备把中断信号发送给中断控制器(8259A)时与之相关联的是一个中断号,当中断控制器把中断信号发送给CPU时与之关联的是一个中断向量。换个角度分析就是中断号是从中断控制器层面划分,中断向量是从CPU层面划分,所以中断号与中断向量之间存在一对一映射关系。在Intel X86中最大支持256种中断,从0到255开始编号,这个8位的编号就是中断向量。其中将0到31保留用于异常处理和不可屏蔽中断。

    • 二、中断数据处理结构

    Linux内核中处理中断主要有三个数据结构,irq_desc,irq_chip和irqaction。

    在\include\linux\ irq.h中定义了

    1)irq_desc用于描述IRQ线的属性与状态,被称为中断描述符。

    /**
    
     * struct irq_desc - interrupt descriptor
    
     * @irq:           interrupt number for this descriptor
    
     * @timer_rand_state:    pointer to timer rand state struct
    
     * @kstat_irqs:        irq stats per cpu
    
     * @irq_2_iommu:  iommu with this irq
    
     * @handle_irq:             highlevel irq-events handler [if NULL, __do_IRQ()]
    
     * @chip:         low level interrupt hardware access
    
     * @msi_desc:        MSI descriptor
    
     * @handler_data:  per-IRQ data for the irq_chip methods
    
     * @chip_data:        platform-specific per-chip private data for the chip
    
     *                    methods, to allow shared chip implementations
    
     * @action:             the irq action chain
    
     * @status:             status information
    
     * @depth:             disable-depth, for nested irq_disable() calls
    
     * @wake_depth:           enable depth, for multiple set_irq_wake() callers
    
     * @irq_count:        stats field to detect stalled irqs
    
     * @last_unhandled:      aging timer for unhandled count
    
     * @irqs_unhandled:      stats field for spurious unhandled interrupts
    
     * @lock:         locking for SMP
    
     * @affinity:            IRQ affinity on SMP
    
     * @node:       node index useful for balancing
    
     * @pending_mask: pending rebalanced interrupts
    
     * @threads_active: number of irqaction threads currently running
    
     * @wait_for_threads:    wait queue for sync_irq to wait for threaded handlers
    
     * @dir:           /proc/irq/ procfs entry
    
     * @name:             flow handler name for /proc/interrupts output
    
     */
    
    struct irq_desc{
    
      unsigned int          irq;
    
      struct timer_rand_state *timer_rand_state;
    
      unsigned int            *kstat_irqs;
    
    #ifdef CONFIG_INTR_REMAP
    
      struct irq_2_iommu      *irq_2_iommu;
    
    #endif
    
      irq_flow_handler_t handle_irq;
    
      struct irq_chip              *chip;
    
      struct msi_desc            *msi_desc;
    
      void               *handler_data;
    
      void               *chip_data;
    
      struct irqaction      *action;   /* IRQ action list */
    
      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;
    
    #ifdef CONFIG_SMP
    
      cpumask_var_t             affinity;
    
      unsigned int          node;
    
    #ifdef CONFIG_GENERIC_PENDING_IRQ
    
      cpumask_var_t             pending_mask;
    
    #endif
    
    #endif
    
      atomic_t         threads_active;
    
      wait_queue_head_t       wait_for_threads;
    
    #ifdef CONFIG_PROC_FS
    
      struct proc_dir_entry    *dir;
    
    #endif
    
      const char             *name;
    
    }

    2)irq_chip用于描述不同类型的中断控制器。

    /**
    
     * struct irq_chip - hardware interrupt chip descriptor
    
     *
    
     * @name:             name for /proc/interrupts
    
     * @startup:           start up the interrupt (defaults to ->enable if NULL)
    
     * @shutdown:              shut down the interrupt (defaults to ->disable if NULL)
    
     * @enable:            enable the interrupt (defaults to chip->unmask if NULL)
    
     * @disable:           disable the interrupt (defaults to chip->mask if NULL)
    
     * @ack:          start of a new interrupt
    
     * @mask:              mask an interrupt source
    
     * @mask_ack:       ack and mask an interrupt source
    
     * @unmask:          unmask an interrupt source
    
     * @eoi:          end of interrupt - chip level
    
     * @end:         end of interrupt - flow level
    
     * @set_affinity:      set the CPU affinity on SMP machines
    
     * @retrigger:         resend an IRQ to the CPU
    
     * @set_type:          set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ
    
     * @set_wake:        enable/disable power-management wake-on of an IRQ
    
     *
    
     * @bus_lock:         function to lock access to slow bus (i2c) chips
    
     * @bus_sync_unlock:    function to sync and unlock slow bus (i2c) chips
    
     *
    
     * @release:           release function solely used by UML
    
     * @typename:              obsoleted by name, kept as migration helper
    
     */
    
    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);
    
      int          (*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);
    
    
      void        (*bus_lock)(unsigned int irq);
    
      void        (*bus_sync_unlock)(unsigned int irq);
    
    
      /* Currently used only by UML, might disappear one day.*/
    
    #ifdef CONFIG_IRQ_RELEASE_METHOD
    
      void        (*release)(unsigned int irq, void *dev_id);
    
    #endif
    
      /*
    
       * For compatibility, ->typename is copied into ->name.
    
       * Will disappear.
    
       */
    
      const char      *typename;
    
    }

    在\include\linux\ interrupt.h中定义了 irqaction用来描述特定设备所产生的中断描述符。

    /**
    
     * struct irqaction - per interrupt action descriptor
    
     * @handler:   interrupt handler function
    
     * @flags: flags (see IRQF_* above)
    
     * @name:      name of the device
    
     * @dev_id:     cookie to identify the device
    
     * @next:  pointer to the next irqaction for shared interrupts
    
     * @irq:    interrupt number
    
     * @dir:    pointer to the proc/irq/NN/name entry
    
     * @thread_fn: interupt handler function for threaded interrupts
    
     * @thread:     thread pointer for threaded interrupts
    
     * @thread_flags:    flags related to @thread
    
     */
    
    struct irqaction {
    
      irq_handler_t handler;
    
      unsigned long flags;
    
      const char *name;
    
      void *dev_id;
    
      struct irqaction *next;
    
      int irq;
    
      struct proc_dir_entry *dir;
    
      irq_handler_t thread_fn;
    
      struct task_struct *thread;
    
      unsigned long thread_flags;
    
    };
    • 三、Linux中断机制

    Linux中断机制由三部分组成:

    1. 中断子系统初始化:内核自身初始化过程中对中断处理机制初始化,例如中断的数据结构以及中断请求等。
    2. 中断或异常处理:中断整体处理过程。
    3. 中断API:为设备驱动提供API,例如注册,释放和激活等。

    3.1中断子系统初始化

    3.1.1中断描述符表(IDT)初始化

    中断描述符表初始化需要经过两个过程:

    1. 第一个过程在内核引导过程。由两个步骤组成,首先给分配IDT分配2KB空间(256中断向量,每个向量由8bit组成)并初始化;然后把IDT起始地址存储到IDTR寄存器中。
    2. 第二个过程内核在初始化自身的start_kernal函数中使用trap_init初始化系统保留中断向量,使用init_IRQ完成其余中断向量初始化。

    3.1.2中断请求队列初始化

    init_IRQ调用pre_intr_init_hook,进而最终调用init_ISA_irqs初始化中断控制器以及每个IRQ线的中断请求队列。

    3.2中断或异常处理

    中断处理过程:设备产生中断,并通过中断线将中断信号送往中断控制器,如果中断没有被屏蔽则会到达CPU的INTR引脚,CPU立即停止当前工作,根据获得中断向量号从IDT中找出门描述符,并执行相关中断程序。

    异常处理过程:异常是由CPU内部发生所以不会通过中断控制器,CPU直接根据中断向量号从IDT中找出门描述符,并执行相关中断程序。

                                                                                          图3-1

    中断控制器处理主要有5个步骤:1.中断请求 2.中断响应 3.优先级比较 4.提交中断向量 5.中断结束。这里不再赘述5个步骤的具体流程。

    CPU处理流程主要有6个步骤:1.确定中断或异常的中断向量 2.通过IDTR寄存器找到IDT 3.特权检查 4.特权级发生变化,进行堆栈切换 5.如果是异常将异常代码压入堆栈,如果是中断则关闭可屏蔽中断 6.进入中断或异常服务程序执行。这里不再赘述6个步骤的具体流程。

    3.3中断API

    内核提供的API主要用于驱动的开发。

    注册IRQ:

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);

    释放IRQ:

    void free_irq(unsigned int, void *);

    注:IRQ线资源非常宝贵,我们在使用时必须先注册,不使用时必须释放IRQ资源。

    激活当前CPU中断:

    local_irq_enable();

    禁止当前CPU中断:

    local_irq_disable();

    激活指定中断线:

    void enable_irq(unsigned int irq);

    禁止指定中断线:

    void disable_irq(unsigned int irq);

    禁止指定中断线:

    void disable_irq_nosync(unsigned int irq);

    注:此函数调用irq_chip中disable禁止指定中断线,所以不会保证中断线上执行的中断服务程序已经退出。

    3.4中断机制划分

     由于中断会打断内核中进程的正常调度运行,所以要求中断服务程序尽可能的短小精悍;但是在实际系统中,当中断到来时,要完成工作往往进行大量的耗时处理。因此期望让中断处理程序运行得快,并想让它完成的工作量多,这两个目标相互制约,诞生——顶/底半部机制。

      中断处理程序是顶半部——接受中断,它就立即开始执行,但只有做严格时限的工作。能够被允许稍后完成的工作会推迟到底半部去,此后,在合适的时机,底半部会被开终端执行。顶半部简单快速,执行时禁止一些或者全部中断。

     底半部稍后执行,而且执行期间可以响应所有的中断。这种设计可以使系统处于中断屏蔽状态的时间尽可能的短,以此来提高系统的响应能力。顶半部只有中断处理程序机制,而底半部的实现有软中断,tasklet和工作队列实现。

                                                 图3-2 注:登记中断,将底半部处理程序挂到该设备的低半部执行队列中。

    3.4.1顶/底半部划分原则:

     1) 如果一个任务对时间非常敏感,将其放在顶半部中执行;

     2) 如果一个任务和硬件有关,将其放在顶半部中执行;

     3) 如果一个任务要保证不被其他中断打断,将其放在顶半部中执行;

     4) 其他所有任务,考虑放置在底半部执行。

     

    3.4.2底半部实现机制

                                                                                                图3-3

    软中断:

    软中断作为下半部机制的代表,是随着SMP(share memory processor)的出现应运而生的,它也是tasklet实现的基础(tasklet实际上只是在软中断的基础上添加了一定的机制)。软中断一般是“可延迟函数”的总称,有时候也包括了tasklet(请读者在遇到的时候根据上下文推断是否包含tasklet)。它的出现就是因为要满足上面所提出的上半部和下半部的区别,使得对时间不敏感的任务延后执行,软中断执行中断处理程序留给它去完成的剩余任务,而且可以在多个CPU上并行执行,使得总的系统效率可以更高。它的特性包括:

    a)产生后并不是马上可以执行,必须要等待内核的调度才能执行。软中断不能被自己打断,只能被硬件中断打断(上半部)。

    b)可以并发运行在多个CPU上(即使同一类型的也可以)。所以软中断必须设计为可重入的函数(允许多个CPU同时操作),因此也需要使用自旋锁来保护其数据结构。

    内核中定义了几种软中断的用途:

    enum
    
    {
       HI_SOFTIRQ=0,
    
       TIMER_SOFTIRQ,
    
       NET_TX_SOFTIRQ,
    
       NET_RX_SOFTIRQ,
    
       BLOCK_SOFTIRQ,
    
       BLOCK_IOPOLL_SOFTIRQ,
    
       TASKLET_SOFTIRQ,
    
       SCHED_SOFTIRQ,
    
       HRTIMER_SOFTIRQ,
    
       RCU_SOFTIRQ,  /* Preferable RCU should always be the last softirq */
    
       NR_SOFTIRQS
    
    };

     

    Tasklet

      tasklet是通过软中断实现的,所以它本身也是软中断。

      软中断用轮询的方式处理。假如正好是最后一种中断,则必须循环完所有的中断类型,才能最终执行对应的处理函数。显然当年开发人员为了保证轮询的效率,于是限制中断个数为32个。

      为了提高中断处理数量,顺道改进处理效率,于是产生了tasklet机制。

      Tasklet采用无差别的队列机制,有中断时才执行,免去了循环查表之苦。Tasklet作为一种新机制,显然可以承担更多的优点。正好这时候SMP越来越火了,因此又在tasklet中加入了SMP机制,保证同种中断只能在一个cpu上执行。在软中断时代,显然没有这种考虑。因此同一种软中断可以在两个cpu上同时执行,很可能造成冲突。

      总结下tasklet的优点:

      (1)无类型数量限制;

      (2)效率高,无需循环查表;

      (3)支持SMP机制;

      它的特性如下:

      1)一种特定类型的tasklet只能运行在一个CPU上,不能并行,只能串行执行。

      2)多个不同类型的tasklet可以并行在多个CPU上。

    3)软中断是静态分配的,在内核编译好之后,就不能改变。但tasklet就灵活许多,可以在运行时改变(比如添加模块时)。

    工作队列:

    上面我们介绍的可延迟函数运行在中断上下文中,于是导致了一些问题,说明它们不可挂起,也就是说软中断不能睡眠、不能阻塞,原因是由于中断上下文出于内核态,没有进程切换,所以如果软中断一旦睡眠或者阻塞,将无法退出这种状态,导致内核会整个僵死。因此,可阻塞函数不能用软中断来实现。但是它们往往又具有可延迟的特性。而且由于是串行执行,因此只要有一个处理时间较长,则会导致其他中断响应的延迟。为了完成这些不可能完成的任务,于是出现了工作队列,它能够在不同的进程间切换,以完成不同的工作。

    工作队列能运行在进程上下文,它将工作给一个内核线程,作为中断守护线程来使用。多个中断可以放在一个线程中,也可以每个中断分配一个线程。我们用结构体workqueue_struct表示工作者线程,工作者线程是用内核线程实现的。而工作者线程是如何执行被推后的工作——有这样一个链表,它由结构体work_struct组成,而这个work_struct则描述了一个工作,一旦这个工作被执行完,相应的work_struct对象就从链表上移去,当链表上不再有对象时,工作者线程就会继续休眠。因为工作队列是线程,所以我们可以使用所有可以在线程中使用的方法。

    如何选择下半部机制:

    1. 软中断和tasklet运行在中断上下文,工作队列运行在进程上下文。如果需要休眠则选择工作队列,否则选择tasklet;如果对性能要求较高则选择软中断。
    2. 从易用性考虑,首选工作队列,然后tasklet,最后是软中断,因为软中断需要静态创建。
    3. 从代码安全考虑,如果对底半部代码保护不够安全,则选择tasklet,因为相对软中断,tasklet对锁要求低,上面也简述它们工作方式以及运用场景。

    四、多处理器系统中断相关概念

    4.1处理器间中断

     在多处理器系统中,操作系统需要在多个处理器中协调操作,所以需要处理器中断(Inter-Processor-Interrupt,IPI)实现,IPI是一种特殊硬件中断,由CPU送出,其他CPU接收,处理CPU之间通信和同步操作。以下是x86中SMP定义的IPI,中断向量号用十六进制表示:

    SPURIOUS_APIC_VECTOR		0xff
    ERROR_APIC_VECTOR		0xfe
    RESCHEDULE_VECTOR		0xfd
    CALL_FUNCTION_VECTOR		0xfc
    CALL_FUNCTION_SINGLE_VECTOR	0xfb
    THERMAL_APIC_VECTOR		0xfa
    THRESHOLD_APIC_VECTOR		0xf9
    REBOOT_VECTOR			0xf8
    INVALIDATE_TLB_VECTOR_END	0xf7
    INVALIDATE_TLB_VECTOR_START	0xf0
    

     

    4.2中断亲和力

         将一个或多个中断服务程序绑定到特定的CPU上处理,这就是中断亲和力(SMP IRQ affinity)。我们可以使用中断亲和力来均衡各个CPU的负载,提高系统处理能力。

    以上便是本人对Linux中断的理解,若有纰漏欢迎指正!

    展开全文
  • linux内核中断处理.doc

    2019-07-31 13:22:20
    linux内核中断处理.doc
  • linux内核中断实例

    2012-04-11 10:25:59
    linux内核中断实例,可能通过模块参数插入指定的中断中
  • Linux 2.4内核中断之探析.pdf
  • Linux2_4内核中断之探析.PDF
  • Linux内核中断机制

    2010-02-04 10:31:52
    Linux内核中断机制,值得看看啊。 Linux内核中断机制,值得看看啊。 Linux内核中断机制,值得看看啊。 Linux内核中断机制,值得看看啊。
  • Linux内核中断分析

    2010-04-28 15:43:56
    Linux内核中断分析,希望对初学者有所帮助!
  • 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驱动开发——按键为例介绍Linux内核中断

    千次阅读 多人点赞 2020-12-14 17:49:36
    Linux内核中断1. 简介2. linux内核中中断的注册与注销3. 代码4. 测试 1. 简介      linux的中断处理过程和ARM裸板中的中断处理过程是一致的。不同点在于裸板开始时所有的软件都是自行编程完成的...


    所有的热爱都要不遗余力,真正喜欢它便给它更高的优先级,和更多的时间吧!

    关于 LINUX驱动 的其它文章请点击这里:     LINUX驱动


    1. 简介

         linux的中断处理过程和ARM裸板中的中断处理过程是一致的。不同点在于裸板开始时所有的软件都是自行编程完成的,在linux中很多中断相关的代码内核已经实现完毕了,某个中断产生后应该做什么样的具体工作没有完成,这就需要我们自己编程实现。

        ● 中断服务程序有如下特点

     (不属于)  1)中断处理程序不属于进程,它运行于中断上下文
     (不交换)  2)在中断上下文中不能做用户空间和内核空间的数据交互(linux不允许,STM32中裸板中可以)
     				 copy_to_user/copy_from_user /kmalloc  
     (不阻塞)  3)在中断上下文中不允许执行引起阻塞或者睡眠的函数,如:
                 	 sleep //睡眠函数
                 	 recv //阻塞函数
     (栈)  	  4)中断使用的栈为独立的栈空间,栈空间为一个4KB的内存页 
     (快)    5)要求对应的处理过程,执行速度越快越好
    

    2. linux内核中中断的注册与注销

    ● 注册

    /*
    @function:注册中断服务程序 
    @para:
        【irq】: 中断号,内核中将所有的中断源做了统一的编号, 获取linux内核中断号的两种方式:
             1)int gpio_to_irq(unsigned gpio)----将管脚编号gpio转换为对应的中断编号
             2) xxx_irq.h----IRQ_GPIO_A_START + index
    	【handler】: 要注册的irq号中断源对应的中断服务程序(用户端)
             btn_isr 也被称作回调函数/钩子函数   (strcmp/open调用别人写的代码,而我们写的函数,被系统调用,就叫做钩子函数)
         【flags】:  设置哪种情况下触发irq号中断
            IRQF_TRIGGER_RISING, 上升沿触发中断
            IRQF_TRIGGER_FALLING
            IRQF_TRIGGER_HIGH,高电平触发中断
            IRQF_TRIGGER_LOW
            IRQF_SHARED: 共享中断
        【name】: 名称
        【dev】:  调用handler 函数时传递的参数(用户调用使用的)
    @return:注册成功返回0  失败返回非0
    */
    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
    

    ● 注销

    /*
    @function:注销中断服务程序
    @note:当调用request_irq(....,dev)  当调用free_iqr(.......,dev)         最后一参数要保持一致     不然注销失败 
    */
    void free_irq(unsigned int irq, void *dev)
    

    3. 代码

    硬件上使用了四个按键,都是下降沿触发。

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/gpio.h>
    #include <linux/interrupt/h>
    
    MODELE_LICENSE("GPL");
    
    struct btn_desc
    {
        int irq;  //中断号
        char *name;
    } 
    
    struct btn_desc btns[]=
    {
        {IRQ_GPIO_A_START+28, "K1"},
        {IRQ_GPIO_B_START+30, "K2"},
        {IRQ_GPIO_B_START+31, "K3"}
        {IRQ_GPIO_B_START+9,  "K4"}, 
    }
    
    /*  回调/钩子函数, 用户自定义
    typedef irqreturn_t (*irq_handler_t)(int, void *);
      第一个参数:中断号
      第二个参数:用户参数    
    当按键触发的时候的时候,会打印 K1~K4         */
    irqreturn_t btn_isr(int irq, void *dev)
    {
        //int test = *((int *)dev);
        sturct btn_sesc *pdata = (sturct btn_desc *)dev;
        printk("%s press!\n",pdate->name);
        return IRQ_HANDLED;
    }
    
    //初始化:注册中断服务函数
    int __init btn_drv_init(void) 
    {
        //int irq = gqio_to_irq(PAD_GPIO_A + 28);           //获取终端号 
        for(i=0; i<ARRAY_SIZE(btns); i++))
        {
    	    //注册中断服务函数  约定一下,按键的时候才使用,具体见上文的函数解释
            int ret = request_irq(btns[i].irq, btn_isr, IRQF_RTIGGER_FALLING, btns[i].name, (void *)&(btns[i]));  
            if(ret)
            {
                printk("request_irq failed!\n");
                return -EAGAIN;
            }
        }
        return 0;
    }
    
    //注销中断服务程序
    void __exit btn_drv_exit(void)
    {
    	int i = 0;
        for(i=0; i<ARRAY_SIZE(btns); i++)
        {
            //int irq = gqio_to_irq(PAD_GPIO_A + 28); 
            free_irq(btns[i].irq, (void *)&(btns[i]));
        }
    }
    ...
    module_init(btn_drv_init);
    module_exit(btn_drv_exit);
    

    4. 测试

    向内核加载模块:

    insmod btn_drv.ko
    insmod: can't insert 'btn_drv.ko': Resource temporarily unavailable
    #原因: 内核中自带了按键的驱动程序,在其中通过request_irq的方式注册了K1对应的中断服务程序,导致再次调用request_irq时注册失败
    

    解决方法:

    make menuconfig
      Device Drivers  --->
          Input device support  --->
                [*]   Keyboards  --->  
                       < >   SLsiAP push Keypad support
      make uImage  #让开发板使用新内核
      cp arch/arm/boot/uImage /tftpboot/
      tftp 48000000 uImage
      mmc write 48000000 800 3000
    

    重启后,再向内核加载模块:

      insmod btn_drv.ko
      # 按下K1观察实验效果
    

    查看中断信息:

      cat /proc/interrupts
      #中断号   中断产生的次数
      134:          0           GPIO   K1
    

    关于 LINUX驱动 的其它文章请点击这里:     LINUX驱动

    展开全文
  • osdevc_libinterrupt 为我的OS内核中断lib
  • 内核中断的流程说的很清楚,学习Linux的必备资料
  • ARM内核中断技术,研究生小论文嵌入式arm7
  • Linux内核中断机制(一):中断注册方法

    千次阅读 2016-12-27 22:00:29
    今天在网上看到一份不错的讲解内核中断原理的文章,分享给大家! 1.中断注册方法 在 linux 内核中用于申请中断的函数是 request_irq(),函数原型在Kernel/irq/manage.c 中定义: int request_irq(unsigned ...
  • 首先说明,答案是否定的,内核中断号可以与硬件中断号不一致,但是这是个无聊的问题。。实用价值不大。但是却可以引起对内核软件中断号与硬件中断号关系的思考。 两者的关系我觉得可以从中断的初始化和分发过程来一...
  • 【proc/interrupts】内核中断

    千次阅读 2018-06-25 19:45:30
    首先是内核中断的分类,这里只是根据proc/interrupts下显示的中断进行分类,主要包含外部中断和IPI中断cat proc/interrupts CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 1: 107049 104452 ...
  • Linux内核中断系统

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

    千次阅读 2016-10-30 14:47:36
    为了支持中断的嵌套执行,Linux内核在进入中断服务程序之前会将硬中断开启,运行完中断服务程序之后再将硬中断关闭,在这期间硬件中断时可以被抢占的,而软中断执行过程中硬件中断始终是开启的。如果没
  • 内核中断初始化完成并且设备注册了响应的中断后,内核就可以响应相应的中断了。  首先要确定如果一个外设发出中断请求,它到底做了什么,设备是不能直接发出中断的,而是借助中断控制器,设备向中断控制器请求...
  • Linux 内核中断内幕

    千次阅读 2016-03-16 18:00:50
    本文对中断系统进行了全面的分析与探讨,主要包括中断控制器、中断分类、中断亲和力、中断线程...Linux 内核需要对连接到计算机上的所有硬件设备进行管理,毫无疑问这是它的份内事。如果要管理这些设备,首先得和它们互
  • Linux内核中断顶半部和底半部的理解

    千次阅读 多人点赞 2020-07-18 09:58:25
    设备的中断会打断内核进程中的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽量短小精悍。但是,这个良好的愿望往往与现实并不吻合。在大多数真实的系统中,当中断到来时,要完成的工作往往并不会是...
  • 细说Linux内核中断机制(详)

    千次阅读 2018-07-20 23:04:21
    本文着重介绍Linux内核中断处理的始末流程,因此对一些基本的概念应该有所了解。 2.硬件支持 我们知道,CPU有一个INTR引脚,用于接收中断请求信号。 而中断控制器用于提供中断向量,即第几号中断。 3.内核需要...
  • 1 什么是中断 Linux 内核需要对连接到计算机上的所有硬件设备进行管理,毫无疑问这是它的份内事。 如果要管理这些设备,首先得和它们互相通信才行,一般有两种方案可实现这种功能: l 轮询(polling) 内核定期对...
  • Linux内核中断:request_irq,free_irq

    千次阅读 2017-08-17 21:55:40
    Linux内核中断   步骤一:打开内核源代码,在里面搜索interrupt.h文件    打开文件后我们可以在里面看到这样的函数 typedef irqreturn_t (*irq_handler_t)(int,void *); 申请中断号 request_irq(unsigned ...
  • 细说内核中断机制

    千次阅读 2018-05-01 20:25:58
     中断通常分为同步中断和异步中断:2 同步中断是当指令执行时由CPU控制单元产生的,之所以称为同步,是因为只有在一条指令终止执行后CPU才会发出中断。 ◎ 异步中断是由其他硬件设备依照CPU时钟信号随机产生的。...
  • 用于将通过cat /sys/kernel/debug/tracing/trace命令获取的log文件,进行解析,和统计,分类:哪一号中断,在哪一号CPU上运行,总共运行时间多久,最大单次处理时间,最小单次处理时间,平均处理时间,相同中断发生...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 221,067
精华内容 88,426
关键字:

内核中断