找回密码
 注册
关于网站域名变更的通知
查看: 504|回复: 1
打印 上一主题 下一主题

rtthread 线程调度的启动 (基于cortex m3系列芯片)

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-2-3 10:03 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x

' q9 ?% K; b! a5 l- mcortex m3具有用户级和特权级两种特权等级  以及 handle模式(处理中断时运行在handle模式)和线程模式 两种模式,其中用户级特权只能运行在线程模式。
1 f  G: }: _! t, ^) l$ x. V
! J6 {3 M7 ~" T  A" Q如下图(摘自cortex m3权威指南):" u& d) h  c' d" k) O& Y+ ^, b) ^% h. S

# @$ @+ W' p5 W8 n2 X. S $ u) O  L$ ]* h& ?6 q8 }4 U1 ?

1 S) Q3 x+ o  b" W: \. y经过测试rtthread所有的代码都是运行在特权级下的,只不过开机启动的时候特权级线程模式使用的堆栈指针是MSP,调用rt_system_scheduler_start()函数之后 开始线程调度,这些线程包括用户自定义的线程,以及main()函数线程,以及空闲线程也都是特权级,只不过它们使用的堆栈指针是PSP,为了区分,我下面的博客把这种使用PSP寄存器的特权级线程,称之为用户级线程,但要注意实际上它们是特权级的(这个freertos不一样,freertos里面的这种线程都是运行在用户级的)。另外handle模式也就是中断处理函数都是特权级的无法改变,并且使用的都是MSP。证据如下:7 T9 C) m( L) p, V9 U

( r, W1 C- n3 }# ]# U
$ a8 s# Z+ ^8 v7 Z" ]  Z2 Z: A, h" M; r* ]2 P" O

, o# u: ~3 x: I# R6 S& }6 ?+ A9 r8 b
rt_system_scheduler_start()函数在触发PendSV中断之后,会通过BX LR语句,进行返回,返回后的处理器状态决定于LR寄存器中保存的EXC_RETURN的值,可以看到通过PendSV通过ORR指令把bit2 置1,也就是使用PSP,但并没有改变处理器运行的特权级。个人觉得如果想切换到用户级,需要在调用触发PendSV中断之前,手动修改CONTROL寄存器,但rtthread并没有修改。) c* a/ ?- X" U5 |7 q

: w. m* O% \/ A' S, y4 z  I7 I. i所以rtthread用户自定义的线程也可以访问所有的特殊寄存器,例如读写PRIMASK寄存器,关闭全局中断等,并且rtthread好像从来没有使用过SVC指令进行系统调用,因为不需要,它本来就是特权级。
- r* X. m: X1 t; v# f; w, B1 N; J4 ?8 T9 r1 `' w1 C: J
cortex-m3的soc启动之后(这里以stm32芯片为例),默认运行在特权级的线程模式,$ v3 o- L6 A+ X4 O8 h- ]! R" ?7 `
% o' C# D* b% c' k: b

