linux 内核中断处理,Linux内核中断处理流程

1. 对异常概念的理解

异常就是可以打断CPU正常运行的事件,比如,外部中断、未定义的指令、软中断等。当这些异常发生时,就打断CPU的正常运行,跳到相应的异常处理程序去处理这些异常要求的一些操作。

2. Linux内核中断处理流程

基于Linux-2.6.29.4

1、Linux内核中异常向量表的拷贝

内核启动时将异常向量表从物理地址0x00000000拷贝到0xffff0000的虚拟地址中去;

其中异常向量在arch/arm/kernel/entry-armv.S中定义

中断向量表

.globl __vectors_start

__vectors_start:

swi SYS_ERROR0

b vector_und+ stubs_offset

ldr pc,.LCvswi + stubs_offset

b vector_pabt+ stubs_offset

b vector_dabt+ stubs_offset

b vector_addrexcptn+ stubs_offset

b vector_irq+ stubs_offset

b vector_fiq+ stubs_offset

.globl __vectors_end

__vectors_end:

注:内核启动时调用start_kernel()函数,在start_kernel()中又调用了大量的函数。

asmlinkagevoid __init

start_kernel(void)

{

.....................................

setup_arch(&command_line);

mm_init_owner(&init_mm,&init_task);

setup_command_line(command_line);

.....................

sched_init();

.....................

init_IRQ();

rest_init();

}

函数setup_arch()在arch/arm/kernel/setup.c中

void__init

setup_arch(char **cmdline_p)

{

.........................

memcpy(boot_command_line,from,

COMMAND_LINE_SIZE);

boot_command_line[COMMAND_LINE_SIZE-1]=

'\0';

parse_cmdline(cmdline_p,from);

paging_init(mdesc);

request_standard_resources(&meminfo,mdesc);

.............................

...............................

early_trap_init();

}

void__init

early_trap_init(void)

{

........................................

memcpy((void*)vectors,

__vectors_start, __vectors_end -

__vectors_start);

memcpy((void*)vectors +

0x200, __stubs_start, __stubs_end -

__stubs_start);

memcpy((void*)vectors +

0x1000 - kuser_sz, __kuser_helper_start,

kuser_sz);

...............................

}

2、Linux内核中中断处理流程

首先,内核启动时完成中断初始化框架的流程

在start_kernel()中调用init_IRQ(),该函数在arch/arm/kernel/irq.c中被定义,被用来初始化中断的处理框架,设置各种中断的默认处理函数。当发生中断时,中断总入口函数asm_do_IRQ()就可以调用这些函数作进一步处理。

void__init

init_IRQ(void)

{

intirq;

for(irq =

0; irq < NR_IRQS; irq++)

irq_desc[irq].status|=

IRQ_NOREQUEST | IRQ_NOPROBE;

#ifdefCONFIG_SMP

bad_irq_desc.affinity=

CPU_MASK_ALL;

bad_irq_desc.cpu=

smp_processor_id();

#endif

init_arch_irq();

}

其中init_arch_irq()被定义为

void(*init_arch_irq)(void)

__initdata = NULL;

它的作用如下,

void__init

setup_arch(char **cmdline_p)

{

structmachine_desc *mdesc;

。。。。。。。。。。

setup_processor();

mdesc=

setup_machine(machine_arch_type);

machine_name=

mdesc->name;

memcpy(boot_command_line,from,

COMMAND_LINE_SIZE);

boot_command_line[COMMAND_LINE_SIZE-1]=

'\0';

parse_cmdline(cmdline_p,from);

paging_init(mdesc);

request_standard_resources(&meminfo,mdesc);

cpu_init();

init_arch_irq=

mdesc->init_irq;

system_timer=

mdesc->timer;

init_machine=

mdesc->init_machine;

early_trap_init();

}

structmachine_desc{

unsignedint

nr;

unsignedint

phys_io;

unsignedint

io_pg_offst;

constchar

*name;

unsignedlong

boot_params;

unsignedint

video_start;

unsignedint

video_end;

unsignedint

reserve_lp0 :1;

unsignedint

reserve_lp1 :1;

unsignedint

reserve_lp2 :1;

unsignedint

soft_reboot :1;

void

(*fixup)(structmachine_desc *,

struct tag

*, char **,

struct

meminfo *);

void

(*map_io)(void);

void (*init_irq)(void);

structsys_timer

*timer;

void

(*init_machine)(void);

};

static

structmachine_desc * __init setup_machine(unsignedint

nr)

