linuxkernel中的中断irq的栈stackarm64体系的irq的栈
先看下irq_handler的中断函数处理的过程:
/*
* Interrupt handling.
*/
.macro irq_handler
ldr_l x1, handle_arch_irq -----将handle地址保存在x1
mov x0, sp
irq_stack_entry ------ 切换栈,也就是将svc栈切换程irq栈. 在此之前,SP还是EL1_SP,在此函数中,将EL1_SP保存,再将IRQ栈的地址写入到SP寄存器
blr x1 ——————执行中断处理函数
irq_stack_exit ------ 恢复EL1_SP(svc栈)
.endm
.macro irq_stack_entry
mov x19, sp // preserve the original sp //将svc mode下的栈地址(也就是EL1_SP)保存到x19
/*
* Compare sp with the base of the task stack.
* If the top ~(THREAD_SIZE - 1) bits match, we are on a task stack,
* and should switch to the irq stack.
*/
#ifdef CONFIG_THREAD_INFO_IN_TASK
ldr x25, [tsk, TSK_STACK]
eor x25, x25, x19
and x25, x25, #~(THREAD_SIZE - 1)
cbnz x25, 9998f
#else
and x25, x19, #~(THREAD_SIZE - 1)
cmp x25, tsk
b.ne 9998f
#endif
adr_this_cpu x25, irq_stack, x26
mov x26, #IRQ_STACK_START_SP //IRQ_STACK_START_SP这是irq mode的栈地址
add x26, x25, x26
/* switch to the irq stack */
mov sp, x26 //将irq栈地址,写入到sp
/*
* Add a dummy stack frame, this non-standard format is fixed up
* by unwind_frame()
*/
stp x29, x19, [sp, #-16]!
mov x29, sp
9998:
.endm
/*
* x19 should be preserved between irq_stack_entry and
* irq_stack_exit.
*/
.macro irq_stack_exit
mov sp, x19 //x19保存着svc mode下的栈,也就是EL1_SP
.endm
这么irq的栈在哪设置的,多大呢?
在irq.h中定义了,irq栈的地址和size。
#define IRQ_STACK_SIZE THREAD_SIZE
#define IRQ_STACK_START_SP THREAD_START_SP
thread_info.h中定义了大小。
define THREAD_SIZE 16384 //也就是irq栈的大小大概15k
#define THREAD_START_SP (THREAD_SIZE - 16) //也就是irq栈的首地址是从"0地址+15k"这个地方开始的
linuxkernel中的栈stack概念介绍:内核栈、内核空间进程栈、用户空间进程栈
内核栈:
在linux最开始启动的时侯,系统只有一个进程(更确切的说是kernelthread),就是PID等于0的哪个进程,称作swapper进程(或则称作idle进程)。
用户空间进程栈、内核空间进程栈:
对于一个应用程序而言,可以运行在用户空间,也可以通过系统调用步入内核空间。在用户空间,使用的是用户栈,也就是我们软件工程师编撰用户空间程序的时侯kernel mode linux,保存局部变量的stack。深陷内核后,其实不能用用户栈了,这时侯就须要使用到内核栈。所谓内核栈似乎就是处于SVCmode时侯使用的栈。
内核栈的实现
内核栈是静态定义的,如下:
(kernel-4.14/arch/arm/include/asm/thread_info.h)
#define init_thread_info (init_thread_union.thread_info)
#define init_stack (init_thread_union.stack)
(kernel-4.14/include/linux/sched.h)
union thread_union {
#ifndef CONFIG_THREAD_INFO_IN_TASK
struct thread_info thread_info;
#endif
unsigned long stack[THREAD_SIZE/sizeof(long)];
};
内核进程栈、用户进程栈的实现
Linuxkernel在内核线程,或用户线程时就会分配一个pageframe,具体代码如下:
(kernel-4.14/kernel/fork.c)
static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
{
......
stack = alloc_thread_stack_node(tsk, node);
if (!stack)
goto free_tsk;
......
}
顶部是structthread_info数据结构,底部(高地址)就是该进程的栈了。
当进程切换的时侯,整个硬件和软件的上下文就会进行切换,这儿就包括了svcmode的sp寄存器的值被切换到调度算法选取的新的进程的内核栈上来。
总结
linuxkernelarm64中定义的irq栈,在显存"首地址"处,大小16k.irq_hander使用irq栈。
5T技术资源大放送!包括但不限于:C/C++,Arm,Linux,Androidlinux版qq,人工智能,单片机,猕猴桃派linux启动盘制作工具,等等。在前面的【人人都是极客】公众号内回复「peter」kernel mode linux,即可免费获取!!
记得点击分享、赞和在看,给我充点儿电吧