对中断的一点理解

1.中断分为:(1)同步中断和异步中断

                   (2)可屏蔽中断和不可屏蔽中断(NMI)

                          (3)根据中断入口跳转方法的不同:

向量中断:不同中断有不同的入口地址。由硬件提供中断服务程序入口地址。

非向量中断:多个中断共享一个入口地址,进入该入口地之后再通过软件判断中断标志来识别具体的哪个中断。由软件提供中断服务程序入口地址。

                 (4)内部中断和外部中断。

2.中断服务例程两个返回值:

IRQ_NONE:中断程序接收到中断信号后发现这并不是注册时指定的中断原发出的中断信号。

IRQ_HANDLED:接收到了准确的中断信号,并且作了相应正确的处理。

         IRQ_RETVAL();

 

3.  (1)当一个给定的中断处理程序正在执行时,这条中断线上的其它中断都会被屏蔽。but,所有其他中断线上的中断都是打开的。因此这些不同中断线上的其他中断都能被处理。(用request_threaded_irq申请的中断:中断处理程序是关自己中断的,中断服务例程是开中断的。下半部运行时是允许中断请求的,而上半部运行时是关中断的,这是二者之间的主要区别)(有点乱,因为内核版本不同,就不同)

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

 

4.查询某个中断线是否可用:

         Intcan_request_irq(unsigned int irq, unsigned long flags);

成功返回非零值。

 

5./proc/interrupts 只会显示已经安装了中断处理例程的中断。

/proc/stat 记录了从开机到现在发生中断的次数。

例:

         Intr5167833 5154006 2 0 2

         第一个数是所有中断的总数,后面从中断0开始依次发生的中断数。

 

 

6.中断处理例程的行为受到的限制:

(1)不能向用户空间发送或者接受数据。(因为它不是在任何进程上下文中执行的)

(2)不能做任何可能生休眠的操作或者锁住一个信号量。

(3)不能调用schdule()函数。

 

7.通常来说我们必须在拥有自选锁的时候阻塞中断,以免死锁系统:

         禁用单个中断:voiddisable_irq(int irq);

                                       Void disable_irq_nosync(int irq);

                                       Void enable_irq(int irq);

                   如果disable_irq();调用两次也要调用两次enable_irq(irq);

         禁用所有的中断:

                                       Void local_irq_save(unsigned long flags);

                                       Void local_irq_disable(void);//没有跟踪,不像disable_irq();

                                       Void local_irq_restore(unsigned long flags);

                                       Void local_irq_enable(void);

        

8.顶半部和底半部:

         (1)Tsklet:通常是底半部处理的优先选择:因为这种机制非常快,但是所有的tsklet代码必须是原子的。

         structtasklet_struct {
structtasklet_struct *next;         /*指向链表中的下一个结构*/
          unsignedlong state;               /* 小任务的状态*/
          atomic_tcount;       /* 引用计数器*/
          void(*func) (unsignedlong);                /* 要调用的函数*/
          unsignedlong data;                /* 传递给函数的参数*/
};

                      ①他们可以被多次调度运行,但是tasklet 的调度并不会累加,也就是说实际只会运行一次,即使在激活tasklet的运行之前重复请求该tasklet的运行也是这样。Tasklet在中断处理例程结束前不会开始运行。

                            ②与一般的软中断不同,某一段tasklet代码在某个时刻只能在一个CPU上运行,而不像一般的软中断服务函数(即softirq_action结构中的action函数指针)那样——在同一时刻可以被多个CPU并发地执行

                            ③与BH机制不同,不同的tasklet代码在同一时刻可以在多个CPU上并发地执行,而不像BH机制那样必须严格地串行化执行(也即在同一时刻系统中只能有一个CPU执行BH函数)。

                   DECLARE_TASKLET(name,function, data);

name->tasklet 名字,data为function的unsigned long 参数

                   tasklet_schdule(&name);

         也可用DECLARE_TASKLET_DISABLED(name,func,data)声明一个tasklet,但是要用

       tasklet_enable(&my_tasklet);       /*  小任务现在被激活*/       激活。

                  

(2)工作队列:

                   可以使用系统默认的工作队列,但是如果我们的驱动程序具有特殊的延迟需求(或者需要在工作队列函数中长时间休眠),则应该创建我们自己的专用工作队列。工作队列(work queue)是另外一种将工作推后执行的形式,它和前面讨论的tasklet有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个 下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。

        

DECLARE_WORK(name,void (*func) (void *), void *data);

INIT_WORK(structwork_struct *work, woid(*func) (void *), void *data);

