目录
多道程序(多任务处理)
- 含义:
- cpu一次载入多个程序
- 先运行第一个程序
- 当第一个程序在等待io操作时
- 运行第二个程序
- 以此类推…
上下文切换流程(yield-os为例)
- 操作系统-main函数
- 初始化,注册回调函数 schedule
- kontext 为两个进程分别创建上下文
-
进程的上下文以及指向它的指针cp的位置
-
创建一个cp指向context
-
让context中的mecp为entry
-
让context中的gpr[10] (a0)为arg
-
返回值为cp
-
- 调用yield,调用ecall
- 硬件-nemu指令-ecall
- 存入当前上下文信息,跳转到mtvec(异常入口)am_asm_trap
- 将上下文信息存入当前进程的栈==(还没将地址存给cp)
- 将sp(栈指针)存入a0
- 调用__am_irq_handle,参数为a0
- 判断事件
- 将mepc+4 ==(下次回到这个进程就执行下一条指令)
- 调用回调函数 schedule
- 操作系统-schedule函数
- (current是栈顶指针,指向某个栈的顶部,用途是在多个进程的栈顶中切换,指示当前运行的进程)
- 存储当前进程的上下文地址到自己的栈中的cp中
- 将current指向新的栈顶
- 将新栈顶的上下文地址cp返回
- 硬件-nemu trap.s
- 从__am_irq_handle返回 ,返回值为a0
- 将sp更新为a0(指向其他进程的栈的指针)
- (此时的上下文地址已经变成另外一个进程的上下文地址,所读取的上下文信息变成了另外一个进程的上下文信息)
- 恢复 mstatus 和 mepc 寄存器、通用寄存器(包括在kcontext中存的a0)(完成进程的切换)
- mret跳转到==(新进程的)mepc(entry)==继续执行 - f函数
- 操作系统-f函数
- 打印
- 等待
- 调用yield
- 重复2、3、4
RT-Thread
两个抽象层(这一思想和AM非常类似)
- BSP(Board Support Package)
- 为各种型号的板卡定义了一套公共的API
- 并基于这套API实现RT-Thread内核;
- 而对于一款板卡, 只需要实现相应的API, 就可以将RT-Thread内核运行在这款板卡上
- 当然, BSP也不仅仅是针对真实的板卡, 也可以对应QEMU等模拟器, 毕竟RT-Thread内核无需关心底层是否是一个真实的板卡.
- libcpu
- 为各种CPU架构定义了一套公共的API
- RT-Thread内核也会调用其中的某些API
需要实现的函数
-
rt_hw_stack_init()
stack_addr
需要地址对齐到sizeof(uintptr_t)
- 调用
kontext
构造上图的栈 - entry为一个包裹函数
- 参数为parameter的首地址,
- 在包裹函数中解析parameter首地址
- 将tentry
- texit
- parateter解析出来
- 在包裹函数中调用tentry
- 在
tentry
返回后调用texit
-
rt_hw_context_switch_to( )
- 作用:
- 切换到
to
指向的上下文指针变量cp所指向的上下文context
- 切换到
- 作用:
-
rt_hw_context_switch( )
- 作用:
- 切换到
to
指向的上下文指针变量cp所指向的上下文context - 额外将当前上下文的指针cp写入
from
指向的上下文指针变量中
- 切换到
- 调用rt_thread_self()返回当前线程的PCB
- 将当前pcb中的user_data存到局部变量中
- 然后将 to 的数据写入当前pcb的user_data中
- 将from的值赋值为当前的pcb的context
- 调用yield,跳转到ev_handler
- 从yield返回后,恢复 user data
- 作用:
-
ev_handler() (在这里返回切换后的context)
- 添加case识别出
EVENT_YIELD
事件后, - 从pcb中的user data读出
to
- 将to的值赋值给传入的c,并返回
- 添加case识别出
思考题
- 如果不同的进程共享同一个栈空间, 会发生什么呢?
- 数据冲突:栈是用于存储局部变量和函数调用信息的,每个进程都有自己的栈空间。如果多个进程共享同一个栈,局部变量可能会被其他进程修改,导致数据不一致和不可预测的行为。
- 栈溢出:栈的大小是有限的,如果多个进程同时使用同一个栈,可能会导致栈溢出,进而引发程序崩溃或未定义行为。
- 安全性问题:共享栈空间可能导致安全漏洞,恶意进程可以修改其他进程的栈内容,从而执行任意代码或获取敏感信息。
- 调试困难:由于栈内容的混乱,调试程序时会变得非常困难,难以追踪错误的来源。
- 上下文切换问题:操作系统在进行进程切换时,通常会保存和恢复每个进程的栈状态。如果栈被多个进程共享,可能会导致上下文切换时的状态不一致。