最近在公司小芯片上移植rtt:
看rtt的一些小感悟:
因为芯片是单核,进程调度发生:
假如A进程-》B进程:
1:在中断中发生,
当clock时钟中断触发,系统tick+1,此时就要检查有没有线程的时间片到了,如果有,要发生调度
2:在已有的进程中手动掉用高优先级进程,要发生调度
3:该进程执行完毕,进程退出的时候,要发生调度
中断中:
12 void rt_hw_timer_handler(void)
113 {
116 write_csr_tintclear(CSR_TINTCLR_TI);
119 rt_tick_increase();
125 }
展开
部分代码已经删除
5 void rt_tick_increase(void)
66 {
76 ++ rt_tick; //系统时间+1
82 -- thread->remaining_tick; //时间+1
83 if (thread->remaining_tick == 0) //如果时间片到了 就要调度
84 {
90 rt_schedule(); //调度
91 }
99 }
展开调度:
这里就有区分了,调度发生在中断里和不发生在中断里
void rt_schedule(void)
{
....
//获取高优先级进程
to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
//调度发生在中断中:
if (rt_interrupt_nest == 0)
485 {
486 extern void rt_thread_handle_sig(rt_bool_t clean_state);
494 rt_hw_interrupt_enable(level);
495
}
517 else //调度不发生在中断中
518 {
519 RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));
520
521 rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp,
522 (rt_ubase_t)&to_thread->sp);
523 }
}
发生在中断里:
因为中断进入的时候,保存了A进程的sp到t
寄存器里,sp换成了系统栈,按理来说退出中断的时候要从tp寄存器取回A进程的sp,然后在sp栈中找到epc,重新到中断打断的代码处执行
但是现在有调度,要在退出中断后执行B进程,就不能从tp里取sp了
要去一个其他位置,这个位置存的是B的sp
看rt_hw_context_switch_interrupt实现
51 rt_hw_context_switch_interrupt:
52 la t0, rt_thread_switch_interrupt_flag
53 REG_L t1, t0, 0
54 bnez t1, _reswitch
55 li t1, 0x01 /* set rt_thread_switch_interrupt_flag to 1 */
56 LONG_S t1, t0, 0
57 la t0, rt_interrupt_from_thread /* set rt_interrupt_from_thread */
58 LONG_S a0, t0, 0
59 _reswitch: //把B进程的sp存在这里
60 la t0, rt_interrupt_to_thread /* set rt_interrupt_to_thread */
61 LONG_S a1, t0, 0
62 jr ra
63 nop
64
然后退出中断的时候去那里读取sp
72 mips_irq_handle:
73 SAVE_ALL
74
75 /* let k0 keep the current context sp */
76 move tp, sp
77 /* switch to kernel stack */
78 la sp, _system_stack
79
80 bl rt_interrupt_enter
81 /* Get Old SP from k0 as paremeter in a0 */
82 move a0, tp
83 bl rt_general_exc_dispatch
84 bl rt_interrupt_leave
85
86 /* switch sp back to thread context */
87 move sp, tp
88 #if 1
89 /*
90 * if rt_thread_switch_interrupt_flag set, jump to
91 * rt_hw_context_switch_interrupt_do and do not return
92 */
93 la t0, rt_thread_switch_interrupt_flag
94 LONG_L t1, t0, 0
95 beqz t1, spurious_interrupt
96 LONG_S zero, t0, 0 /* clear flag */
97
98 /*
99 * switch to the new thread
100 */
101 la t0, rt_interrupt_from_thread//做一个标志位
102 LONG_L t1, t0, 0
103 nop
104 LONG_S sp, t1, 0 /* store sp in preempted task TCB */
105
106 la t0, rt_interrupt_to_thread
107 LONG_L t1, t0, 0
108 nop //在这里读取B进程sp
109 LONG_L sp, t1, 0 /* get new task stack pointer */
110 #endif
111 b spurious_interrupt
112 nop
113 spurious_interrupt:
114 RESTORE_ALL_AND_RET
如果进程切换不是发生在中断中,怎么记录呢
mips和la的指令都是risc指令结构,手动调度高优先级的时候要调用bl和jirl指令,就会存储打断代码下调ra,然后把让存到epc里,然后保存上下文到A进程栈sp中,再去调度。
26 rt_hw_context_switch:
27 csrwr ra, LOONGARCH_CSR_EPC;//把打断处ra放到epc里
28 SAVE_ALL//现在的sp还是A的,把epc存到sp里
29 //sp寄存器里存B进程的栈
30 REG_S sp, a0, 0 /* store sp in preempted tasks TCB */
31 REG_L sp, a1, 0 /* get new task stack pointer */
32 //当个异常返回了,这样就执行B进程了
33 RESTORE_ALL_AND_RET