10 中断与时钟
主要讲解中断与定时器的处理,其中涉及到了顶半部和底半部的机制。
按照中断入口跳转方法的不同,分为向量中断和非向量中断,采用向量中断,不同的中断分配不同的中断号对应不同的入口地址。而非向量中断,多个中断共享一个入口地址,然后在通过软件判断出具体是哪个中断。
可编程中断控制器(PIC),通过读写PIC寄存器,程序员可以屏蔽或者使能某中断及获得中断状态。
Linux将中断处理程序分为两个半部,顶半部和底半部。顶半部完成尽可能少的比较紧急的功能,简单的读取寄存器中的中断状态并且清除中断标志后就进行“登记中断”的工作,登记中断意味着将底半部处理程序挂到该设备的底半部执行队列中去。
申请中断和释放中断:request_irq(),一般dev_id为设备结构体,而如果共享的时候,要注意标志IRQF_SHARED
释放IRQ:free_irq(unsigned int irq, void * dev_id)
使能和屏蔽中断 disable_irq() disable_irq_nosync() enable_irq() enable_irq() local_irq_disable() local_irq_enable()
底半部机制:
tasklet
my_tasklet_func() 定义一个处理函数 DECLARE_TASKLET(my_tasklet,my_tasklet_func,data)定义一个my_tasklet结构,并且与my_tasklet_func(data)函数相连接
最后通过一个tasklet_schedule()在适当的时候调度
工作队列
struct work_struct my_wq;定义一个工作队列 void my_wq_func()定义一个处理函数
INIT_WORK(&my_wq,my_wq_func,NULL)初始化工作队列并将其与处理函数绑定
然后也是一个schedule_work(&my_wq)
软中断
它也是一种传统的底半部处理机制。local_bh_disable()和local_bh_enable()是用于禁止和使能软中断和tasklet底半部机制的函数。
硬中断时外部设备对CPU的中断。软中断是中断底半部的一种处理机制。信号则是内核(或者其他进程)对某个进程的中断。
中断共享:当中断到来时,会遍历执行共享此中断的所有中断处理程序,直到某个函数返回IRQ_HANDLED,在顶半部程序,根据硬件寄存器中的信息比照传入的dev_id是否是本设备的中断。
内核定时器:
Linux设备驱动编程中,可以利用Linux内核中提供的一组函数和数据结构来完成定时触发工作或者完成某周期性的事务。
timer_list:在timer_list结构体的一个实例对应着一个定时器——P203
其中定时器期满后,function()成员将被执行,data成员则是传入其中的参数,expires则是定时器到期的时间。
初始化定时器
init_timer() TIMER_INITIALIZER()宏用于赋值上面的结构体 TIMER_TIMER() setup_timer()
增加定时器 add_timer()
删除定时器 del_timer() del_timer_sync()同步版
修改定时器的时间 mod_timer(struct timer_list * timer, unsigned long expires)
对于周期性的延迟工作可以使用delayed_work,可以通过调度一个delayed_work在指定的延时后执行。schedule_delayed_word(struct delayed_work * work,unsigned long delay) 当时间到达时,会执行delayed_work结构体中work成员的work_func_t类型成员func()。
取消delayed_work cancel_delayed_work (struct delayed_work * work) cancel_delayed_work_sync (struct delayed_work * work)
内核延时
短延迟 ndelay(unsigned long nsecs) udelay() mdelay() 这些是忙等待
长延时 timer_before(jiffies,delay) timer_after()
睡着延迟
比忙等待更好的方式,处于睡眠状态,CPU资源被其他进程使用。 msleep() msleep_interruptible() 他们都是依靠schedule_timeout()的schedule_timeout_uninterruptible()和schedule_timeout_interruptible()实现的。