Linux驱动开发---中断和定时器

本文概述了Linux中断处理中的向量中断与非向量中断,重点介绍了底半部(tasklet)和工作队列的工作原理,以及如何通过request_irq与free_irq进行中断申请与释放。同时涵盖了软中断的使用,以及中断共享和内核定时器、延迟的实践技巧。
摘要由CSDN通过智能技术生成

中断

分类

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

顶半部(top half)/底半部(bottom half)

top half: 读取中断状态、清除中断标志后作“登记中断”的工作;—便于服务更多的中断
bottom half:完成中断处理的大部分工作;一般比较耗时。
Note:一个中断处理不一定都要分为两部分。如果top half本身能够完成整个中断处理,那bottom half也就不需要了。

Linux中断编程

申请/释放中断:request_irq/free_irq

屏蔽/使能中断:

屏蔽/使能某中断源
disable_irq/disable_irq_nosync;
enable_irq

屏蔽/恢复本cpu内所有中断
local_irq_save/local_irq_disable;
local_irq_restore/local_irq_enable

底半部机制

  1. tasklet
  • 定义底半部处理函数my_tasklet_func(),并利用DECLARE_TASKLET将my_tasklet_func()与my_tasklet_func()绑定起来: DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);
  • 在驱动加载函数中申请中断(request_irq),该函数调用中断顶半部处理函数xxx_interrupt;
  • 在顶半部处理函数中,调用tasklet_schedule(&my_task),则会调用底半部处理函数my_tasklet_func();
  • 在驱动卸载函数中释放中断(free_irq)
  1. 工作队列
  • 定义一个工作队列和底半部处理函数;
    struct work_struct my_wq;
    void my_wq_func(unsigned long);
  • 在驱动加载函数中申请中断并初始化工作队列;
    request_irq; / * 调用中断顶半部处理函数xxx_interrupt * /
    INIT_WORK(&my_wq, (void (*)(void *)) my_wq_func, NULL);
  • 在顶半部处理函数中,调用schedule_work(&my_wq),最终调用底半部处理函数my_wq_func;
  • 在驱动卸载函数中释放中断(free_irq)
  1. 软中断
  • local_bh_enable()/local_bh_disable(); 使能/禁止软中断
  • 软中断/tasklet运行于中断上下文,不能睡眠;工作队列运行于进程上下文,则允许睡眠。

中断共享

  • 多个设备共享一条硬件中断线,如PCI设备
  • 申请中断request_irq时均使用SA_SHIRQ 标志,且有最后一个参数dev_id。
  • 在中断到来后,在top half中应尽快根据dev_id判断是否是该设备的中断,若不是应迅速返回(return IRQ_NONE;)

内核定时器编程

利用timer_list结构体和一组函数实现。

struct timer_list { 
	struct list_head entry;                           //定时器列表
	unsigned long expires;                           //定时器到期时间
	void (*function)(unsigned long);    		//定时器处理函数
	unsigned long data;                            //作为参数被传入定时器处理函数
	struct timer_base_s *base;
};
  1. 定义一个定时器:struct time_list my_timer;
  2. 初始化定时器
    2.1 Init_timer(&my_timer); //初始化timer_list中的entry的next为NULL,并给base赋值。
    TIMER_INITIALIZER(_function, _expires, _data); //初始化timer_list中的function、expires、data 和base成员。
    2.2 另外,也可调用setup_timer(struct timer_list * timer, void (*function)(unsigned long), unsigned long data) //初始化定时器并赋值其成员。
  3. 注册内核定时器:add_timer(&my_timer);
  4. 删除定时器:del_timer(&my_timer);
  5. 修改定时器:mod_timer(struct timer_list *timer, unsigned long expires);

内核延迟

忙等待

  1. 短延迟
    1.1 ndelay(); //纳秒延迟
    1.2 udelay(); //微秒延迟
    1.3 mdelay(); //毫秒延迟,最好不要直接使用(会无谓的耗费CPU资源
    1.4 毫秒及以上的延迟:推荐用睡眠延迟

  2. 长延迟:time_before(jiffies, delay);

睡眠等待

  1. msleep();
  2. msleep_interruptible();
  3. ssleep();

**NOTE:**为了充分利用CPU资源,使系统有更好的吞吐性能,在对延迟时间的要求并不是很精确的情况下,睡眠等待通常是值得推荐的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值