{

structmachine_desc

*list;

list=

lookup_machine_type(nr);

if(!list)

{

printk("Machineconfiguration

botched (nr %d), unable "

"to

continue.\n", nr);

while(1);

}

printk("Machine:%s\n",

list->name);

returnlist;

}

ENTRY(lookup_machine_type)

stmfd

sp!,{r4 - r6, lr}

mov

r1,r0

bl

__lookup_machine_type

mov

r0,r5

ldmfd

sp!,{r4 - r6, pc}

ENDPROC(lookup_machine_type)

__lookup_machine_type:

adr

r3,3b

ldmia

r3,{r4, r5, r6}

sub r3,r3,

r4 @ get offset between virt&phys

add r5,r5,

r3 @ convert virt addresses to

add r6,r6,

r3 @ physical address space

1: ldr

r3,[r5, #MACHINFO_TYPE] @ get machine type

teq r3,r1 @

matches loader number?

beq 2f

@found

add r5,r5,

#SIZEOF_MACHINE_DESC @ next machine_desc

cmp

r5,r6

blo

1b

mov r5,#0 @

unknown machine

2: mov

pc,lr

ENDPROC(__lookup_machine_type)

接下来是内核完成启动后响应中断的流程

当CPU被中断时,强制跳转到异常中断向量表中的

b vector_fiq+ stubs_offset

如vector_irq这些是宏,定义在vector_stub

irq,IRQ_MODE, 4

vector_stubirq,IRQ_MODE,

4

这两句对应

.macrovector_stub,irq,IRQ_MODE,correction=4

.align 5

vector_irq:

.if4

sub lr,lr, #4计算返回地址

.endif

@

@Save r0, lr_ (parent PC) and spsr_

@(parent CPSR)

@保存被中断时CPU的现场

stmia sp,{r0, lr} @ save r0, lr

mrs lr,spsr

str lr,[sp, #8] @ save spsr

@

@Prepare for SVC32 mode. IRQs remain disabled.

@进入系统模式

mrs r0,cpsr

eor r0,r0, #(\mode ^ SVC_MODE)

msr spsr_cxsf,r0

@

@the branch table must immediately follow this code

@

and lr,lr, #0x0f

mov r0,sp

ldr lr,[pc, lr, lsl #2]

movs pc,lr @ branch to handler in SVC mode继续执行跳转

ENDPROC(vector_irq)

.endm

执行完上述语句后,跳转到

vector_stub

irq,IRQ_MODE, 4

.long

__irq_usr @ 0 (USR_26 / USR_32)

如果是用户态发生中断时,跳转到__irq_usr地址去执行

.long

__irq_invalid @ 1 (FIQ_26 / FIQ_32)

.long

__irq_invalid @ 2 (IRQ_26 / IRQ_32)

.long

__irq_svc @ 3 (SVC_26 / SVC_32)

.long

__irq_invalid @ 4

.long

__irq_invalid @ 5

.long

__irq_invalid @ 6

.long

__irq_invalid @ 7

.long

__irq_invalid @ 8

.long

__irq_invalid @ 9

.long

__irq_invalid @ a

.long

__irq_invalid @ b

.long

__irq_invalid @ c

.long

__irq_invalid @ d

.long

__irq_invalid @ e

.long

__irq_invalid @ f

当用户态发生中断是跳到__irq_usr

__irq_usr:

usr_entry

kuser_cmpxchg_check

#ifdefCONFIG_TRACE_IRQFLAGS

bl

trace_hardirqs_off

#endif

get_thread_infotsk

#ifdefCONFIG_PREEMPT

ldr

r8,[tsk, #TI_PREEMPT] @ get preempt count

add r7,r8,

#1 @ increment it

str

r7,[tsk, #TI_PREEMPT]

#endif

.macro

irq_handler

get_irqnr_preambler5,

lr

1:

get_irqnr_and_baser0, r6, r5, lr

movne

r1,sp

@

@routine

called with r0 = irq number, r1 = struct pt_regs

*

@

adrne

lr,1b

bne asm_do_IRQ

***位于irq.c

asmlinkagevoid

__exception asm_do_IRQ(unsigned int irq, struct pt_regs

*regs)

{

structpt_regs *old_regs

= set_irq_regs(regs);

irq_enter();

if(irq

>= NR_IRQS)

handle_bad_irq(irq,&bad_irq_desc);

else

generic_handle_irq(irq);

irq_finish(irq);

irq_exit();

set_irq_regs(old_regs);

}

irq_desc定义在include/linux/irq.h中

structirq_desc{

unsignedint

irq;

#ifdefCONFIG_SPARSE_IRQ

structtimer_rand_state

*timer_rand_state;

unsignedint

*kstat_irqs;

#ifdef

CONFIG_INTR_REMAP

structirq_2_iommu

*irq_2_iommu;

#endif

#endif

irq_flow_handler_t

handle_irq;

structirq_chip

*chip;

structmsi_desc

*msi_desc;

void

*handler_data;

void

*chip_data;

structirqaction

*action;

unsignedint

status;

unsignedint

depth;

unsignedint

wake_depth;

unsignedint

irq_count;

unsignedlong

last_unhandled;

unsignedint

irqs_unhandled;

spinlock_t

lock;

#ifdefCONFIG_SMP

cpumask_t

affinity;

unsignedint

cpu;

#endif

#ifdefCONFIG_GENERIC_PENDING_IRQ

cpumask_t

pending_mask;

#endif

#ifdefCONFIG_PROC_FS

structproc_dir_entry

*dir;

#endif

constchar

*name;

}

定义在include/linux/irq.h

staticinline void

generic_handle_irq(unsigned int irq)

{

generic_handle_irq_desc(irq,irq_to_desc(irq));

}

staticinline void

generic_handle_irq_desc(unsigned int irq, struct

irq_desc*desc)

{

#ifdefCONFIG_GENERIC_HARDIRQS_NO__DO_IRQ

desc->handle_irq(irq,desc);

#else

if(likely(desc->handle_irq))

desc->handle_irq(irq,desc);

else

__do_IRQ(irq);

#endif

}

最终处理的函数为handle_irq,为用户自定义。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值