【转】Linux 中断学习之小试牛刀篇

原文网址:http://www.linuxidc.com/Linux/2011-02/32129.htm

前言

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

一.内核中断程序 :

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

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

1.程序interrupt.c

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

2.Makefile的编写

1 obj-m:=tiger.o   
 2    
 3 CURRENT_PATH:=$(shell pwd)   
 4 VERSION_NUM:=$(shell uname -r)   
 5 LINUX_PATH:=/usr/src/linux-headers-$(VERSION_NUM)   
 6    
 7    
 8 all :   
 9         make -C $(LINUX_PATH) M=$(CURRENT_PATH) modules   
10 clean:   
11         make -C $(LINUX_PATH) M=$(CURRENT_PATH) clean  
  1 obj-m:=tiger.o
  2 
  3 CURRENT_PATH:=$(shell pwd)
  4 VERSION_NUM:=$(shell uname -r)
  5 LINUX_PATH:=/usr/src/linux-headers-$(VERSION_NUM)
  6 
  7 
  8 all :
  9         make -C $(LINUX_PATH) M=$(CURRENT_PATH) modules
 10 clean:
 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则表示设备可以被看做是事件随见的发生源。

以下是官方解释:

/*   
* These flags used only by the kernel as part of the   
* irq handling routines.   
*   
* IRQF_DISABLED - keep irqs disabled when calling the action handler   
* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator   
* IRQF_SHARED - allow sharing the irq among several devices   
* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur   
* IRQF_TIMER - Flag to mark this interrupt as timer interrupt   
* IRQF_PERCPU - Interrupt is per cpu   
* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing   
* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is   
*                registered first in an shared interrupt is considered for   
*                performance reasons)   
*/   
#define IRQF_DISABLED           0x00000020   
#define IRQF_SAMPLE_RANDOM       0x00000040   
#define IRQF_SHARED             0x00000080   
#define IRQF_PROBE_SHARED       0x00000100   
#define IRQF_TIMER               0x00000200   
#define IRQF_PERCPU             0x00000400   
#define IRQF_NOBALANCING         0x00000800   
#define IRQF_IRQPOLL             0x00001000    
    /* 
    * These flags used only by the kernel as part of the 
    * irq handling routines. 
    * 
    * IRQF_DISABLED - keep irqs disabled when calling the action handler 
    * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator 
    * IRQF_SHARED - allow sharing the irq among several devices 
    * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur 
    * IRQF_TIMER - Flag to mark this interrupt as timer interrupt 
    * IRQF_PERCPU - Interrupt is per cpu 
    * IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing 
    * IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is 
    *                registered first in an shared interrupt is considered for 
    *                performance reasons) 
    */ 
    #define IRQF_DISABLED           0x00000020 
    #define IRQF_SAMPLE_RANDOM       0x00000040 
    #define IRQF_SHARED             0x00000080 
    #define IRQF_PROBE_SHARED       0x00000100 
    #define IRQF_TIMER               0x00000200 
    #define IRQF_PERCPU             0x00000400 
    #define IRQF_NOBALANCING         0x00000800 
    #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:接收到了准确的中断信号,并且作了相应正确的处理。

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

John哥说明:

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

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

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

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

113struct irqaction {   
114        irq_handler_t handler;   
115        unsigned long flags;   
116        const char *name;   
117        void *dev_id;   
118        struct irqaction *next;   
119        int irq;   
120        struct proc_dir_entry *dir;   
121        irq_handler_t thread_fn;   
122        struct task_struct *thread;   
123        unsigned long thread_flags;   
124};   
125  
 113struct irqaction {
 114        irq_handler_t handler;
 115        unsigned long flags;
 116        const char *name;
 117        void *dev_id;
 118        struct irqaction *next;
 119        int irq;
 120        struct proc_dir_entry *dir;
 121        irq_handler_t thread_fn;
 122        struct task_struct *thread;
 123        unsigned long thread_flags;
 124};
 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:线程中断的标志。

