因此引入了顶半部和底半部机制,顶半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态并清除中断标志后就进行”登记中断“的工作。登记中断意味着将底半部处理程序挂到该设备的底半部执行队列中区。底半部可以被其他新的中断打断。
另外内核对时钟的处理也采用中断方式。
根据中断入口跳转方法的不同,中断分为向量中断和非向量中断。向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务程序入口地址。
申请中断 request_irq();
释放中断 free_irq();
屏蔽一个中断 disable_irq(); disable_irq_nosync();
使能中断 enable_irq
屏蔽本cpu内的所有中断 local_irq_save(); local_irq_disable();
与上面对应的 local_irq_restore(); local_irq_enable();
linux系统实现底半部的机制主要有tasklet, 工作队列,软中断。
tasklet 用DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);将名为my_tasklet的tasklet与my_tasklet_func()函数绑定,传入的参数为data.
顶半部调度底半部 tasklet_schedule(&my_tasklet);
工作队列
定义一个工作队列 work_struct
INIT_WORK()可以初始化工作队列并将工作队列与处理函数绑定。
schedule_work()调度工作队列。
专家建议在设备第一次打开时菜申请设备中断,并在最后一次关闭时释放中断,尽量减少中断被这个设备占用的时间,但大多数情况下,工程师还是会把中断请求和释放的工作放在设备驱动的模块加载和卸载函数中。
软中断和硬中断,信号的区别:
硬中断是外部设备对CPU的中断;
软中断通常是硬中断服务程序对内核的中断;
信号是内核(或其他进程)对某个进程的中断。
内核中用softirq_action结构体表征一个软中断,open_softirq()可以注册软中断对应的处理函数,raise_softirq()可以触发一个软中断。
软中断和tasklet仍然运行于中断上下文,而工作队列则运行于进程上下文。因此工作队列处理函数可以睡眠,而另外两个则不允许。
local_bh_disable()和local_bh_enable()是内核中用于禁止和使能软中断和tasklet底半部机制的函数。
s3c2410实时钟(RTC)中断模块可以产生两种中断:周期节拍(tick)和报警(alarm).前者相当于一个周期性的定时器,后者相当于一个闹钟。
共享中断 SA_SHIRQ, 在中断处理顶半部用is_myint()判断是否为本设备中断。
内核提供的用于操作定时器的数据结构和函数如下:
timer_list结构体,一个实例对应一个结构体,其中有定时器处理函数,参数,定时器到期时间等成员
init_timer()用于初始化timer_list.
TIMER_INITIALIZER宏用于赋值定时器结构体的成员,DEFINE_TIMER宏用于定义并初始化定时器解耦固体成员。
setup_timer()也用于初始化定时器并赋值成员。
add_timer()想内核注册定时器。
del_timer()删除定时器。多处理器系统的同步版del_timer_sync()
mod_timer()修改定时器的expire.
定时器的到期时间往往是在目前的jiffies基础上加一个时延。在定时器处理函数中,在做完相应的工作后,往往会延后expires并将定时器再次添加到内核定时器链表,以便定时器能再次被触发。
用户可以在软件中进行如下延迟:
void delay(unsigned int time)
{
while (time--);
}
在内核中,最好不要直接调用mdelay()函数,这将无谓地耗费cpu资源,对于毫秒以上的时延,内核提供了以下函数:
msleep(), msleep_interruptible(), ssleep()
长延时
unsigned long delay = jiffies + 2*HZ;
while (time_before(jiffies, delay));
time_before(), time_after()将传入的未来时间jiffies和现在调用时的jiffies进行简单的比较。
有睡着延迟,使当前任务睡眠指定的jiffies之后重新被调度执行