1、线程概念的介绍
在optee的设计思想中,也是有线程这个概念的。也有一个结构体 thread_ctx
表示一个线程(类似于Linux Kernel中的task_struct),在Kernel中,我们习惯称一个线程为TASK,而在optee我们习惯称一个线程为thread。
在optee中,并不是所有程序都有thread概念。只有std smc entry进来的执行程序才会有线程的概念,才会为其分配thread结构体。其它方式进入执行的程序,都不会分配thread结构体,也就没有线程的概念了。
为什么会这样设计呢?那是因为:
- 只有std smc entry进来时,异常/中断才会被Unmasked;只有std smc entry进来的程序,才会有被forgign中断打断的可能;只有std smc entry进来的程序,才可以调用RPC机制。故只有std smc entry进来的程序,才会发生上下文切换
- 其它方式进来的程序,不会发生上下文切换,故也就不需要保存和恢复ctx了。
optee最多支持 CFG_NUM_THREADS
个数的线程,记录在 structthread_ctx
结构体定义的的threads[]全局数组中。
struct thread_ctx threads[CFG_NUM_THREADS];
thread_ctx
结构体描述着一个线程的信息,类似于Linux Kernel中的task_struct。其结构体原型如下
其中:
线程的ctx(可以理解为cpu_contex)。ctx的设计和大多数操作系统或软件的设计思想是一样的。即切换上下文的时候,保存当前线程的ctx, 恢复下一个线程的ctx。燃鹅,非常遗憾,在optee中没有调度,也没有线程切换的概念。
那么optee有哪些切换上下文的场景呢?
- 当线程调用了RPC或被foreign中断打断,cpu需要调用smc主动切换出去时,此时会保存当前线程的ctx;
- 当一个std call进来,判定为从RPC恢复而来(foreign中断回来也属于RPC回来)时,此时会根据传回来的thread_id恢复之前线程的ctx。
- 线程正在运行时,被native中断打断了,此时会保存当前线程的ctx,native
(2)、 enumthread_state state
: 线程有三个的状态,其切换关系如下所示:
2、关于调度
当tee中的thread线程被外部中断中断切回REE时,REE有机会重新调度正在运行的应用程序。只有在REE应用程序被重新切回TEE后,tee中的thread才会恢复。因此tee中的thread执行遵循正常世界调用者上下文的调度。
Optee_os没有实现任何线程调度。每个optee thread都关联从REE调用来的程序,或者说每一个optee thread都属于它对应的User线程程序。模型如下所示:
如果再进一步拆解,其实可以把这个程序或者这个大任务,拆解成3个阶段,分别是UserSpace、KernelSpace、Secure Space。从Linux Kernel的视角来看,这3个阶段都属于user线程。由于在TEE内没有调度机制,所以在Secure Space程序也是无法被调度的。只有在UserSpace和KernelSpace程序才可能发生调度。也就是说,当一个secure thread正在执行了,如果它不被切回REE,它是不可能被调度到其它secure thread上的。那么当这个secure thread返回REE时(对于下图中的2处),是有可能被REE程序调度的。
3、总结
- optee中没有调度,没有线程切换的概念。
- optee中只有在native中断、RPC、foreign中断发生时和返回时,才会涉及到thread ctx的保存和恢复
- 只有std smc entry程序才会发生:native中断、RPC、foreign中断
- optee中的thread切回REE时,在REE可能发生调度。