B.irq_desc的数据结构体

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

175struct irq_desc {   
176        unsigned int            irq;   
177        struct timer_rand_state *timer_rand_state;   
178        unsigned int            *kstat_irqs;   
179#ifdef CONFIG_INTR_REMAP   
180        struct irq_2_iommu      *irq_2_iommu;   
181#endif   
182        irq_flow_handler_t      handle_irq;   
183        struct irq_chip         *chip;   
184        struct msi_desc         *msi_desc;   
185        void                    *handler_data;   
186        void                    *chip_data;   
187        struct irqaction        *action;        /* IRQ action list */  
188        unsigned int            status;         /* IRQ status */  
189   
190        unsigned int            depth;          /* nested irq disables */  
191        unsigned int            wake_depth;     /* nested wake enables */  
192        unsigned int            irq_count;      /* For detecting broken IRQs */  
193        unsigned long           last_unhandled; /* Aging timer for unhandled count */  
194        unsigned int            irqs_unhandled;   
195        raw_spinlock_t          lock;   
196#ifdef CONFIG_SMP   
197        cpumask_var_t           affinity;   
198        const struct cpumask    *affinity_hint;   
199        unsigned int            node;   
200#ifdef CONFIG_GENERIC_PENDING_IRQ   
201        cpumask_var_t           pending_mask;   
202#endif   
203#endif   
204        atomic_t                threads_active;   
205        wait_queue_head_t       wait_for_threads;   
206#ifdef CONFIG_PROC_FS   
207        struct proc_dir_entry   *dir;   
208#endif   
209        const char              *name;   
210} ____cacheline_internodealigned_in_smp;   
211   
212extern void arch_init_copy_chip_data(struct irq_desc *old_desc,   
213                                        struct irq_desc *desc, int node);   
214extern void arch_free_chip_data(struct irq_desc *old_desc, struct irq_desc *desc);   
215   
216#ifndef CONFIG_SPARSE_IRQ   
217extern struct irq_desc irq_desc[NR_IRQS];  
 175struct irq_desc {
 176        unsigned int            irq;
 177        struct timer_rand_state *timer_rand_state;
 178        unsigned int            *kstat_irqs;
 179#ifdef CONFIG_INTR_REMAP
 180        struct irq_2_iommu      *irq_2_iommu;
 181#endif
 182        irq_flow_handler_t      handle_irq;
 183        struct irq_chip         *chip;
 184        struct msi_desc         *msi_desc;
 185        void                    *handler_data;
 186        void                    *chip_data;
 187        struct irqaction        *action;        /* IRQ action list */
 188        unsigned int            status;         /* IRQ status */
 189
 190        unsigned int            depth;          /* nested irq disables */
 191        unsigned int            wake_depth;     /* nested wake enables */
 192        unsigned int            irq_count;      /* For detecting broken IRQs */
 193        unsigned long           last_unhandled; /* Aging timer for unhandled count */
 194        unsigned int            irqs_unhandled;
 195        raw_spinlock_t          lock;
 196#ifdef CONFIG_SMP
 197        cpumask_var_t           affinity;
 198        const struct cpumask    *affinity_hint;
 199        unsigned int            node;
 200#ifdef CONFIG_GENERIC_PENDING_IRQ
 201        cpumask_var_t           pending_mask;
 202#endif
 203#endif
 204        atomic_t                threads_active;
 205        wait_queue_head_t       wait_for_threads;
 206#ifdef CONFIG_PROC_FS
 207        struct proc_dir_entry   *dir;
 208#endif
 209        const char              *name;
 210} ____cacheline_internodealigned_in_smp;
 211
 212extern void arch_init_copy_chip_data(struct irq_desc *old_desc,
 213                                        struct irq_desc *desc, int node);
 214extern void arch_free_chip_data(struct irq_desc *old_desc, struct irq_desc *desc);
 215
 216#ifndef CONFIG_SPARSE_IRQ
 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的具体代码如下:

