一、前言
上一节我们实现了2个在任务中主动调用,实现任务切换的函数接口,其具有一下几个功能:
-
从一个循环的任务切换到另一个循环的任务 -
切换回来之后,从切换之前被打断的位置继续执行 -
这是函数调用做不到的,函数调用只能从函数开始位置执行
但是一个操作系统,大部分的调用场景会发生在定时器中断处理中,在中断环境下,我们还需要定义并实现中断环境的任务切换。
中断环境除了要保存任务的状态以外,中断本身也需要保存一些现场状态,因为中断对正常执行的程序来说,可能会发生在任意一个指令之间,所以为了退出中断之后程序还可以正确运行,需要在进出中断的过程中进行保护现场与恢复现场
二、中断环境下任务切换的流程
- 保存寄存器到当前任务的堆栈
- 调用中断处理函数
- 设置中断切换标志位、设置全局from任务信息、设置全局to任务信息
- 完成中断处理,准备退出中断
- 判断是否需要在退出中断时进行任务切换
- 如果不需要,从当前任务栈恢复寄存器,返回继续执行
- 如果需要进行任务切换
- 把任务栈中保存的寄存器信息恢复
- 把from任务的寄存器数据保存到from任务的堆栈已经任务结构体
- 把to任务堆栈中的寄存器状态恢复到当前寄存器中
- 退出异常,进入to任务
三、代码展示
aarch64 中断处理函数,在退出中断时,会根据标志变量判断是否需要进行任务切换操作
IRQInterruptHandler:
????//进入中断,保存寄存器
????saveregister
????savefloatregister
????//执行中断处理函数
????bl?handle_domain_irq
????//判断退出中断时候是否需要进行任务切换
????adr?x2,?task_thread_switch_interrupt_flag
????ldr?x3,?[x2]
????cmp?x3,?#1
????beq?exit_irq_with_task_switch
????//不需要任务切换,恢复寄存器之后退出中断
????restorefloatregister
????restoreregister
????eret
//如果标志置位,需要在退出中断的时候进行任务切换
exit_irq_with_task_switch:
????//将中断切换标志位清零
????adr?x2,?task_thread_switch_interrupt_flag
????mov?x3,?#0
????str?x3,?[x2]
????//恢复寄存器,恢复到进入中断之前的状态
????restorefloatregister
????restoreregister
????//此时的状态是进入中断之前的状态,也就是from任务的状态
????//开始进行任务切换
????//保存from任务的状态到堆栈
????//1?保存from任务通用寄存器状态
????sub?sp,?sp,?#30?*?8
????stp?x0,?x1,?[sp,?#16?*?0]
????stp?x2,?x3,?[sp,?#16?*?1]
????stp?x4,?x5,?[sp,?#16?*?2]
????stp?x6,?x7,?[sp,?#16?*?3]
????stp?x8,?x9,?[sp,?#16?*?4]
????stp?x10,?x11,?[sp,?#16?*?5]
????stp?x12,?x13,?[sp,?#16?*?6]
????stp?x14,?x15,?[sp,?#16?*?7]
????stp?x16,?x17,?[sp,?#16?*?8]
????stp?x18,?x19,?[sp,?#16?*?9]
????stp?x20,?x21,?[sp,?#16?*?10]
????stp?x22,?x23,?[sp,?#16?*?11]
????stp?x24,?x25,?[sp,?#16?*?12]
????stp?x26,?x27,?[sp,?#16?*?13]
????stp?x28,?x29,?[sp,?#16?*?14]
????str?x30,?[sp,?#16?*?15]
????//将from任务和to任务的任务信息恢复到x0、x1寄存器
????adr?x2,?task_interrupt_from_thread
????ldr?x0,?[x2]
????adr?x2,?task_interrupt_to_thread
????ldr?x1,?[x2]
????//2?保存from任务sp状态
????//获取当前任务sp状态
????//获取任务结构体中变量地址
????//保存sp地址到任务结构体中对应位置
????mov?x3,?sp
????mov?x2,?x0
????add?x2,?x2,?#8?*?0
????str?x3,?[x2]
????//3?保存from任务cpcr状态
????//获取当前任务spsr状态
????//获取任务结构体中spsr变量位置
????//保存spsr到任务结构体中对应位置
????mrs?x3,?spsr_el1
????mov?x2,?x0
????add?x2,?x2,?#8?*?1
????str?x3,?[x2]
????//4?保存from任务的返回地址
????//获取当前任务返回地址
????//获取任务结构体中变量地址
????//保存返回地址到任务结构体中对应位置
????mrs?x3,?elr_el1
????mov?x2,?x0
????add?x2,?x2,?#8?*?2
????str?x3,?[x2]
????//开始从to任务堆栈恢复to任务
????//1?恢复to任务sp寄存器
????mov?x2,?x1
????add?x2,?x2,?#8?*?0
????ldr?x3,?[x2]
????mov?sp,?x3
????//2?恢复to任务spsr状态寄存器
????mov?x2,?x1
????add?x2,?x2,?#8?*?1
????ldr?x3,?[x2]
????msr?spsr_el1,?x3
????//3?恢复to任务异常返回地址
????mov?x2,?x1
????add?x2,?x2,?#8?*?2
????ldr?x3,?[x2]
????msr?elr_el1,?x3
????//4?恢复to任务通用寄存器
????ldp?x0,?x1,?[sp,?#16?*?0]
????ldp?x2,?x3,?[sp,?#16?*?1]
????ldp?x4,?x5,?[sp,?#16?*?2]
????ldp?x6,?x7,?[sp,?#16?*?3]
????ldp?x8,?x9,?[sp,?#16?*?4]
????ldp?x10,?x11,?[sp,?#16?*?5]
????ldp?x12,?x13,?[sp,?#16?*?6]
????ldp?x14,?x15,?[sp,?#16?*?7]
????ldp?x16,?x17,?[sp,?#16?*?8]
????ldp?x18,?x19,?[sp,?#16?*?9]
????ldp?x20,?x21,?[sp,?#16?*?10]
????ldp?x22,?x23,?[sp,?#16?*?11]
????ldp?x24,?x25,?[sp,?#16?*?12]
????ldp?x26,?x27,?[sp,?#16?*?13]
????ldp?x28,?x29,?[sp,?#16?*?14]
????ldr?x30,?[sp,?#16?*?15]
????//数据从堆栈取出之后,堆栈指针移动
????add?sp,?sp,?#30?*?8
????//当前处理器状态已经切换到了to任务,异常返回之后到to任务执行
????eret
设置任务切换标志变量
.global?task_interrupt_from_thread;
.global?task_interrupt_to_thread;
.global?task_thread_switch_interrupt_flag;
.global?interrupt_task_switch_from_to
.type?interrupt_task_switch_from_to,?"function"
//?x0:?from
//?x1:?to
//设置切换标志为1,通过全局变量传递from和to任务信息
interrupt_task_switch_from_to:
????adr?x2,?task_thread_switch_interrupt_flag
????mov?x3,?#1
????str?x3,?[x2]
????adr?x2,?task_interrupt_from_thread
????mov?x3,?x0
????str?x3,?[x2]
????adr?x2,?task_interrupt_to_thread
????mov?x3,?x1
????str?x3,?[x2]
????ret
任务调度代码
void?timer_handler(struct?irq_desc?*desc)
{
????arch_timer_compare(arch_timer_frequecy());
????log_i("arch?timer_handler!");
????if((count++)?%?2?==?0)
????{
????????log_i("switch?to?taskb!");
????????interrupt_task_switch_from_to(&taska,?&taskb);
????}
????else
????{
????????log_i("switch?to?taska!");
????????interrupt_task_switch_from_to(&taskb,?&taska);
????}
}
运行结果
qemu-system-aarch64?-machine?virt,gic-version=3?-cpu?cortex-a57?-smp?1?-m?1024?-nographic?-serial?mon:stdio?-kernel?app
run?in?main!
[????0.002736]?EasyLogger?V2.2.99?is?initialize?success.
i?am?taska?run!?line:?20
i?am?taska?run!?line:?22
i?am?taska?run!?line:?20
i?am?taska?run!?line:?22
i?am?taska?run!?line:?20
i?am?taska?run!?line:?22
[????1.007194]?irq_enter
[????1.007440]?handle_domain_irq:?[30]
[????1.007831]?arch?timer_handler!
[????1.008077]?switch?to?taskb!
[????1.008260]?irq_exit
i?am?taskb?run!?line:?38
i?am?taskb?run!?line:?40
i?am?taskb?run!?line:?38
i?am?taskb?run!?line:?40
[????2.008983]?irq_enter
[????2.009176]?handle_domain_irq:?[30]
[????2.009412]?arch?timer_handler!
[????2.009574]?switch?to?taska!
[????2.009716]?irq_exit
i?am?taska?run!?line:?20
i?am?taska?run!?line:?22
i?am?taska?run!?line:?20
i?am?taska?run!?line:?22
i?am?taska?run!?line:?20
i?am?taska?run!?line:?22
四、操作系统的任务切换
在rtos系统中,会根据一些策略去选择一个任务去执行。
当前我们已经实现了在任务环境与中断环境下的任务切换功能。
接下来通过添加一些策略来选择合适的任务执行,就可以实现一个简单的根据优先级以及时间片去进行调度的小系统。
可以添加和丰富更多任务相关的资源以及功能。
|