repo:https://github.com/wanghun315/Rust-in-action2022
Rust语言学习
- 作为系统级语言,不需要gc,不用关心内存溢出的情况。相比于其他语言,个人感觉这个特性很有说服力
- 面向对象编程,作为一个一直使用java和c#的开发者而言,继承封装多态已经深入骨髓
- 可能接触的没有过于深入,感觉Rust的代码比c++更容易让人明白,不需要声明与实现分离,引用只需要use即可
- 强类型,特别特别的强类型,一开始有些不习惯,后来慢慢习惯了,感觉Rust的作者很谨慎,宁愿报错也要效率和节省内存
- Cargo的功能堪比maven,Nuget和mpn,论一个包管理的重要性
- 指针和引用还在,虽然讨厌,但是不得不接受他的存在,据说俄罗斯的开发者为何厉害,因为他们喜欢指针套指针,能套五层。
rcore
通过lab0,让我对计算机的启动和答题的工作有了一定的了解,包括批处理和分时任务
- OS启动过程
- 通电后,CPU的通用寄存器清零(.bss段)
- PC指向0x1000(有固化再硬件中的一小段引导代码)
- 跳转到RustSBI(0x80000000)
- 硬件加载完成后跳转到$(KERNEL_BIN) 所在内存位置 0x80200000
- 执行第一条指令
- 罪魁祸首:三元组-当去除标准库后,一切就变得那么的不美好了,什么都要自己写
- 切换目标平台
- 指定连接器连接的文件(设置入口点,目标平台,指令存放地,各段的地址)
- 配置空间布局,入口点指向的Rust代码;预留操作系统使用空间;栈顶和栈底
- 入口点函数与配置空间布局汇编连理联系
- 清空.bss段,谁知道这个地址的内存以前放的是啥
- 移除println!宏,需要自己写一个
- 将main函数变为入口点
- 添加退出机制
- 运行
- QEMU有两种运行模式:
- User mode 模式,即用户态模拟
- System mode 模式,即系统态模式
- 懒加载的妙用
- 声明了main函数,不过是懒加载,当出现其他main函数时(相同函数名的函数更准确)就会加载并作为入口点执行
- 这让我感觉像是继承,当方法被覆盖后,就应该执行覆盖后的方法,只是没想到连入口函数也能这么玩
- 这样带来的好处是,同时声明多个入口点(main函数),就会被执行多次。
- 无时无刻的上下文
- 通过ecall进入内存态时,OS保存被打断的app的Trap上下文
- 通过CSR寄存器,完成系统调用服务的分发与处理
- sret回归到原应用中
- Trap上下文实现过程
1
2
3
41. 根据RISC-V特权级规范,将__altraps地址4字节对齐
2. CSR原有值暂存到rd寄存器中,CSR新值改为rs寄存器的数值,sp与sscratch的内核栈和用户栈互换
3. 保存Trap上下文
4. sp->a0
- 任务切换
- 静态与动态内存分配
- 静态内存分配:编译器在编译期完成
- 动态分配分配:场景-c的new和delete,c++的malloc和free,导致内存移除的罪魁祸首
- rust特性:函数内部消亡,除了基础类型和借用规则之外,所有的生命都需要在函数内部初始化和销毁
- 虚拟地址
- 硬件虚拟化,形成地址空间,数据结构的引用
- 内存管理单元(MMU)将虚拟地址空间转换为物理空间的访问
1
2
3
4
5
6
7
81.分段内存管理
a. 插槽的方式 特点:MMU操作简单 缺点:内碎片严重
b. 减小颗粒度 特点:减少了内碎片 缺点:外碎片严重
2.分页内存管理
定义:应用根据目录去查找物理内存的地址,通过虚拟页号与物理页号形成的页表进行操作
特点:安全,有效的保护了物理地址
灵活,将物理内存进行了抽象
MMU的功能变强大了
- SV39
- 虚拟地址的设定;每个页面设置为4Kib,低12位字节用于表示页内偏移
- 页表项的说明,每个地址都有明确的定义,例如低8位是设置项,PPN(地11位到54位)是物理页号
- 多级页表:按需分配,一本书,维护一本目录(目录里面可能还说目录),可能目录是空的,那是作者的问题,我们只需要通过目录能找到实际地址即可
- 地址转换过程:陈老师一直说很棒的一张图,大体过程如下:
1
2
3
4SV39模式采用三级页表
VPN2->VPN1->VPN0->实际物理页号
实际物理页号对应的物理页基址加上偏移就是虚拟地址对应的物理地址
每个VPN页都是4Kib大小,正好对应了物理页大小 - 物理页的管理
1
2
31.设置目前物理内存的开始地址和结束地址,设置用过以后的地址
2.分配时,先判断是否存在使用完毕回收的,有则使用无则判断是否物理内存不够,如果还有余则将开始地址向后移动,将原有的地址分配出去
3.回收,先判断回收的地址是否被分配,分配了则放到回收栈中,以备下次使用 - RAII:资源获取就是初始化,帧页被创建后,纪要进行物理资源的获得
lab3
- 什么是进程: 有系统资源且能在系统中执行的应用程序(与线程的不尽相同)
- 进程与系统调用:三种运行态之间的改变
- fork系统调用:基于ecall指令,将命令放到不同寄存器中,创建后两个进程会建立联系
- waitpid系统调用:前进程等待一个子进程变为僵尸进程,回收其全部资源并收集其返回值
- exec系统调用:将当前进程的地址空间清空并加载一个特定的可执行文件,返回用户态后开始它的执行
- 进程核心数据结构
- 进程控制块的重要组成部分:进程标识符和内核栈
1
2进程标识符:唯一的存在(便于声明和回收)
内核栈:保存物理帧的生命周期 - 进程核心结构:任务控制块(内核对进程进行管理的单位,故而是一种极其关键的内核数据结构。在内核看来,它就等价于一个进程)
- 管理进程集合:任务管理器
- 调用和维护处理器状态:处理器管理结构
- 进程机制的实现
- 创建初始进程
- 进程调用机制
- 进程生成机制
- 进程资源回收
- 进程调度
- 通过批处理调度,与批处理系统的不同点是每个执行的时间不同
- 根据不同的调度策略进行不同思路的调度任务运行
- 可抢占:当应用层优先级高(例如I/O外设),通过时间轮转进行进程切换