111struct irq_chip {   
112        const char      *name;   
113        unsigned int    (*startup)(unsigned int irq);   
114        void            (*shutdown)(unsigned int irq);   
115        void            (*enable)(unsigned int irq);   
116        void            (*disable)(unsigned int irq);   
117   
118        void            (*ack)(unsigned int irq);   
119        void            (*mask)(unsigned int irq);   
120        void            (*mask_ack)(unsigned int irq);   
121        void            (*unmask)(unsigned int irq);   
122        void            (*eoi)(unsigned int irq);   
123   
124        void            (*end)(unsigned int irq);   
125        int             (*set_affinity)(unsigned int irq,   
126                                        const struct cpumask *dest);   
127        int             (*retrigger)(unsigned int irq);   
128        int             (*set_type)(unsigned int irq, unsigned int flow_type);   
129        int             (*set_wake)(unsigned int irq, unsigned int on);   
130   
131        void            (*bus_lock)(unsigned int irq);   
132        void            (*bus_sync_unlock)(unsigned int irq);   
133   
134        /* Currently used only by UML, might disappear one day.*/  
135#ifdef CONFIG_IRQ_RELEASE_METHOD   
136        void            (*release)(unsigned int irq, void *dev_id);   
137#endif   
138        /*  
139         * For compatibility, ->typename is copied into ->name.  
140         * Will disappear.  
141         */  
142        const char      *typename;   
143};   
144  
 111struct irq_chip {
 112        const char      *name;
 113        unsigned int    (*startup)(unsigned int irq);
 114        void            (*shutdown)(unsigned int irq);
 115        void            (*enable)(unsigned int irq);
 116        void            (*disable)(unsigned int irq);
 117
 118        void            (*ack)(unsigned int irq);
 119        void            (*mask)(unsigned int irq);
 120        void            (*mask_ack)(unsigned int irq);
 121        void            (*unmask)(unsigned int irq);
 122        void            (*eoi)(unsigned int irq);
 123
 124        void            (*end)(unsigned int irq);
 125        int             (*set_affinity)(unsigned int irq,
 126                                        const struct cpumask *dest);
 127        int             (*retrigger)(unsigned int irq);
 128        int             (*set_type)(unsigned int irq, unsigned int flow_type);
 129        int             (*set_wake)(unsigned int irq, unsigned int on);
 130
 131        void            (*bus_lock)(unsigned int irq);
 132        void            (*bus_sync_unlock)(unsigned int irq);
 133
 134        /* Currently used only by UML, might disappear one day.*/
 135#ifdef CONFIG_IRQ_RELEASE_METHOD
 136        void            (*release)(unsigned int irq, void *dev_id);
 137#endif
 138        /*
 139         * For compatibility, ->typename is copied into ->name.
 140         * Will disappear.
 141         */
 142        const char      *typename;
 143};
 144

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

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

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

135request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,   
136            const char *name, void *dev)   
137{   
138        return request_threaded_irq(irq, handler, NULL, flags, name, dev);   
139}   
140  
 135request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
 136            const char *name, void *dev)
 137{
 138        return request_threaded_irq(irq, handler, NULL, flags, name, dev);
 139}
 140

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

先看一下官方的解释

