概述
一 .中断基础
中断是除了系统调用之外的另一种可进行用户空间和内核空间切换的途径;
中断分为硬件中断(hardware interrupt)和软中断(softirq),下文中的中断是指硬件中断。
硬件中断:由系统本身和它所接外设产生的,用于实现更高效率的驱动程序,也可能是由处理器运行错误和异常的程序所导致。前者可称为异步中断,后者叫同步中断和异常。
软中断:当中断发生时,要求中断处理程序有尽快的响应速度,减少对其它中断或程序影响,linux所采用的一种延期操作技术。通常linux将中断处理函数分为2部分,上半部 和下半部,一些关键的操作,如些数据保存可放在上半部实现,而一些可以延期的不是特别重要的如数据处理可以放在下半部处理;
中断不能由处理器的外设直接产生,而必须亦一个叫中断控制器(interrupt controller)的组件来请求,该组件存在于每个系统中,这个请求的正确叫法叫IRQ,或中断请求(interrupt request);每一个中断都有一个编号,有些设备可以共享中断号,中断号是有限的,在使用过程中要注意。
当中断发生时,中断处理过程如图1所示;
图1 中断处理过程
如图1所示,中断处理可以分为三个部分,当中断来时先切换到内核栈,保存当前运行的应用程序的寄存器状态,以便中断处理完后恢复运行;接下来进入中断处理程序,最后恢复之前保存的寄存器,激活用户栈。
中断处理软件流程图(ARM)如图2所示
图2ARM处理器对应的中断处理流程图
set_irq_regs用来将用户空间程序的寄存器状态保存和恢复在栈空间;irq_enter用来负责更新一些统计 量,如果jiffies,generic_handle_irq是与体系结构无关的通用接口,它会调用IRQ对应的irq_desc[IRQ]->handle_irq来响应流控处理程序;在流控处理程序里会调用handler_irq_event通用接口并最终调用用户申请中断时确定的中断处理程序action->handler; irq_exit用来更新统计量并会调用do_softirq来处理等待的softirq,最后的set_irq_regs用来快栈空间恢复之前保存的信息。
在写中断处理程序时需要注意:
1.中断时异步执行的,禁止访问用户空间;
2.中断上下文中不能调用调度器 ;
3. 中断处理程序不能进入睡眠状态;
二 . 中断注册和释放
int request_irq(unsigned int irq, irqreturn_t handler, unsigned long irqflags, const char *devname, void *dev_id);
request_irq用于向系统申请中断,其中irq为中断号,handler为中断处理程序,中断处理程序返回值为IRQ_NONE或IRQ_HANDLED,irqflags为中断管理位掩码,devname为中断设备名,显示在/proc/interrupts里,dev_id为传递给中断处理程序的数据;
void free_irq(unsigned int irq, void *dev_id)
free_irq用来释放已经申请的中断,irq为中断号,当有多个设备共享中断号时,可能通过dev_id进行区分要释放中断;
三 .软中断
软中断 是指将内核中的某些任务进行延期执行行为;
1.软中断数据结构
struct softirq_action {
void (*action)(struct softirq_action *);
}
action是软中断发生时执行的程序;
2. 注册软中断
void open_softirq(int nr, struct softirq_action *)
通过open_softirq来向系统注册软中断,nr为软中断对应的唯一编号,在linux系统中的软中断个数是有限的,它通过一个数组softirq_vec[]来管理,系统中每个软中断都有一个对应的编号,编号对应数组的索引,如果要自己增加软中断,需要自己添加,而softirq_action为软中断处理程序。目前系统中有9个软中断,分别是:
enum {
HI_SOFTIRQ, tasklet
TIMER_SOFTIRQ, 低精度定时器
NET_TX_SOFTIRQ, 网络收发
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ, tasklet
SCHED_SOFTIRQ, 调度器
HRTIMER_SOFTIRQ, 高精度定时器
RCU_SOFTIRQ,
};
3.启动软中断
void raise_softirq(int nr)
raise_softirq用来启动软中断,nr为软中断对应的编号;除通过raise_softirq来启动软中断外,在中断处理程序中,为了能及时响应系统中的软中断,退出中断上下文函数irq_exit,除更新一些统计信息外,还会调用invoke_softirq函数,该函数会调用do_softirq来运行系统中在等待的软中断。raise_softirq和do_softirq程序流程图如图3、图4所示所示;
图3 raise_softirq流程图
图4 do_softirq流程图
从图3.4中看出不管是raise_softirq还是do_softirq,都会先用in_interrupt来进行判断当前所处的环境,如果处于硬件中断、软中断或不可屏蔽中断,则直接退出,否则运行软中断的处理程序action或调用wakeup_softirq唤醒软中断守护进程ksoftirqd,当软中断 的守护进程被唤醒后,会先查看是否有没有处理的软中断,如果有的话就会调用do_softifq来执行软中断,直到没有未处理的软中断为止。
in_interrupt用来判断当前是否处于硬件中断,软中断或非屏蔽中断,in_interrupt定义如下:
#define preempt_count() (current_thread_info()->preempt_count)
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK))
#define in_interrupt() (irq_count())
由上面宏定义可以发现in_interrupt最终通过获取当前线程描述符中的preempt_count变量来进行判断;preempt_count这个变量由多个部分构成,各部分代表意思如果下所示:
bit 0~7 preempt count 内核抢占计数器,为0表示可以内核抢占,大于0表示禁止抢占;preempt_disable增大其值,preempt_enable_no_resched 减小其值;
bit 8~15 softirq count 软中断计数器,__local_bh_disable增大其值,__local_bh_enable减小其值;
bit 16~25 hardirq count 硬中断计数器,irq_enter增大其值,irq_exit减小其值;
bit 26 nmi mask
bit 28 preempt_active flag
四. tasklet
tasklet是在softirq基础上实现 的一种延期执行工作的机制。在上面提到过目前系统中有两个用于tasklet的软中断,一个是HI_SOFTIRQ,另一个是TASKLET_SOFTIRQ.
1.tasklet数据结构
struct tasklet_struct {
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsgined long);
unsigned long data;
}
其中next为指向下一个tasklet_struct的指针,func为延期后运行的函数指针,data为前面函数运行所需要的数据,而count为原子变量,当count为0时,可以对tasklet进行调度。state为tasklet当前状态,一共有两种状态,注册到内核,等待调度执行,设置为TASKLET_STATE_SCHED;如果当前tasklet任务正在运行则设置成TASKLET_STATE_RUN。
2. tasklet初始化、注册及执行
void tasklet_init(struct tasklet_struct *t, void (*fucn)(unsigned long ),unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count,0);
t->func = func;
t->data = data;
}
tasklet_init用于初始化tasklet,t 为要初始化的tasklet,func为延期所要运行的函数,data为函数运行所需要的数据,初始化将count设置为0。
void tasklet_schedule(struct tasklet_struct *t)
tasklet_schedule用于向系统注册一个tasklet任务,在注册的时候首先会判断state的状态,如果为TASKLET_STATE_SCHED则表示已经注册过了,取消注册;否则将taskle添加到一个叫tasklet_vec的列表里,并调用raise_softirq_irqoff去设置softirq_pending,如果当前不是在硬中断,软中断和不可屏蔽中断运行状态下,则去唤醒软中断 的守护进程,执行tasklet软中断tasklet_action,最终运行tasklet_vec列表里的func延期函数。
void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
list = __this_cpu_read(tasklet_vec.head);
while(list){
struct tasklet_struct *t = list;
list = list->next;
......
if(tasklet_trylock(t){
if(!atomic_read(&t->count){
if (!test_and_clear_bit(TASKLET_STATE_SCHED,&t->state){
bug();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
}
t->next = NULL;
*__this_cpu_read(tasklet_vec.tail) = t;
__this_cpu_write(tasklet_vec.tail, &(t->next));
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
}
}
其中tasklet_trylock函数为:
int tasklet_trylock(struct tasklet_struct *t)
{
return ! test_and_set_bit(TASKLET_STATE_RUN,&t->state);
}
tasklet_action函数先检测并置位state变量,如果state本来就为TASKLET_STATE_RUN说明 已经在运行了,则获取列表中其它成员继续运行,否则去读取count的值,如果为0则运行tasklet的延期函数。 对于由于tasklet_trylock和atomic_read失败面未执行的tasklet,则重新加入到tasklet列表,重新去触发软中断。
对于另一个tasklet软中断HI_SOFTIRQ,只是存在优先级别上不同,HI_SOFTIRQ优先级别比较高,注册接口函数不同tasklet_hi_schedule,软中断延期函数名不同tasklet_hi_action,但里面的实现机制都是一样的。
最后
以上就是魁梧大白为你收集整理的中断及其延期执行技术一 .中断基础二 . 中断注册和释放三 .软中断四. tasklet的全部内容,希望文章能够帮你解决中断及其延期执行技术一 .中断基础二 . 中断注册和释放三 .软中断四. tasklet所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复