红联Linux门户
Linux帮助

include/linux中的interrupt.h

发布时间:2006-08-28 10:15:29来源:红联作者:syncode
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 << (nr); } while (0)
extern void FASTCALL(raise_softirq_irqoff(unsigned int nr));
extern void FASTCALL(raise_softirq(unsigned int nr));
这几个函数就是用于激活软中断,相当于硬中断中硬件产生了一个硬中断,实际上就是将typedef struct {
unsigned int __softirq_pending;
unsigned long idle_timestamp;
unsigned int __nmi_count; /* arch dependent */
unsigned int apic_timer_irqs; /* arch dependent */
} ____cacheline_aligned irq_cpustat_t;
这个结构中对应的__softirq_pending的某一位置位,是哪一位有nr决定,这个结构在asm-i386/hardirq.h中。硬中断处理程序返回是会检查 这个结构中的__softirq_pending看是否有软中断要处理,这样软中断处理程序就被激活了。

到这里softirq相关的部分就完了,下面的就是一个具体的软中断tasklet的相关结构及一些声明,和上面的作用理念差不多,对应的大部分函数的实现在kernel/softirq.c
文件太长了,今天就到这里吧,
/* Tasklets --- multithreaded analogue of BHs.

Main feature differing them of generic softirqs: tasklet
is running only on one CPU simultaneously.

Main feature differing them of BHs: different tasklets
may be run simultaneously on different CPUs.

Properties:
* If tasklet_schedule() is called, then tasklet is guaranteed
to be executed on some cpu at least once after this.
* If the tasklet is already scheduled, but its excecution is still not
started, it will be executed only once.
* If this tasklet is already running on another CPU (or schedule is called
from tasklet itself), it is rescheduled for later.
* Tasklet is strictly serialized wrt itself, but not
wrt another tasklets. If client needs some intertask synchronization,
he makes it with spinlocks.
*/

struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};

#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }


enum
{
TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */
TASKLET_STATE_RUN /* Tasklet is running (SMP only) */
};

#ifdef CONFIG_SMP
static inline int tasklet_trylock(struct tasklet_struct *t)
{
return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->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
文章评论

共有 0 条评论