1006/**  
1007 *      request_threaded_irq - allocate an interrupt line  
1008 *      @irq: Interrupt line to allocate  
1009 *      @handler: Function to be called when the IRQ occurs.  
1010 *                Primary handler for threaded interrupts  
1011 *                If NULL and thread_fn != NULL the default  
1012 *                primary handler is installed  
1013 *      @thread_fn: Function called from the irq handler thread  
1014 *                  If NULL, no irq thread is created  
1015 *      @irqflags: Interrupt type flags  
1016 *      @devname: An ascii name for the claiming device  
1017 *      @dev_id: A cookie passed back to the handler function  
1018 *  
1019 *      This call allocates interrupt resources and enables the  
1020 *      interrupt line and IRQ handling. From the point this  
1021 *      call is made your handler function may be invoked. Since  
1022 *      your handler function must clear any interrupt the board  
1023 *      raises, you must take care both to initialise your hardware  
1024 *      and to set up the interrupt handler in the right order.  
1025 *  
1026 *      If you want to set up a threaded irq handler for your device  
1027 *      then you need to supply @handler and @thread_fn. @handler ist  
1028 *      still called in hard interrupt context and has to check  
1029 *      whether the interrupt originates from the device. If yes it  
1030 *      needs to disable the interrupt on the device and return  
1031 *      IRQ_WAKE_THREAD which will wake up the handler thread and run  
1032 *      @thread_fn. This split handler design is necessary to support  
1033 *      shared interrupts.  
1034 *  
1035 *      Dev_id must be globally unique. Normally the address of the  
1036 *      device data structure is used as the cookie. Since the handler  
1037 *      receives this value it makes sense to use it.  
1038 *  
1039 *      If your interrupt is shared you must pass a non NULL dev_id  
1040 *      as this is required when freeing the interrupt.  
1041 *  
1042 *      Flags:  
1043 *  
1044 *      IRQF_SHARED             Interrupt is shared  
1045 *      IRQF_SAMPLE_RANDOM      The interrupt can be used for entropy  
1046 *      IRQF_TRIGGER_*          Specify active edge(s) or level  
1047 *  
1048 */  
1006/**
1007 *      request_threaded_irq - allocate an interrupt line
1008 *      @irq: Interrupt line to allocate
1009 *      @handler: Function to be called when the IRQ occurs.
1010 *                Primary handler for threaded interrupts
1011 *                If NULL and thread_fn != NULL the default
1012 *                primary handler is installed
1013 *      @thread_fn: Function called from the irq handler thread
1014 *                  If NULL, no irq thread is created
1015 *      @irqflags: Interrupt type flags
1016 *      @devname: An ascii name for the claiming device
1017 *      @dev_id: A cookie passed back to the handler function
1018 *
1019 *      This call allocates interrupt resources and enables the
1020 *      interrupt line and IRQ handling. From the point this
1021 *      call is made your handler function may be invoked. Since
1022 *      your handler function must clear any interrupt the board
1023 *      raises, you must take care both to initialise your hardware
1024 *      and to set up the interrupt handler in the right order.
1025 *
1026 *      If you want to set up a threaded irq handler for your device
1027 *      then you need to supply @handler and @thread_fn. @handler ist
1028 *      still called in hard interrupt context and has to check
1029 *      whether the interrupt originates from the device. If yes it
1030 *      needs to disable the interrupt on the device and return
1031 *      IRQ_WAKE_THREAD which will wake up the handler thread and run
1032 *      @thread_fn. This split handler design is necessary to support
1033 *      shared interrupts.
1034 *
1035 *      Dev_id must be globally unique. Normally the address of the
1036 *      device data structure is used as the cookie. Since the handler
1037 *      receives this value it makes sense to use it.
1038 *
1039 *      If your interrupt is shared you must pass a non NULL dev_id
1040 *      as this is required when freeing the interrupt.
1041 *
1042 *      Flags:
1043 *
1044 *      IRQF_SHARED             Interrupt is shared
1045 *      IRQF_SAMPLE_RANDOM      The interrupt can be used for entropy
1046 *      IRQF_TRIGGER_*          Specify active edge(s) or level
1047 *
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()内部是如何实现的。

1049int request_threaded_irq(unsigned int irq, irq_handler_t handler,   
1050                         irq_handler_t thread_fn, unsigned long irqflags,   
1051                         const char *devname, void *dev_id)   
1052{   
1053        struct irqaction *action;   
1054        struct irq_desc *desc;   
1055        int retval;   
1056   
1057        /*  
1058         * Sanity-check: shared interrupts must pass in a real dev-ID,  
1059         * otherwise we‘ll have trouble later trying to figure out  
1060         * which interrupt is which (messes up the interrupt freeing  
1061         * logic etc).  
1062         */  
1063        if ((irqflags & IRQF_SHARED) && !dev_id)   
1064                return -EINVAL;   
1065   
1066        desc = irq_to_desc(irq);   
1067        if (!desc)   
1068                return -EINVAL;   
1069   
1070        if (desc->status & IRQ_NOREQUEST)   
1071                return -EINVAL;   
1072   
1073        if (!handler) {   
1074                if (!thread_fn)   
1075                        return -EINVAL;   
1076                handler = irq_default_primary_handler;   
1077        }   
1078   
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;   
1088   
1089        chip_bus_lock(irq, desc);   
1090        retval = __setup_irq(irq, desc, action);   
1091        chip_bus_sync_unlock(irq, desc);   
1092   
1093        if (retval)   
1094                kfree(action);   
1095   
1096#ifdef CONFIG_DEBUG_SHIRQ   
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        }   
1114#endif   
1115        return retval;   
1116}  
1049int request_threaded_irq(unsigned int irq, irq_handler_t handler,
1050                         irq_handler_t thread_fn, unsigned long irqflags,
1051                         const char *devname, void *dev_id)
1052{
1053        struct irqaction *action;
1054        struct irq_desc *desc;
1055        int retval;
1056
1057        /*
1058         * Sanity-check: shared interrupts must pass in a real dev-ID,
1059         * otherwise we‘ll have trouble later trying to figure out
1060         * which interrupt is which (messes up the interrupt freeing
1061         * logic etc).
1062         */
1063        if ((irqflags & IRQF_SHARED) && !dev_id)
1064                return -EINVAL;
1065
1066        desc = irq_to_desc(irq);
1067        if (!desc)
1068                return -EINVAL;
1069
1070        if (desc->status & IRQ_NOREQUEST)
1071                return -EINVAL;
1072
1073        if (!handler) {
1074                if (!thread_fn)
1075                        return -EINVAL;
1076                handler = irq_default_primary_handler;
1077        }
1078
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;
1088
1089        chip_bus_lock(irq, desc);
1090        retval = __setup_irq(irq, desc, action);
1091        chip_bus_sync_unlock(irq, desc);
1092
1093        if (retval)
1094                kfree(action);
1095
1096#ifdef CONFIG_DEBUG_SHIRQ
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        }
1114#endif
1115        return retval;
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()函数内部是如何实现的

