首页 > 编程语言 >pa4 多道程序和nemu运行RT-thread

pa4 多道程序和nemu运行RT-thread

时间:2024-11-16 23:07:59浏览次数:1  
标签:RT 上下文 pa4 函数 yield pcb cp stack 多道程序

首先看一下讲义里提到的yield os,这个os里面只有两道程序切换的模拟内容,只要做过pa3就很容易理解:

#define STACK_SIZE (4096 * 8)
typedef union {
  uint8_t stack[STACK_SIZE];
  struct { Context *cp; };
} PCB;
static PCB pcb[2], pcb_boot, *current = &pcb_boot;

static void f(void *arg) {
  while (1) {
    putch("?AB"[(uintptr_t)arg > 2 ? 0 : (uintptr_t)arg]);
    for (int volatile i = 0; i < 100000; i++) ;
    yield();
  }
}

static Context *schedule(Event ev, Context *prev) {
  current->cp = prev;
  current = (current == &pcb[0] ? &pcb[1] : &pcb[0]);
  return current->cp;
}

int main() {
  cte_init(schedule);
  pcb[0].cp = kcontext((Area) { pcb[0].stack, &pcb[0] + 1 }, f, (void *)1L);
  pcb[1].cp = kcontext((Area) { pcb[1].stack, &pcb[1] + 1 }, f, (void *)2L);
  yield();
  panic("Should not reach here!");
}

首先定义了PCB结构体,里面只包含一个栈和上下文(在这里上下文只考虑通用+特殊寄存器的值)。然后声明长度2数组来放两道程序。 在main函数里,cte init注册回调函数schedule,他的功能就是在保存当前pcb的信息,并切换到另一个pcb。随后创建两个pcb上下文,然后调用yield(),从此开始执行两个程序并不断切换,达到的效果就是不断地输出AB。

 

在pa3里,我们实现了异常响应,程序触发yield时执行一些其他动作,然后返回到之前的位置,恢复上下文继续执行。而现在,如果我们不返回,而是从b的栈里,把程序B的上下文加载进来,那么就做到了多道程序。

 

在yield-os里,f()起到了类似内核线程的作用。而kconfig()的功能则是创建上下文。按照讲义+union,如果没有设置cp指针,那就是栈顶指针,反之则是cp,cp又在这个32K的内存区间里指向了上下文。

|               |
+---------------+ <---- kstack.end   高地址
|               |
|    context    |
|               |
+---------------+ <--+
|               |    |
|               |    |
|               |    |
|               |    |
+---------------+    |
|       cp      | ---+
+---------------+ <---- kstack.start   低地址
|               |

  栈的设计是先入后出。将程序装载入内存时,我们一般把栈放在最大地址的位置。同时,让栈顶和栈底指针都指向栈的底部,也就是最大地址处。随着栈的装入,栈顶指针逐渐向着低地址方向移动。

 

  讲义要求我们实现kcontext函数,这个函数的功能是初始化上下文,那么我们需要做的就是生成一份寄存器并初始化再返回即可。在这里,我们首先需要初始化一份ctx,就用 context *ctx = (context *)(kstack.end - sizeof(context)),然后清零。

  在riscv32里,前8个参数通过a0到a7传递,之后还有参数则通过栈传递。返回值通过a0传递。所以参数arg要放入a0。entry要放入mepc(这里我还没完全理解,ecall指令里,我们将原计划的下一条命令放入mepc,这里将entry放入mepc,那不会在和后面触发yield时被覆盖吗)。为了difftest,还需要把mstatus设置0x1800。此外,我们还需要把当前的栈顶指针放入gpr[2]--sp寄存器。而pidr用于分页目录指针,此时我们不需要,设置为NULL。 
  这样一来,yield-os就可以正常输出AB了。

 -- ----- -- ------- ---- ----- - - - -- -------

  在rt-thread里。讲义要求实现三个函数stac_init 和两个switch,配套的ev_handle也需要修改。先说ev_handle,这个函数就是回调函数,每次在触发异常响应时,在irq_handle里被调用执行。对应两个switch函数。
  stack_init函数的作用是创建一套上下文。此时就不能直接调用CTE的kcontext函数,因为参数不匹配。rt-therad里还多了一个texit函数,tentry是原本要执行的内核线程,而texit是线程执行完后负责清理工作的函数。其实就是tentry执行完再执行texit的意思。 按照讲义,我们可以设置一个包裹函数当作内核线程,把tentry para texit三个参数打包成arg,传给包裹函数。包裹函数收到以后再拆开。
  值得一提的是,在这里不需要用内联汇编,会把这里搞的更麻烦,只要把三个参数传给包裹函数,正常调用kcontext就行。但在实际做的时候我也遇到了一个问题:kcontext的首个参数是area结构体,也就是堆栈的首尾指针,他的大小应该设置为多少?stack_addr就是首,那尾呢?我的解决思路就是从PCB结构体里抠出来stack_size,发现是16384(或者是别的值),就设置为了这个值。另:stack_init里不可以调用rt_thread_self,因为PCB还没创建好。
  两个switch函数稍微麻烦一些,但讲义里也已经给了提示。可以利用PCB里面的user_data。讲义提示user_data可能本身也是一个有用的值,那我们怎么呢?和stack_init的思路类似,我们声明一个结构体,里面包含from to 和原本的user_data三个变量。
  在两个switch里,先声明结构体变量(用static,拓展生命周期,但它还是一个局部变量),用rt_thread_self取出PCB,然后拿出这个user_data,和from to 打包塞进去,然后触发Yield。在ev_handle里,再把user_data取出,此时的user_data存的是结构体指针,此时拆开,就得到了from to 和原本的user_data按需处理即可。