9 T$ h' L! A! R7 x1 j2 C
( n4 N. _' a+ F& }图片来源rtthread官网# V2 _& ^: m: T4 S6 H
9 R5 v9 v4 F& r7 P- q
在芯片启动后运行完汇编启动文件(主要是初始化MSP堆栈寄存器,以及运行复位中断处理函数,完成初始化),会跳转到$Sub$$main()函数,并按照上图调用rtthread_startup()函数,注意这里全部运行在特权级的线程模式。其中rt_application_init()会初始化用户级线程也就是我们用户自己编写的main()函数,但是初始化之后并没有运行,知道rt_system_scheduler_start()函数调用之后,打开调度器,开始运行用户级线程,特权级线程至此结束,之后的特权级基本运行在handle模式而不是线程模式。
" R+ Z4 V' C# [# Q: O
% I+ b: a2 _" k9 j4 o. o. x下面我们来分析rt_system_scheduler_start()函数:
( e, C3 O3 O5 r& }) P$ I
8 o3 q3 D9 @; b
  • void rt_system_scheduler_start(void)
  • {
  •     register struct rt_thread *to_thread;
  •     rt_ubase_t highest_ready_priority;
  •     /* 其实到这里也就两个用户级线程,一个main函数,一个空闲线程,这里to_thread肯定是前面
  •      * rt_application_init()函数所初始化的main函数线程
  •      */
  •     to_thread = _get_highest_priority_thread(&highest_ready_priority);
  • #ifdef RT_USING_SMP
  •     to_thread->oncpu = rt_hw_cpu_id();
  • #else
  •     rt_current_thread = to_thread;
  • #endif /*RT_USING_SMP*/
  •     rt_schedule_remove_thread(to_thread);
  •     to_thread->stat = RT_THREAD_RUNNING;
  •     /* switch to new thread */
  • #ifdef RT_USING_SMP
  •     rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp, to_thread);
  • #else
  •     /*最终调用了以下函数,这个函数好像只在这里调用了一次,由于首次调用,所以不用指定from线程
  •      *因为运行到这里仍然是特权级的线程模式,并没有用户级线程在运行,所以from线程还不存在,也不用
  •      *为from线程保存现场
  •      */
  •     rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp);
  • #endif /*RT_USING_SMP*/
  •     /* never come back */
  • }0 `7 y2 n6 \$ V3 S$ y
     
. g* R4 M- _( x3 `7 P1 T
/ n0 P8 V7 J% Prt_hw_context_switch_to()函数是汇编函数如下:( O1 _6 Z$ g0 E
: L8 [: _# ^( r7 O0 B/ q( ]
  • rt_hw_context_switch_to:
  •     LDR     R1, =rt_interrupt_to_thread
  •     STR     R0, [R1]
  •     /* set from thread to 0 */
  •     LDR     R1, =rt_interrupt_from_thread
  •     MOV     R0, #0
  •     STR     R0, [R1]
  •     /* set interrupt flag to 1 */
  •     LDR     R1, =rt_thread_switch_interrupt_flag
  •     MOV     R0, #1
  •     STR     R0, [R1]
  •     /* set the PendSV exception priority */
  •     LDR     R0, =SHPR3
  •     LDR     R1, =PENDSV_PRI_LOWEST
  •     LDR.W   R2, [R0,#0]             /* read */
  •     ORR     R1, R1, R2              /* modify */
  •     STR     R1, [R0]                /* write-back */
  •     LDR     R0, =ICSR               /* trigger the PendSV exception (causes context switch) */
  •     LDR     R1, =PENDSVSET_BIT
  •     STR     R1, [R0]
  •     /* restore MSP */
  •     /*重置MSP寄存器为中断向量表中指定的值,因为特权级线程模式执行到这里也就到头了,永远也不会返回
  •      *了,所以可以重置MSP寄存器清空堆栈,供以后中断处理函数使用
  •      */
  •     LDR     r0, =SCB_VTOR
  •     LDR     r0, [r0]
  •     LDR     r0, [r0]
  •     NOP
  •     MSR     msp, r0
  •     /* enable interrupts at processor level */
  •     /* 打开中断之后会立即调到PendSV中断处理函数,用来切换到main函数线程*/
  •     CPSIE   F
  •     CPSIE   I
  •     /* never reach here! */ /* 根据官方注释可知,永远不会到达这里了 */
    % i  a) x, ^0 @
; T) ^2 @+ Y4 Z9 R- F

8 I0 }0 e& f" j6 B, H
7 k# G' X4 x. p/ x& W; n       PendSV_Handler中断函数如下:
  L% T+ p1 I. N2 ?3 h8 l0 _  F( r: t7 J
  • PendSV_Handler:
  •     /* disable interrupt to protect context switch */
  •     MRS     R2, PRIMASK
  •     CPSID   I
  •     /* get rt_thread_switch_interrupt_flag */
  •     LDR     R0, =rt_thread_switch_interrupt_flag
  •     LDR     R1, [R0]
  •     CBZ     R1, pendsv_exit         /* pendsv aLReady handled */
  •     /* clear rt_thread_switch_interrupt_flag to 0 */
  •     MOV     R1, #0
  •     STR     R1, [R0]
  •     LDR     R0, =rt_interrupt_from_thread
  •     LDR     R1, [R0]
  •     CBZ     R1, switch_to_thread    /* skip register save at the first time */
  •     MRS     R1, PSP                 /* get from thread stack pointer */
  •     STMFD   R1!, {R4 - R11}         /* push R4 - R11 register */
  •     LDR     R0, [R0]
  •     STR     R1, [R0]                /* update from thread stack pointer */
  • switch_to_thread:
  •     LDR     R1, =rt_interrupt_to_thread
  •     LDR     R1, [R1]
  •     LDR     R1, [R1]                /* load thread stack pointer */
  •     LDMFD   R1!, {R4 - R11}         /* pop R4 - R11 register */
  •     MSR     PSP, R1                 /* update stack pointer */
  • pendsv_exit:
  •     /* restore interrupt */
  •     MSR     PRIMASK, R2
  •     ORR     LR, LR, #0x04 /* 这句话保证PendSV中断返回之后,自动运行在用户级线程模式,其实是特权级线程模式 */
  •     BX      LR* E% ]' H7 m, a+ Y