993void free_irq(unsigned int irq, void *dev_id)   
 994{   
 995        struct irq_desc *desc = irq_to_desc(irq);   
 996   
 997        if (!desc)   
 998                return;   
 999   
1000        chip_bus_lock(irq, desc);   
1001        kfree(__free_irq(irq, dev_id));   
1002        chip_bus_sync_unlock(irq, desc);   
1003}  
 993void free_irq(unsigned int irq, void *dev_id)
 994{
 995        struct irq_desc *desc = irq_to_desc(irq);
 996
 997        if (!desc)
 998                return;
 999
1000        chip_bus_lock(irq, desc);
1001        kfree(__free_irq(irq, dev_id));
1002        chip_bus_sync_unlock(irq, desc);
1003}

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

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

时间: 2024-10-13 19:49:20

【转】Linux 中断学习之小试牛刀篇的相关文章

Linux Shell学习之基础篇

在学习Linux和OpenStack过程中,感觉不管是大规模部署部署还是运维,Shell脚本都已经是标配,所以学好脚本很有必要. 以下仅为Linux Shell的一些基础笔记,这里作为笔记记下. ===============linux shell简介====================== 1.命令补全:连续按两次Tab   文件或者文件夹补全:一次Tab   命令帮助:--help 2.chmod u=rwx,g+w,o+r filename   chown root.root file

