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

本文深入解析Linux中断处理机制,介绍顶半部与底半部概念,对比服务端网络程序处理模型,阐述中断处理核心代码在底半部执行的原因。探讨软中断、tasklet和工作队列三种底半部实现机制,通过具体示例展示如何使用tasklet和工作队列处理设备驱动程序中断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了平衡中断处理程序时间要求短和工作量要大的问题,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); 
   ... 
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值