歡迎您光臨本站 註冊首頁

include/linux中的interrupt.h

←手機掃碼閱讀     火星人 @ 2014-03-26 , reply: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 << (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

[火星人 ] include/linux中的interrupt.h已經有832次圍觀

http://coctec.com/docs/linux/show-post-189624.html