中断处理的总结

1 中断处理程序
        响应特定函数内核执行的函数,称为中断处理程序;
        注册中断处理程序                   int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);

2 共享的中断处理程序必须符合的要求
        a). request_irq();函数的flag参数设置为SA_SHIRQ;
        b). 对每个中断处理程序来说每个dev必须是唯一的,不能给共享的中断处理程序设置为NULL;
        c). 中断处理程序要能判断是否真正产生中断;

3 中断上下文:
    上半部 :实际响应中断的例程,也是用request_irq函数注册的处理例程;
    下半部 :被上半部调度的处理函数,也是在稍后更安全的时间内执行的例程; 

4中断下半部:
     软中断       是在编译期间静态分配的,主要用于执行频率高连续性高的情况下;软中断处理程序执行的时候,允许响应中断, 但它自己不能休 眠;引入软中断的主要原因是其可扩展性,如果不需要扩展到多个处理器,那么,就使用tasklet吧, tasklet本质上也是软中断,只 不过同一处理程序的多个实例不能在多个处理器上同时运行;
     
    tasklet       tasklet的本质是软中断,大多情况下使用tasklet效果不错,而且很容易使用。它有2类软中断代表HI_SOFTORQ和TASKLET_SOFTIRQ ,这两者的主要区别是HI_SOFTORQ软中断先于TASKLET_SOFTIRQ软中断执行;它既可以 静态的注册也可以 动态的注册,由于它是靠软中断实现的,所以tasklet不能睡眠,这就意味着你不能在tasklet中 使用信号量或者其他的阻塞式的函 数,如果你的tasklet和其他的tasklet或者软中断共享了数据, 你必须要进行适当的锁保护;
     
    工作队列     它是另外一种将工作推后执行的形式,它把工作推后给内核线程去执行,它允许重新调度甚至是睡眠; 它是唯一能够在进程上下文 运行睡眠的下半部实现机制;
     定时器

5 同步机制
    
    原子操作    不可分割的指令;有原子整数操作和原子位操作之分;
     
    自旋锁        只能被一个线程持有的锁机制,如果该所被其他线程持有,本进程将会进入死循环等待所持有线程的释放,
如果没有被其他线程持 有,则本线程会继续执行下去;自旋锁是为了防止多个执行线程同时进入临界区;
     
    信号量        它是一种睡眠锁,如果 有一个任务试图获得一个已经被占有的信号量时,信号量会将其推进一个等待队列,
然后让其睡眠,这时处 理器能重获自由,从而去执行其他的代码。当持有信号量的进程将信号量 释放后, 处于等待队列中的那个任务将被唤醒,并获得该 信号量。
                
                     从信号量的睡眠中我们得出一些结论:
                            1.由于争用信号量的进程在等待锁重新变为可用时会睡眠,所以信号量适用于锁会长时间持有的情况;
                            2.锁被短时间持有时,使用信号量就不太适宜了,因为睡眠、维护等待队列以及唤醒所花费的开销可能比锁 被占用的全部时间还要长;
                            3.由于执行线程在锁被争用时会睡眠,所以只能在进程上下文 中才能获取信号量,因为在中断上下文 是不能被调度的;
                            4.你可以持有信号量去睡眠(也可以不睡眠), 因为当其他的进程试图获得同一信号量时不会因此而死锁( 因为该线程也只是去睡眠而 已,而你最终会继续执行);       
                            5.在你占用信号量时不能同时占用自旋锁,因为在你等待信号时可能会睡眠,而持有自旋锁时是不允许睡眠的;                
         
    完成量        如果一个任务要执行一些工作时,另一个任务就会在完成变量上等待,当任务完成工作后,会使用完成变量去呼唤 在等待的任务。
                           完成变量的方法:
                            1.init_completion(struct completion *)                        初始化指定的动态创建的完成变量
                            2.wait_for_completion(structcompletion *)                     等待指定的完成变量接受信号
                            3.completion(struct completion *)                             发信号唤醒任何等待的任务       

6 定时器          定时器用于调度函数(定时器处理程序)在未来某个特定时间执行。与任务队列不同,你可以指定你的函数在未来 何时被调用, 但你不能确 定任务队列中的任务何时执行。
                       
                        Linux使用了两种定时器,所谓的“旧定时器”和新定时器:
                        1.旧定时器包括32个静态的定时器。旧定时器的数据结构包括一个标明活动的定时器的位屏蔽码和定时器数组, 数组的每个成员又包括一 个处理程序和该定时器的超时值。 旧的定时器结构的主要问题在于, 每个需要定时器 来延迟操作的设备都要 静态地分配给一个定时器
                        2.新的定时器被组织成双向链接表。这意味着你加入任意多的定时器。定时器包括它的timeout(超时)(单位是jiffies) 和超时时调用的函 数。定时器处理程序需要一个参数,该参数和处理程序函数指针本身一起存放在一个数据结构中。

