linux中断处理顶半部和底半部

你。 提交于 2020-03-07 01:18:04

为了平衡中断处理程序时间要求短和工作量要大的问题,linux将中断处理程序分为顶半部(top half)和底半a部(bottom half)

我们在编写服务端网络程序时往往会开启一个监听线程来专门接收客户端的请求。一旦接收到某个客户端的请求,一般不会直接在监听线程中处理客户端的请求,而是再开启一个专门处理客户端请求的线程,并在该线程中处理客户端的请求。这样监听线程就可以解脱出来处理更多的客户端请求,从而大大提高服务端程序的吞吐量。
中断处理程序和服务端网络程序类似,当硬件向内核发送中断请求时,内核(在这里内核就相当于服务端网络程序)首先会接收中断请求,这个接收中断请求的任务就是由中断处理程序的顶半部完成的。然而在顶半部中并不会执行中断处理的核心代码,而这些代码需要在底半部完成。对于顶半部来说,除了接收中断请求外,还会进行"登记工作"。也就是说要将底半部处理程序挂到发送中断请求的设备的底半部执行队列中。这样的安排,顶半部的执行速度就会很快,可以服务更多的中断请求。

如下特征的任务放在顶半部:
1、对时间非常敏感
2、与硬件相关的
3、不能被其他中断打断的工作

除了以上三点,基本考虑放在底半部。
中断底半部实现的机制有:
1.软中断softirq
2.tasklet(由软中断实现)
3.工作队列work queue

二、函数:

<linux/interrupt.h>//函数所在内核头文件
    
    **typedef irqreturn_t (*irq_handler_t)(int , void *);  函数指针类型**

    irqreturn_t    返回值类型是枚举类型
    IRQ_NONE			没有处理
    IRQ_HANDLED		已经处理完成

    irqreturn_t    irq_handler(int  irq,void*args)
    {
        
    }
    
    中断的触发方式(中断标志中的一种):
        IRQF_TRIGGER_RISING      上升沿触发
        IRQF_TRIGGER_FALLING    下降沿触发
        IRQF_TRIGGER_HIGH          高电平触发
        IRQF_TRIGGER_LOW 		低电平触发
        
    IRQF_DISABLED      屏蔽同级别中断
    IRQF_SHARED         中断共享
    
    **int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);**
    功能:向内核请求注册中断
    参数:
            @irq	   		中断号  //使用gpio_to_irq()函数获得终中断号
            @handler		中断处理函数指针
            @flags          中断标志
            @name			中断名字
            @dev            私有数据
    返回值:成功返回0,失败返回负数错误码
    
    
    **void free_irq(unsigned int, void *);   //释放/注销中断**

三、底半部处理机制例子
1、软中断
特点:可以被中断(顶半部)打断,不可以被中断底半部打断,不参与进程调度
要求:可以有耗时操作(相对来将) —>>> 可以使用for ,不可以使用延时和睡眠函数
2、tasklet
特点:可以被中断(顶半部)打断,不可以被中断底半部打断,不参与进程调度
要求:可以有耗时操作(相对来将) —>>> 可以使用for ,不可以使用延时和睡眠函数
3、工作队列
特点:可以被中断(顶半部)打断,也可以被中断底半部打断,也参与进程调度
要求:可以有耗时操作,也可以有涉及进程调度相关函数

4、使用 tasklet 作为底半部处理中断的设备驱动程序

 /*中断处理底半部*/ 
 void xxx_do_tasklet(unsigned long) 
 { 
   ... 
 } 
  /*中断处理顶半部*/ 
irqreturn_t xxx_interrupt(int irq, void *dev_id) 
{ 
  ... 
  tasklet_schedule(&xxx_tasklet); 
  ... 
} 

/*设备驱动模块加载函数*/ 
int _ _init xxx_init(void) 
{ 
  ... 
  /*申请中断*/ 
  result = request_irq(xxx_irq, xxx_interrupt, 
    IRQF_DISABLED, "xxx", NULL); 
  ...   
  return IRQ_HANDLED; 
} 

/*设备驱动模块卸载函数*/ 
void _ _exit xxx_exit(void) 
{ 
  ... 
  /*释放中断*/ 
  free_irq(xxx_irq, xxx_interrupt); 
  ... 
} 

5、使用工作队列作为底半部处理中断的设备驱动程序

/*中断处理底半部*/ 
 void xxx_do_work(unsigned long) 
 { 
   ... 
 } 

 /*中断处理顶半部*/ 
 irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) 
 { 
   ... 
   schedule_work(&xxx_wq); 
   ... 
   return IRQ_HANDLED; 
 } 

 /*设备驱动模块加载函数*/ 
 int xxx_init(void) 
 { 
   ... 
   /*申请中断*/ 
   result = request_irq(xxx_irq, xxx_interrupt, 
      IRQF_DISABLED, "xxx", NULL); 
  ... 
  /*初始化工作队列*/ 
   INIT_WORK(&xxx_wq, (void (*)(void *)) xxx_do_work, NULL); 
  ... 
 } 
 /*设备驱动模块卸载函数*/ 
 void xxx_exit(void) 
 { 
   ... 
   /*释放中断*/ 
   free_irq(xxx_irq, xxx_interrupt); 
   ... 
 } 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!