schedule_work(&work);

schedule_delayed_work(&work,delay);

 

structworkqueue_struct *my_queue

my_queue= create_singlethread_workqueue(name)

work_pending(work)//判断上一次是否执行完,执行完返回0

intqueue_work(structworkqueue_struct *, struct work_struct *);

destroy_workqueue(*);

 

 

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

struct irqaction {

         irq_handler_thandler;//指向具体的服务例程

         unsignedlong flags;//中断标志

         constchar *name;//请求中断的设备名称,对应于request_irq()函数中第四个参数

         void*dev_id;//共享中断时用

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

         intirq;

         structproc_dir_entry *dir;// 指向proc/irq/NN/name entry

         irq_handler_tthread_fn;//指向具体的一个线程化的中断

     struct task_struct *thread;//指向线程中断的指针
       unsigned long thread_flags;//线程中断标志

};

 

B.irq_desc的数据结构体

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

 

         structirq_desc {

         unsignedint               irq;//这个描述符对应的中断号

         irq_flow_handler_t handle_irq;// 指向该IRQ线的公共服务程序(即该IRQ所对应的中断处理程序)

         structirq_chip          *chip;// 它是一个structirq_chip类型的指针,是中断控制器的描述符。在2.6以前的版本中它是hw_irq_controller。

         structmsi_desc                  *msi_desc;//

         void                    *handler_data;// 是handler_irq的参数。

         void                    *chip_data;// 是指向irq_chip的指针。

         structirqaction         *action;   /* IRQ action list */

         unsignedint               status;               /* IRQ status */

 

         unsignedint               depth;      //中断线被激活时,值为0;当值为正数时,表示被禁止的次数。

         unsignedint               wake_depth;   /* nested wake enables */

         unsignedint               irq_count;         /* For detecting broken IRQs */

         unsignedint               irqs_unhandled;//该IRQ线上未处理中断发生的次数

         unsignedlong            last_unhandled;       /* Aging timer for unhandled count */

         spinlock_t                   lock;

#ifdef CONFIG_SMP

         cpumask_t                 affinity;

         unsignedint               cpu;

#endif

#ifdef CONFIG_GENERIC_PENDING_IRQ

         cpumask_t                 pending_mask;

#endif

#ifdef CONFIG_PROC_FS

         structproc_dir_entry       *dir;

#endif

         constchar                  *name;//申请中断设备的名字

} ____cacheline_internodealigned_in_smp;

 

C..struct irq_chip结构体

 

         structirq_chip {

         constchar         *name;//中断控制器名字

         unsignedint     (*startup)(unsigned int irq);//启动中断线

         void           (*shutdown)(unsigned int irq);//关闭中断线

         void           (*enable)(unsigned int irq);//允许中断

         void           (*disable)(unsigned int irq);//禁止中断

 

         void           (*ack)(unsigned int irq);//irq的全局ack

         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);

         void           (*set_affinity)(unsigned int irq,cpumask_t 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);

 

         /*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 copiedinto ->name.

          * Will disappear.

          */

         constchar         *typename;

};

 

5. request_threaded_irq()

 

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

如果handler是NULL,并且thread_fn不是NULL,则执行默认的handler,让你后执行thread_fn。如果你想为你的设备设置线程化的irq 处理程序,则你需要同时提供handler 和thread_fn 。handler 仍然在硬中断上下文被调用,所以它需要检查中断是否是由它服务的设备产生的。如果是,它返回IRQ_WAKE_THREAD ,这将会唤醒中断处理程序线程并执行thread_fn 。这种分开的中断处理程序设计是支持共享中断所必需的。Dev_id 必须全局唯一。通常是设备数据结构的地址。如果要使用的共享中断,则必须传递一个非NULL 的dev_id ,这是在释放中断的时候需要的。
4>.irqflags:表示中断标志位。
5>.devname:表示请求中断的设备的名称。

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

If you want to set up a threaded irq handler for yourdevice
then you need to supply @handler and @thread_fn. @handler ist
still called in hard interrupt context and has to check
whether the interrupt originates from the device. If yes it
needs to disable the interrupt on the device and return
IRQ_WAKE_THREAD which will wake up the handler thread and run
@thread_fn. This split handler design is necessary to support
shared interrupts.

request_threaded_irq() 函数返回0 表示成功,返回-EINVAL 表示中断号无效或处理函数指针为NULL ,返回-EBUSY 表示中断号已经被占用且不能共享。

 


你可能感兴趣的:(thread,数据结构,工作,struct,action,任务)