Linux学习——Linux的异常处理

本文深入解析Linux内核在ARM架构下的异常向量基址配置及中断处理流程,包括start_kernel函数调用、异常向量表构造、vector_irq宏实现、中断处理C函数入口及具体处理函数调用等关键步骤。通过分析,揭示了Linux内核如何高效管理和响应中断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在Linux内核的配置文件.config文件中可以知道Linux内核使用ARM架构CPU的异常向量基址为0xFFFF0000。

1.内核对中断的处理流程

arch/arm/kernel/entry-armv.S

__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

异常向量表,无非还是一些跳转指令。当发生irq中断,执行指令“b vector_irq + stubs_offset”,也就是跳转到vector_irq代码段继续执行。

阅读该文件时,注意到每一个异常处理时开头都有一段相似的代码,比如:

vector_stub	irq, IRQ_MODE, 4
vector_stub	dabt, ABT_MODE, 8
vector_stub	pabt, ABT_MODE, 4

vector_stub是一段宏,展开来则是:

	.macro	vector_stub, name, mode, correction=0
	.align	5

vector_\name:
	.if \correction
	sub	lr, lr, #\correction @计算返回地址
	.endif

	@ 保存现场
	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
	@ (parent CPSR)
	@
	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
	.endm

跳转表(以中断为例):

vector_stub	irq, IRQ_MODE, 4

	.long	__irq_usr			@  0  (USR_26 / USR_32)
	.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

总结: 在linux内核初始化阶段,start_kernel函数(init/main.c)会调用trap_init、init_IRQ两个函数来初始化异常向量相关处理函数。并构造异常向量表__vectors_start,当发生中断时跳转至vector_irp(用宏来实现),在里面实现保存现场、计算返回地址、转换至管理模式、调用处理函数:以用户模式下的中断为例

__irq_usr:
    usr_entry             @将usr模式下的寄存器、中断返回地址保存到堆栈中    //保存现场

    get_thread_info tsk  @获取当前进程的进程描述符中的成员变量thread_info的地址,并将该地址保存到寄存器tsk等于r9

    irq_handler          @中断处理    //宏

    mov    why, #0       
    b    ret_to_user     @中断处理完成,返回中断产生的位置

irq_handler是一个宏,展开来是:

.macro    irq_handler
  get_irqnr_preamble r5, lr
1:    get_irqnr_and_base r0, r6, r5, lr
    movne    r1, sp
    adrne    lr, 1b
    bne    asm_do_IRQ
   .endm

进入asm_do_IRQ函数开始具体的中断处理。需要指出的是,asm_do_IRQ是中断的C语言总入口函数。

2.asm_do_IRQ函数

smlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

开始执行之后,

struct irq_desc *desc = irq_desc + irq;

从名字上可以知道,irq_desc是一个中断描述数组,以中断号为下标,

之后进入处理函数:

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

该函数所做的是分辨是哪一个中断、调用对应的处理函数、清除中断标志位

搜索handle_irq,发现handle_irq被__set_irq_handler调用过
搜索__set_irq_handler,发现__set_irq_handler被set_irq_handler调用过
搜索set_irq_handler,发现__set_irq_handler被s3c24xx_init_irq(void)调用过
 

中断处理 C 函数入口 “asm_do_IRQ”会调用到事先初始化好的“handle_irq”函数。

handle_irq == handle_edge_irq,将执行:

①desc->chip->ack(irq):清除中断

②handle_IRQ_event:取出action链表中的成员,执行action->handler

3.request_irq( )函数

int request_irq(unsigned int irq, irq_handler_t handler,
		unsigned long irqflags, const char *devname, void *dev_id)

用于注册用户编写的中断处理函数,里面将执行:

①分配irqaction结构

②setup_irq(irq,action):

       在irq_desc(irq)->action链表里加入传入的irq、action,

       在desc->chip->settype里将引脚设置为中断引脚

       在desc->chip->sturtup/enable里使能中断

4.free_irq( )函数

void free_irq(unsigned int irq, void *dev_id)

用于卸载用户编写的中断服务程序。会执行出链、除能中断

写完驱动代码之后,使用命令exec /5</dev/buttons打开中断驱动即可,就是打开这个设备,定位到5处

想关闭则使用命令exec 5<&-

5.休眠与唤醒

使用休眠功能,可以将程序的CPU占有率降低至2%,大大提高了性能。

在中断服务函数里面:
ev_press = 1;       /* 有按键按下 */
wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

在驱动的read函数里面:
wait_event_interruptible(button_waitq, ev_press);

/* 返回数据给内核 */
copy_to_user(buf, &key_val, sizeof(key_val));
ev_press = 0;

按键按下后,进入中断服务函数执行一系列操作并唤醒休眠的进程,执行完毕之后程序将从休眠处继续执行。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值