天外有天,人外有人。
分类: LINUX
2013-09-04 22:37:48
Arch/i386/kernel/traps.c中trap_init()中对系统保留的中断向量进行初始化(前面syscalls0x80在此初始化)。
Arch/i386/kernel/i8259.c中void __init init_IRQ(void)对外设中断.
void __init init_IRQ(void)
{
pre_intr_init_hook(); //调用init_ISA_irqs()从而初始化8259A和irq_desc[]。
for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
set_intr_gate(vector, interrupt[i]); //设置各中断服务入口地址interrupt[i]
}
}
在2.4内核中,interrupt[i]采用很复杂的宏定义来实现(具体的情景分析讲得很清楚),而在2.6中采用下面代码实现:
.data
ENTRY(interrupt)
.text
vector=0
ENTRY(irq_entries_start)
.rept NR_IRQS
ALIGN
1: pushl $vector-256 为何要减256?防止当有符号数处理出错. do_IRQ()中
//int irq = regs->orig_eax & 0xff;
jmp common_interrupt
.data
.long 1b //用于对齐4个字节
.text
vector=vector+1
.endr
中断处理程序是处于中断上下文中而不是进程上下文中。Current仍然指向被中断进程。以前中断共享内核栈,现在可通过设置配置项为使得个处理处理器都有一个大小为一页的中断栈。
int request_irq(…)
{
为action分配内存并初始化;
setup_irq(irq, action); //实现见后面
}
int setup_irq(unsigned int irq, struct irqaction * new)
{
struct irq_desc *desc = irq_desc + irq; // struct irqdesc irq_desc[NR_IRQS];中断描述数组
p = &desc->action;
if ((old = *p) != NULL) { //此中断号是否已注册有中断处理程序
/* Can't share interrupts unless both agree to */
if (!(old->flags & new->flags & SA_SHIRQ)) {
return -EBUSY;
}
/* add new interrupt at end of irq queue */
do {
p = &old->next;
old = *p;
} while (old);
shared = 1;
}
*p = new; //插入new到(irq_desc + irq)->action结尾
}
irq_flags取值:
1. 0
2. #define SA_INTERRUPT 0x20000000 //中断处理程序在屏蔽所有中断情况下执行,不加此标志则// 蔽相应的那条中断线
3. #define SA_SAMPLE_RANDOM SA_RESTART //中断间隔时间加入到随机数熵池中
4. #define SA_SHIRQ 0x04000000 //共享中断线
3.中断执行流程:
1. arch/i386/kernel/entry.S文件:
common_interrupt://前面中断向量表中
call do_IRQ
jmp ret_from_intr //检查need_resched标志
2. arch/i386/kernel/irq.c文件
fastcall unsigned int do_IRQ(struct pt_regs *regs)
{
irq_enter(); //为__do_IRQ做些准备工作,如:增加preempt_count等
__do_IRQ(irq, regs); //详见后面
irq_exit();//为__do_IRQ做些善后工作,如:对就前面减少preempt_count,调用__do_softirq();
}
fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs)
{
irq_desc_t *desc = irq_desc + irq;
if (desc->handler->ack)
desc->handler->ack(irq); //对收到中断进行应答,禁止这条线上的中断
action = desc->action;
action_ret = handle_IRQ_event(irq, regs, action); //详见后面
}
fastcall inthandle_IRQ_event(unsigned int irq, struct pt_regs *regs, struct irqaction *action)
{
if (!(action->flags & SA_INTERRUPT)) //
local_irq_enable();
do { //循环执行共享中断的每一个中断处理程序
ret = action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq); //加熵到熵池
}
中断处理程序中常用的宏:#define local_irq_save(x) __asm__ __volatile__("pushfl ; popl %0 ;cli":"=g" (x): /* no input */ :"memory")
#define local_irq_restore(x) do { typecheck(unsigned long,x); \
__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory", "cc"); } while (0)
为何没有sti ?这里没有显式执行开中断的动作。实际上,在标志寄存器中保持了原来的中断状态,在恢复寄存器的同时将中断也恢复到以前的状态。
#define local_irq_disable() __asm__ __volatile__("cli": : :"memory")
#define local_irq_enable() __asm__ __volatile__("sti": : :"memory")
中断下半部:
中断处理程序执行时将该线上中断关闭,甚至禁止所有本地中断,这样就可能因执行太久使用权中断丢失。所以将能推后执行的部分推后。
2.4内核中采用的下半部处理方式有: BH,task queue, softirq, tasklet
2.6中加入了work queue并去掉了BH和 task queue。
1.软中断:softirq在编译时静态分配,tasklet动态注册和删除.
Kernel/softirq.c中static struct softirq_action softirq_vec[32]限定了softirq注册数不超过32个。
struct softirq_action
{
void (*action)(struct softirq_action *);
void *data;
};
Softirq执行流程:一. asmlinkage void __initstart_kernel(void){ softirq_init(); } //注册HI_SOFTIRQ和TASKLET_IRQ软中断,作tasklet用.
二. do_IRQ()中间接调用__do_softirq();,详见上面do_IRQ()的注释,
三. ksoftirqd内核线程调用do_softirq() -à__do_softirq();.
四. 显示调用do_softirq()。
asmlinkage void __do_softirq(void)
{
pending = local_softirq_pending();
h = softirq_vec;
do {
if (pending & 1) //从0至32位逐个判断有没有挂起标志
h->action(h); //执行具体的softirq
h++;
pending >>= 1;
} while (pending);
}
Softirq应用:一. 分配索引:
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
SCSI_SOFTIRQ,
TASKLET_SOFTIRQ
};
二. 注册处理程序:
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
三. 触发softirq
void fastcall raise_softirq(unsigned int nr)
{
raise_softirq_irqoff(nr); //定义见下面
}
inline fastcall void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr); //定义见下面
}
#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0) //将相应pending位置位
2.Tasklet:
一.Tasklet最终也是通过softirq来实现。(从下面就可以看出)
static inline void tasklet_schedule(struct tasklet_struct *t) //调度tasklet
{
__tasklet_schedule(t);//见后面
}
void fastcall __tasklet_schedule(struct tasklet_struct *t){
raise_softirq_irqoff(TASKLET_SOFTIRQ);
}
具体与softirq关联:
void __init softirq_init(void) //在Main.c中的start_kernel()中调用
{
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); //注册tasklet的TASKLET_SOFTIRQ到softirq
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
static void tasklet_action(struct softirq_action *a)
{
while (list) {
list = list->next;
t->func(t->data); //执行具体的tasklet注册函数
continue;
}
}
工作队列
工作队列是将工作交由内核线程执行,因此它可以调用kmalloc等会引起休眠的函数。
struct workqueue_struct {
struct cpu_workqueue_struct *cpu_wq; //每个CPU一个工作者线程
const char *name; //本线程类名
};
struct cpu_workqueue_struct {
struct list_head worklist; //work_struct链表指针
};
struct work_struct {
void (*func)(void *); //具体处理函数
void *data; //参数
};
工作流程:
1. 创建work_struct实例. (对应一个具体的推后处理的函数)
#define DECLARE_WORK(n, f, d) struct work_struct n = __WORK_INITIALIZER(n, f, d)
//f 为function, d为参数
2. 调度运行.
int fastcall schedule_work(struct work_struct *work){
return queue_work(keventd_wq, work);
}
int fastcall queue_work(struct workqueue_struct *wq, struct work_struct *work){//挂工作到工作队列链表中去
__queue_work(per_cpu_ptr(wq->cpu_wq, cpu), work);
}
static void __queue_work(struct cpu_workqueue_struct *cwq, struct work_struct *work){
list_add_tail(&work->entry, &cwq->worklist); //对应run_workqueue中的list_del_init
}
start_kernel>>>>rest_init创建init线程>>>do_basic_setup>>>init_workqueues>>>create_workqueue(调用它创建新的工作线程类)>>>__create_workqueue对每个cpu调用一次create_workqueue_thread创建worker_thread线程
static int worker_thread(void *__cwq){ //类似于ksoftirqd线程
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
if (list_empty(&cwq->worklist))
schedule();
run_workqueue(cwq);
}
}
static inline void run_workqueue(struct cpu_workqueue_struct *cwq){
while (!list_empty(&cwq->worklist)) {
struct work_struct *work = list_entry(cwq->worklist.next, struct work_struct, entry);
void (*f) (void *) = work->func;
void *data = work->data;
list_del_init(cwq->worklist.next);
f(data);
}
}
参考的几张图:
Linux中断处理流程 *引用自《linux内核设计与实现》
*引自情景分析
*引自情景分析
门中段选择码*引自情景分析