标签:RT,上下文,pa4,函数,yield,pcb,cp,stack,多道程序
From: https://www.cnblogs.com/namezhyp/p/18525683

相关文章

  • Let'sGoFurther - Chapter 8: Advanced CRUD Operations
         var(ErrRecordNotFound=errors.New("recordnotfound")ErrEditConflict=errors.New("editconflict")) func(mMovieModel)Update(movie*Movie)error{query:=`UPDATEmovieSET......
  • C# The file is too long. This operation is currently limited to supporting file
    namespaceConsoleApp4{internalclassProgram{staticvoidMain(string[]args){stringbigFile=@"C:\Users\fred\Downloads\ebook-master.zip";ReadBigFile(bigFile);}......
  • Ubuntu24.04 安装 VirtualBox
    Ubuntu24.04安装VirtualBox下载DownloadVirtualBoxforLinuxHostshttps://www.virtualbox.org/wiki/Linux_Downloadswgethttps://download.virtualbox.org/virtualbox/7.1.4/virtualbox-7.1_7.1.4-165100~Ubuntu~noble_amd64.deb安装sudodpkg-ivirtualbox-7.1_7.1......
  • 基于RT_Thread的UART
        前言:基于RT的资料相对较少,本章参考RT官网文档写下这篇文章,仅仅是对于自己学习RTT操作系统的总结和复习,仅供参考,有问题欢迎提出。首先我们简单回顾一下UART的知识一.UART设备        UART(UniversalAsynchronousReceiver/Transmitter,通用异步收发传输器......
  • Android平台如何拉取RTSP|RTMP流并转发至轻量级RTSP服务?
    技术背景好多了解我们模块的开发者都知道,我们有非常成熟的轻量级RTSP服务模块,可以采集摄像头或屏幕的数据,编码打包注入Android平台的轻量级RTSP服务模块,让Android设备端,充当个类似于网络摄像头的角色,对外提供个RTSP拉流的URL,实现内网环境下的无服务部署直播场景,这种在内网监控......
  • Let'sGoFurther - Chapter 6: SQL Migrations
      InstallingthemigratetoolTomanageSQLmigrationsinthisprojectwe’regoingtousethemigratecommand-line tool(whichitselfiswritteninGo).OnLinuxandWindows,theeasiestmethodistodownloadapre-builtbinaryandmove ittoalocat......
  • 4.APM32-USART-串口接发
    效果展示USART-串口接发硬件原理图我们使用的开发板上没有USB转串口的芯片,如果要连接到电脑上还需要使用USB转串口的模块或者jlink自带的虚拟串口。开发板的PA9(TX)引脚接USB转串口模块的RX引脚,开发板的PA10(RX)引脚接USB转串口模块的TX引脚,同时双方的GND还要连起......
  • Virtual Box 虚拟机扩容
    VirtualBox虚拟机扩容扩容有风险,有可能导致磁盘损坏,系统不可用。需要使用的工具gpartedGParted--Afreeapplicationforgraphicallymanagingdiskdevicepartitions步骤图解1.扩大虚拟硬盘的容量2.加载分区工具执行分区操作3.扩展逻辑卷要扩大/分......
  • 「AI Infra 软件开源不是一个选项,而是必然」丨云边端架构和 AI Infra专场回顾@RTE2024
      在人工智能和开源技术蓬勃发展的当下,AIInfra项目正经历着日新月异的变革。从跨平台运行时到云边端AI基础设施,再到多模态知识助手,创新浪潮席卷而来。这些进步不仅显著提升了技术指标,也为实时音视频处理、边缘计算、大模型应用等场景开辟了新的可能性。 在RTE2024......
  • GOAT‘S AI早鸟报Part4
    欢迎后台......