- 论坛徽章:
- 0
|
include/linux 中的interrupt.h中包含了与中断相关的大部分宏及struct结构的定义,以后有时间再一一说明
在2.6.12版的linux kernel中已经没有bottom half 和task
queue这两种机制,而是softirq,tasklet和work queue
,softirq主要是通过在每次中断返回是执行do_softirq这个函数完成,函数的主要部分是一个循环:
do {
if (pending & 1) {
h->action(h);
rcu_bh_qsctr_inc(cpu);
}
h++;
pending >>= 1;
} while (pending);
在最坏的情况下,这个循环会被执行10次,如果此时pending还是不为0,也就是说还有软中断没有处理,就唤醒一个内核线程去处理剩下的,这个线程叫
softirqd,每个cpu会有一个,所以如果执行ps
aux这条命令,会在某一行看到[softirqd/0],这个就是0号cpu的软中断守候线程。
tasklet实际上是两个软中断,为什么是两个呢?为了处理不同优先级的事情。这两个软中断一个的优先级高点,另一个的低一点。目前linux内核中已经用了6个软中断,这个在中有定义:
enum
{
HI_SOFTIRQ=0, 优先级高点的tasklet
TIMER_SOFTIRQ, 定时器
NET_TX_SOFTIRQ, 发送网络数据
NET_RX_SOFTIRQ, 接收网络数据
SCSI_SOFTIRQ, SCSI设备的软中断
TASKLET_SOFTIRQ 优先级低的tasklet
};
这些数实际上是一个数,就是数组softirq_vec[32]的下标。
工作队列我不是很熟悉,这里就不说了,现在来对interrupt.h这个文件做一下注释吧
以下内容全部是从interrupt.h中拷贝出来的,并按原来的顺序排列,出于篇幅的考虑,减去了一些内容,以下的说明都是我认为很重要或者不好理解的部分。
typedef int irqreturn_t;
这个量及下面的三个宏都是为了可移植性,是中断处理函数的返回值,代码的作者有很详细的说明,可以直接看文件中的注释
#define IRQ_NONE (0)
#define IRQ_HANDLED (1)
#define IRQ_RETVAL(x) ((x) != 0)
struct irqaction {
irqreturn_t (*handler)(int, void *, struct pt_regs *);
unsigned long flags;
cpumask_t mask;
const char *name;
void *dev_id;
struct irqaction *next;
int irq;
struct proc_dir_entry *dir;
};
这个结构就是中断处理程序很重要的结构了,这个就是在handle_IRQ_event(); 中处理的主要结构,用于执行真正的中断处理函数,
结构中handler是中断处理函数,你通过request_irq()
注册的函数,其实request_irq()传进来的参数就是用来填这个结构的,这个结构是一个单向链表,handle_IRQ_event()处理时是
遍历这个表
extern irqreturn_t no_action(int cpl, void *dev_id, struct pt_regs *regs);
这个是一个空函数,在kernel/irq/handle.c中,只有一句:return IRQ_NONE;
extern int request_irq(unsigned int,
irqreturn_t (*handler)(int, void
*, struct pt_regs *),
unsigned long, const char *, void *);
这个就是注册中断处理程序的函数,《linux kernel development》中有说明。
extern void free_irq(unsigned int, void *);
释放中断处理程序的函数,同上。
#ifdef CONFIG_GENERIC_HARDIRQS
extern void disable_irq_nosync(unsigned int irq);
extern void disable_irq(unsigned int irq);
extern void enable_irq(unsigned int irq);
#endif
这几个我还不知道是干什么的。
/*
* Temporary defines for UP kernels, until all code gets fixed.
*/
#ifndef CONFIG_SMP
一直到下面的#endif 都是开关cpu中断的指令的包装,实际上就是cli和sti和一些push和pop指令,这些在目录下用grep一下就出来了。
static inline void __deprecated cli(void)
{
local_irq_disable();
}
static inline void __deprecated sti(void)
{
local_irq_enable();
}
static inline void __deprecated save_flags(unsigned long *x)
{
local_save_flags(*x);
}
#define save_flags(x) save_flags(&x);
static inline void __deprecated restore_flags(unsigned long x)
{
local_irq_restore(x);
}
static inline void __deprecated save_and_cli(unsigned long *x)
{
local_irq_save(*x);
}
#define save_and_cli(x) save_and_cli(&x)
#endif /* CONFIG_SMP */
/* SoftIRQ primitives. */
#define local_bh_disable() \
do { add_preempt_count(SOFTIRQ_OFFSET); barrier(); } while (0)
#define __local_bh_enable() \
do { barrier(); sub_preempt_count(SOFTIRQ_OFFSET); } while (0)
extern void local_bh_enable(void);
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
SCSI_SOFTIRQ,
TASKLET_SOFTIRQ
};
这个就是前面提到的已经使用的软中断
/* softirq mask and active fields moved to irq_cpustat_t in
* asm/hardirq.h to get better cache usage. KAO
*/
struct softirq_action
{
void (*action)(struct softirq_action *);
void *data;
};
这个就是软中断的结构,和硬中断的irqcation差不多的
asmlinkage void do_softirq(void);
这个不用说明,软中断的入口,这个函数在irq_exit()中被调用,如果有软中断要处理的话。
extern void open_softirq(int nr, void (*action)(struct
softirq_action*), void *data);
这个函数有于注册一个软中断,和request_irq()差不多。
extern void softirq_init(void);
#define __raise_softirq_irqoff(nr) do { local_softirq_pending() |= 1UL state);
}
static inline void tasklet_unlock(struct tasklet_struct *t)
{
smp_mb__before_clear_bit();
clear_bit(TASKLET_STATE_RUN, &(t)->state);
}
static inline void tasklet_unlock_wait(struct tasklet_struct *t)
{
while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
}
#else
#define tasklet_trylock(t) 1
#define tasklet_unlock_wait(t) do { } while (0)
#define tasklet_unlock(t) do { } while (0)
#endif
extern void FASTCALL(__tasklet_schedule(struct tasklet_struct *t));
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
extern void FASTCALL(__tasklet_hi_schedule(struct tasklet_struct *t));
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_hi_schedule(t);
}
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
atomic_inc(&t->count);
smp_mb__after_atomic_inc();
}
static inline void tasklet_disable(struct tasklet_struct *t)
{
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic_dec();
atomic_dec(&t->count);
}
static inline void tasklet_hi_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic_dec();
atomic_dec(&t->count);
}
extern void tasklet_kill(struct tasklet_struct *t);
extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu);
extern void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data);
/*
* Autoprobing for irqs:
*
* probe_irq_on() and probe_irq_off() provide robust primitives
* for accurate IRQ probing during kernel initialization. They are
* reasonably simple to use, are not "fooled" by spurious interrupts,
* and, unlike other attempts at IRQ probing, they do not get hung on
* stuck interrupts (such as unused PS2 mouse interfaces on ASUS boards).
*
* For reasonably foolproof probing, use them as follows:
*
* 1. clear and/or mask the device's internal interrupt.
* 2. sti();
* 3. irqs = probe_irq_on(); // "take over" all unassigned idle IRQs
* 4. enable the device and cause it to trigger an interrupt.
* 5. wait for the device to interrupt, using non-intrusive polling or a delay.
* 6. irq = probe_irq_off(irqs); // get IRQ number, 0=none, negative=multiple
* 7. service the device to clear its pending interrupt.
* 8. loop again if paranoia is required.
*
* probe_irq_on() returns a mask of allocated irq's.
*
* probe_irq_off() takes the mask as a parameter,
* and returns the irq number which occurred,
* or zero if none occurred, or a negative irq number
* if more than one irq occurred.
*/
#if defined(CONFIG_GENERIC_HARDIRQS) && !defined(CONFIG_GENERIC_IRQ_PROBE)
static inline unsigned long probe_irq_on(void)
{
return 0;
}
static inline int probe_irq_off(unsigned long val)
{
return 0;
}
static inline unsigned int probe_irq_mask(unsigned long val)
{
return 0;
}
#else
extern unsigned long probe_irq_on(void); /* returns 0 on failure */
extern int probe_irq_off(unsigned long); /* returns 0 or negative on failure */
extern unsigned int probe_irq_mask(unsigned long); /* returns mask of ISA interrupts */
#endif
#endif
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/16553/showart_99324.html |
|