, S0 l% d$ ^6 h$ U
' \% t+ D+ ?' _8 Q7 u1 f! \: k* S        可以看到执行完rt_hw_context_switch_to()函数,特权级线程模式到此结束,PendSV中断会返回用户级线程模式,开始main函数 用户级线程,在main()为用户自定义函数,用户可以在main()中初始化和启动其他用户级线程,之后系统会借助实时时钟的中断、以及rt_thread_sleep()、rt_mq_recv()等函数实现用户级线程切换,其实都是通过调用rt_schedule()实现线程切换的,rt_schedule()函数的实质也是通过出发PendSV中断,来实现线程切换的,也就是之后无论是在用户级线程模式(用户级线程主动调用rt_thread_sleep()、rt_mq_recv()等函数请求切换线程),还是特权级handle模式(实时时钟中断中请求切换线程),都通过触发PendSV中断来实现线程切换,也就是处理器好像再也没有回到特权级线程模式了,都是在特权级handle模式。
3 u" T" j1 N: {3 G7 Y+ B, u
$ _2 v( }. r+ N) W! y6 R# g这里贴出来rt_schedule()的代码:
8 ~' g& [5 Q3 B# _% b# Q  y* T8 }# b6 K% j
  • void rt_schedule(void)
  • {
  •     rt_base_t level;
  •     struct rt_thread *to_thread;
  •     struct rt_thread *from_thread;
  •     /* disable interrupt */
  •     level = rt_hw_interrupt_disable();
  •     /* check the scheduler is enabled or not */
  •     if (rt_scheduler_lock_nest == 0)
  •     {
  •         rt_ubase_t highest_ready_priority;
  •         if (rt_thread_ready_priority_group != 0)
  •         {
  •             /* need_insert_from_thread: need to insert from_thread to ready queue */
  •             int need_insert_from_thread = 0;
  •             to_thread = _get_highest_priority_thread(&highest_ready_priority);
  •             if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING)
  •             {
  •                 if (rt_current_thread->current_priority < highest_ready_priority)
  •                 {
  •                     to_thread = rt_current_thread;
  •                 }
  •                 else
  •                 {
  •                     need_insert_from_thread = 1;
  •                 }
  •             }
  •             if (to_thread != rt_current_thread)
  •             {
  •                 /* if the destination thread is not the same as current thread */
  •                 rt_current_priority = (rt_uint8_t)highest_ready_priority;
  •                 from_thread         = rt_current_thread;
  •                 rt_current_thread   = to_thread;
  •                 RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread));
  •                 if (need_insert_from_thread)
  •                 {
  •                     rt_schedule_insert_thread(from_thread);
  •                 }
  •                 rt_schedule_remove_thread(to_thread);
  •                 to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK);
  •                 /* switch to new thread */
  •                 RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
  •                         ("[%d]switch to priority#%d "
  •                          "thread:%.*s(sp:0x%08x), "
  •                          "from thread:%.*s(sp: 0x%08x)\n",
  •                          rt_interrupt_nest, highest_ready_priority,
  •                          RT_NAME_MAX, to_thread->name, to_thread->sp,
  •                          RT_NAME_MAX, from_thread->name, from_thread->sp));
  • #ifdef RT_USING_OVERFLOW_CHECK
  •                 _rt_scheduler_stack_check(to_thread);
  • #endif
  •                 if (rt_interrupt_nest == 0)
  •                 {
  •                     extern void rt_thread_handle_sig(rt_bool_t clean_state);
  •                     rt_hw_context_switch((rt_ubase_t)&from_thread->sp,
  •                             (rt_ubase_t)&to_thread->sp);
  •                     /* enable interrupt */
  •                     rt_hw_interrupt_enable(level);
  • #ifdef RT_USING_SIGNALS
  •                     /* check signal status */
  •                     rt_thread_handle_sig(RT_TRUE);
  • #endif
  •                     goto __exit;
  •                 }
  •                 else
  •                 {
  •                     RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));
  •                     rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp,
  •                             (rt_ubase_t)&to_thread->sp);
  •                 }
  •             }
  •             else
  •             {
  •                 rt_schedule_remove_thread(rt_current_thread);
  •                 rt_current_thread->stat = RT_THREAD_RUNNING | (rt_current_thread->stat & ~RT_THREAD_STAT_MASK);
  •             }
  •         }
  •     }
  •     /* enable interrupt */
  •     rt_hw_interrupt_enable(level);
  • __exit:
  •     return;
  • }8 K4 h0 A& t& v3 |; j

8 T' I0 n, r% z) j
( ^8 j( F. \& y8 G9 @/ Q                  9 p- `, e& \# S: i

* `* q& M! _8 q, `" e: R2 K

该用户从未签到

2#
发表于 2021-2-3 11:01 | 只看该作者
rtthread 线程调度的启动 (基于cortex m3系列芯片)
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-24 21:32 , Processed in 0.187500 second(s), 26 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表