7.相关接口函数

#include <linux/param.h>

HZ                                              HZ符号指出每秒钟产生的时钟滴答数。

 

volatile unsigned long jiffies                  jiffies变量每个时钟滴答后加1;因此它每秒增加1HZ

 

#include <linux/time.h>

void do_gettimeofday(struct timeval *tv);       该函数返回当前时间。1.2版的内核并不提供。

 

#include <linux/delay.h>

void udelay(unsigned long usecs);               udelay函数延迟整数数目的微秒数,但不应超过1毫秒。

 

#include <linux/tqueue.h>

void queue_task(struct tq_struct *task, task_queue *list);

void queue_task_irq();

void queue_task_irq_off();

这些函数注册延迟执行的任务。第一个函数,queue_task,总是可以被调用;第二个函数只能在不可重入的函数内被调用,而最后一个函数只有在关闭中断后才能被调用。新近的内核只提供第一种函数接口了

 

void run_task_queue(task_queue *list);           该函数运行任务队列。

 

task_queue tq_immediate, tq_timer, tq_scheduler; 这些预定义的任务队列在每个时钟滴答后并在内核调度新的进程前尽快地分别得到执行。

 

#include <linux/timer.h>

void init_timer(struct timer_list *timer);       该函数初始化新分配的定时器队列。

 

void add_timer(struct timer_list * timer);       该函数将定时器插入待处理的定时器的全局队列。

 

int del_timer(struct timer_list *timer);

del_timer函数将定时器从挂起的定时器队列中删除。如果队列中有该定时器,del_timer返回1,否则返回0

                                        
中断机制:硬件设备在产生事件的时候向内核发送信号(变内核主动为硬件主动)的机制;
#include <linux/interrupt.h>
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
 irq:              中断号 arch/arm/plat-s3c64xx/include/plat/irqs.h
 handler:          中断处理函数指针 irqreturn_t handler(int irq, void *dev_id);
 irqreturn_t:      See include/linux/irqreturn.h
 irqflags:         See line 21-59 in include/linux/interrupt.h        使用IRQF_SHARED共享irq时, irqflags必须相同 

#include <asm/signal.h>       =============== request_irq函数的标志
          SA_INTERRUPT:要求安装一个快速的处理例程;
          SA_SHIRQ:安装一个共享的处理例程;
          SA_SAMPLE_RANDOM:中断时间戳可用来生产系统熵;
  
如:   request_irq(IRQ_EINT(0), handler1, IRQF_TRIGGER_FALLING | IRQF_SHARED, "dev1", &dev1);
         request_irq(IRQ_EINT(0), handler2, IRQF_TRIGGER_FALLING | IRQF_SHARED, "dev2", &dev2);
         devname:         设备名,   cat /proc/interrupts
         dev_id:              发生中断时将dev_id传递给handler函数, irqflags含有IRQF_SHARED时dev_id不能为NULL, 并且要保证唯一 dev_id 一般采用当前设备的结构体指针
注:
        不能在中断上下文和不允许阻塞的代码中调用request_irq()函数;kmalloc()函数是会休眠的函数。

void free_irq ( unsigned int irq, void * dev_id); 释放匹配irq和dev_id的中断, 如果irq有多个相同的dev_id, 将释放第一个 So, 共享中断的dev_id不是唯一时, 可能会释放到其它设备的中断 

启用可禁止单个指定的中断
void disable_irq(unsigned int irq);                     关闭指定irq号中断  (当前所有正在处理程序退出后才能返回)
void enable_irq(unsigned int irq);                      开启指定irq号中断  使用共享处理例程的驱动不能使用这两个函数;

无条件启动和禁止当前CPU的中断
void local_irq_disable(void);                               无条件关闭当前CPU中断
void local_irq_enable(void);                                无条件开启当前CPU中断

无条件启动和禁止当前CPU的中断,但是保存和恢复当前状态flag
void local_irq_save(unsigned long flags);         关闭当前CPU中断并保存当前状态到flags
void local_irq_restore(unsigned long flags);      传递flags保存的先前中断状态到当前CPU

in_interrupt();         如果在中断上下文中,则返回非0;如果在进程上下文中,则返回0;
in_irq();               如果当前正在执行的中断处理程序,则返回非0,否则返回0;
注: 没有关闭和开启所有CPU中断的函数(没必要);   















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值