LINUX中断学习笔记【转】

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

linux命令学习-首篇

在home目录的.bashrc 添加了如下逻辑,目的是可以"每日"学习或复习linux命令 # 学习linux命令 # echo "Did you know that:"; whatis $(ls /bin | shuf -n 1) cowsay -f $(ls /usr/share/cowsay/cows | shuf -n 1 | cut -d. -f1) $(whatis $(ls /bin) 2>/dev/null | shuf -n 1) 效果图:

linux内核学习:中断

编程相关 注册中断 int request_irq( unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev) typedef irqreturn_t (*irq_handler_t)(int, void *); IRQF_DISABLED 会禁用除本本身以外的其它中断,一般是不用的 IRQF_SAMPLE_RANDOM 可以帮助内核随机数的产生.如果中断产生地毫无规律,可

linux内核学习:中断中推后执行的部分

软中断-softirq 特点 相同和不同的软中断都可以在不同处理器上同时执行 一个软中断不会抢占另一个软中断 何时执行 从中断程序返回时 ksoftirqd线程中 显示调用 软中断最多有32个,一个32位的整型数据可以被用来标记刮起的软中断 使用策略 软中断应用于确实需要的场合,目前只有网络驱动和SCSI驱动中使用.另外,内核定时器和tasklet建立在软中断之上. 使用方法 注册软中断 void open_softirq(int nr, void (*action)(struct softir

Linux入门学习教程:虚拟机体验之KVM篇

在上一篇中,我展示了虚拟机软件QEMU的使用.效果及其性能,同时也分析了不同用户对虚拟机的不同追求.但是不管是桌面用户还是企业级用户,对虚拟机 软件的追求有一点是共同的,那就是性能.QEMU是一个强大的虚拟机软件,它可以完全以软件的形式模拟出一台完整的电脑所需的所有硬件,甚至是模拟出不同 架构的硬件,在这些虚拟的硬件之上,可以安装完整的操作系统.QEMU的运行模式如下图: 很显然,这种完全以软件模拟硬件的形式虽然功能强大,但是性能难以满足用户的需要.模拟出的硬件的性能和物理硬件的性能相比,必然会

Linux学习笔记——第一篇——Ubuntu安装与操作

笔者是Windows的使用者,由于Coding的需要以及在Linux下开发的方便,所以开始使用Linux. 当然笔者还是割舍不下Windows的,毕竟很多通讯工具等软件以及游戏在Linux下是没有发行的,所以笔者使用了虚拟机啊. 下面给出简单的安装过程. 1.下载虚拟机软件,笔者比较喜欢VMPlayer,因为它比较轻便且免费,并且很好的支持了拖拽复制功能(VM TOOL),当然也可以使用如VirtualBox.VPC等 链接:https://my.vmware.com/web/vmware/fr

Linux内核学习总结

李泽源 原创作品 转载请注明出处 <Linux内核分析>MOOC课程:http://mooc.study.163.com/course/USTC-1000029000 [Linux内核学习总结] 幸福来得很突然,这门课就快结束了…… 是时候,总结下这段时间的坚持了,也给同样对Linux内核有兴趣的你一个指南. 在这门课的学习过程中,按照老师的要求,每次课后都写一篇博文,这是一个很好的学习方式.每当写这些文章的时候,总是要多看几遍视频,再查查相关的资料,才能勉强凑成一个完整的文档:同时也把自己学

以Qemu模拟Linux,学习Linux内核

文章名称:以Qemu模拟Linux,学习Linux内核作      者:five_cent文章地址:http://www.cnblogs.com/senix/archive/2013/02/21/2921221.html维护日志:2013-02-21 建立文档(注:文章参考自http://www.linuxidc.com/Linux/2011-07/39373.htm, 是对该篇文章的一些补充和说明.文章内所使用的环境是Ubuntu 12.04,如果其中遇到编译问题,请自行参考错误说明,配置依赖