2022-08-05 update
This commit is contained in:
parent
4e3b1c7fd2
commit
8d1ce0043f
|
@ -2,6 +2,8 @@
|
|||
|
||||
总线是电路的通路,它连接了CPU芯片,显卡芯片,内存芯片和I/O控制芯片等。
|
||||
|
||||
不同的总线有不同的I/O适配器家族。比如,磁盘控制器就是一种适配器。
|
||||
|
||||
##### 分类
|
||||
|
||||
- 按用途分类
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
#### 概述
|
||||
|
||||
柱面(cylinker):所有盘片上相同位置的磁道组成了柱面。
|
||||
|
||||
磁头(head):一个盘面只能有一个磁头,所以磁头代表了盘面。一个柱面上的磁道数等于磁头数。
|
||||
|
||||
扇区(secter):一个盘面被划分为多个扇形区域,所以被称为扇区。
|
||||
|
||||
如果每条磁道扇区数都相同,由于每个扇区里存储的字节数是固定的,这使得外层磁道的数据比内层磁道稀疏。为了保存更多的数据,现代硬盘处层磁道比内层磁道有更多的扇区,但IDE驱动器屏蔽了这个差异,从上层的角度来看每条磁道上的扇区数还是相同的。
|
||||
|
||||
磁盘控制器是一个特殊的集成电路,被集成在主板上。磁盘控制器的电路比较简单,因为集成在磁盘里的磁盘驱动器做了大部分的工作。
|
||||
|
||||
控制器可以在两个以上的驱动器上同时寻道,被叫**重叠寻道**(overlapped seeks)。在设计硬盘驱动程序的时候可以考虑使用这一特性。
|
||||
|
||||
在阅读硬盘规范的时候要注意,驱动程序所使用的几何规范和物理格式是不同的。因为最初的IBM PC ROM BIOS对扇区、磁头、柱面的计数分别使用了6比特、4比特、14比特,所以会看到任意硬盘的“建议参数”都是16383个柱面、14个磁头、63个扇区。突破此限制的办法是使用**逻辑块寻址**(logical block addressing, LBA),仅对扇区从0开始编号,不考虑磁盘的几何规格。
|
||||
|
||||
##### RAID
|
||||
|
||||
CPU的性能要远高于磁盘的速度,人们认为并行磁盘I/O有助于提高硬盘性能,于是产生了新的I/O设备**RAID**(独立磁盘冗余阵列)。RAID的基本思想是,把磁盘都放在一个盒子里,用RAID控制器来取代磁盘控制器,通过RAID来复制数据,然后其它操作都是一样的。
|
||||
|
||||
#### 硬盘控制器和硬盘的接口
|
||||
|
||||
假设硬盘有1024个扇区,每个扇区512字节。从硬盘传到硬盘控制器的实际是比特流,这个比特流以**前导符**(preamble)开始,然后是扇区里的4906个比特,最后是一个检验和(也被叫做**纠错码**,ECC)。前导符是在硬盘格式化的时候写入的,包含了柱面号、扇区号、扇区大小这样的信息。
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
正常来考虑,一个不使用系统调用的程序,在被`execve`系统调用执行后,应该不使用任何系统调用。但`musl-gcc`编译出的程序,使用`strace`却看到执行了如下系统调用:
|
||||
|
||||
```
|
||||
set_tid_address
|
||||
brk
|
||||
mmap
|
||||
mprotect
|
||||
exit_group
|
||||
```
|
||||
|
||||
使用`-static`选项,也有如下的系统调用:
|
||||
|
||||
```
|
||||
set_tid_address
|
||||
exit_group
|
||||
```
|
||||
|
|
@ -156,3 +156,38 @@ RAM硬盘的思想就是在内存里保存块,可以快速的访问经常使
|
|||
RAM盘也是由块组成的。当向驱动程序发出消息要读或写一个块的时候,它计算出块的位置直接在内存里读或写。最终通过系统任务调用`phys_copy`实现内存间复制。
|
||||
|
||||
一个RAM盘驱动程序可以支持多个RAM盘,这些RAM盘之间通过次设备号来区分。RAM盘的空间一般应该是分开的,但也可以重叠。
|
||||
|
||||
##### 磁盘驱动程序基本知识
|
||||
|
||||
1. 磁盘臂调度算法,用以优化平均寻道时间
|
||||
|
||||
读写磁盘块的时间由如下三个因素决定:寻道时间,旋转时延,传输时间。其中寻道时延比其它两个要大的多,所以减小寻道时间可以大大提高系统性能。
|
||||
|
||||
- 先来先服务(FCFS):磁盘驱动程序按顺序处理请求。难以优化寻道时间。
|
||||
- 最短寻道优先(SSF):按柱面号索引磁盘请求,总是处理最近的柱面。平均寻道时间得到了优化,问题是柱面两端的极端区域较难到达。
|
||||
- 电梯算法(elevator algorithm):按一个方向运行直到没有请求为止,然后换个方向运动。
|
||||
|
||||
2. 错误处理
|
||||
|
||||
- 程序性错误:解决方法是结束当前磁盘请求。
|
||||
- 校验和错误:解决方法是重复操作几次,还不行就把块标记为**坏块**(bad block)。
|
||||
- 寻道错误:大多数磁盘控制器可以自动修复寻道错误。
|
||||
- 控制器错误:控制器芯片上有一个引脚可以强迫自己复位,磁盘驱动程序可以触发此信号来重置控制器。
|
||||
|
||||
3. 磁道缓存,用以加速磁盘访问
|
||||
|
||||
就是一次旋转把整个磁盘的内容都读出来。如果控制器做了此事,磁盘驱动程序就不必再做了。
|
||||
|
||||
##### 硬盘驱动程序概述
|
||||
|
||||
Minix把不同的硬盘驱动程序都放进了启动镜像,由用户决定来用哪个。
|
||||
|
||||
用户通过向引导监控程序输入参数`albel = AT`强制让Minix3使用IDE磁盘控制器(at_wini)。
|
||||
|
||||
硬盘驱动程序的主循环支持9种请求。
|
||||
|
||||
- DEV_OPEN
|
||||
- DEV_CLOSE
|
||||
- DEV_IOCTL
|
||||
- DEV_READ, DEV_WRITE, DEV_GATHER, DEV_SCATTER
|
||||
- DEV_CANCEL. DEV_SELECTE:被忽略。
|
||||
|
|
|
@ -141,3 +141,9 @@ sudo apt install fcitx5 fcitx5-chinese-addons
|
|||
原因分析:依赖冲突?
|
||||
|
||||
解决方法:`apt install libwacom9 libwacom2-`
|
||||
|
||||
2. 问题描述:`man gcc`没有相关内容
|
||||
|
||||
原因分析:gcc-doc没有安装
|
||||
|
||||
解决方法:`apt instal gcc-doc`
|
||||
|
|
|
@ -54,5 +54,8 @@ git branch -d tmp # 删除tmp分支
|
|||
|
||||
# 2 删除远程追踪的分支
|
||||
git branch -rd origin/tmp # 删除远程仓库origin里的tmp分支
|
||||
|
||||
# 3 查询本地分支追踪的是哪个远程分支
|
||||
git branch -vv
|
||||
```
|
||||
|
||||
|
|
|
@ -47,3 +47,7 @@ git pull --unshallow # 取消浅层复制时的所有限制,使本地仓库成
|
|||
原因分析:第二台电脑的本地仓库和远程仓库存在了冲突
|
||||
|
||||
解决方法:第二台电脑的本地仓库要回退到没有冲突的提交点上再拉取远程仓库。如果子模块也存在着这样的冲突,子模块的提交点也要回退到没有冲突的提交点上再拉取。
|
||||
|
||||
3. ! [rejected] main -> main (non-fast-forward)
|
||||
|
||||
原因分析:发现是本地提交记录和远程提交记录存在冲突。
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#### actions
|
||||
#### github actions
|
||||
|
||||
- [Github Actions 入门教程——阮一峰](http://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html)
|
||||
- [actions的官方市场](https://github.com/marketplace?type=actions)
|
||||
|
@ -89,6 +89,12 @@
|
|||
|
||||
`cache-hit` - 一个布尔值,表示是否为键找到了确切的匹配。
|
||||
|
||||
#### github packages
|
||||
|
||||
#### github API
|
||||
|
||||
#### github webhook
|
||||
|
||||
#### 免密登陆
|
||||
|
||||
[使用ssh免密登陆](https://help.github.com/en/articles/connecting-to-github-with-ssh)
|
||||
|
|
|
@ -9,7 +9,7 @@ Docker镜像和容器命令行接口(CLI)。是通过CLI与daemon交互的,共
|
|||
sudo apt-get install docker docker-engine docker.io # for ubuntu
|
||||
sudo pacman -S docker # for arch
|
||||
|
||||
# 方法二:从第三方仓库安装
|
||||
# 方法二:从第三方仓库安装for ubuntu
|
||||
sudo apt-get install apt-transport-https ca-certificates curl software-properties-common
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
|
||||
sudo apt-key fingerprint 0EBFCD88
|
||||
|
@ -19,6 +19,13 @@ sudo add-apt-repository \
|
|||
$(lsb_release -cs) \
|
||||
stable"
|
||||
sudo apt-get install docker-ce
|
||||
|
||||
# 方法三:从第三方仓库安装for debian
|
||||
sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt-get update
|
||||
sudo apt-get install docker-ce docker-ce-cli containerd.io
|
||||
```
|
||||
|
||||
|
||||
|
@ -432,6 +439,7 @@ binfmt_misc可以模拟执行异架构的二进制程序
|
|||
apt install binfmtc binfmt-support
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset --persistent yes
|
||||
docker run -it riscv64/alpine:edge
|
||||
docker run -it riscv64/debian:sid
|
||||
```
|
||||
|
||||
##### 使用buildx子命令
|
||||
|
|
|
@ -10,6 +10,7 @@ gcc一般会进行预处理,编译,汇编,连接四步。
|
|||
-c # 不运行连接器,这样就只生成汇编器生成的目标文件。
|
||||
-S|-E #
|
||||
-specs=<file> # 编译器读取标准specs文件后再处理<file>,用来覆盖传给cc1,cc1plus,as,ld等的默认开关。
|
||||
-v # 打印各个执行阶段所运行的命令。
|
||||
```
|
||||
|
||||
##### C语言选项
|
||||
|
@ -57,6 +58,8 @@ gcc一般会进行预处理,编译,汇编,连接四步。
|
|||
##### 优化选项
|
||||
|
||||
```
|
||||
-ffuction-sections
|
||||
-fdata-sections
|
||||
-fomit-frame-pointer # 在不需要frame指针的函数中忽略frame指针。
|
||||
-fno-omit-frame-pointer # 看起来与-fomit-frame-pointer相反。
|
||||
-foptimize-sibling-calls # 优化同级和尾部递归调用。
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
#### 标题
|
||||
|
||||
| zCore测试代码解析 |
|
||||
| :----------------------------------------------------------: |
|
||||
| 清华大学计算机系工程师<br>负责OS测例的维护与开发<br>本文档地址:[https://github.com/shzhxh/computer_knowledge_notes/blob/master/others/zCore测试代码解析.md]() |
|
||||
|
||||
#### 需求分析
|
||||
|
||||
1. 我写了一个操作系统,怎么验证实现是正确的?
|
||||
- zCore支持Zircon和Linux两套系统调用
|
||||
- zCore支持libos和bare两种模式
|
||||
- zCore支持X64, AArch64, Riscv64三种架构
|
||||
- zCore支持Qemu和多种开发板等硬件环境
|
||||
2. 多人向操作系统贡献代码,怎么保证代码质量?
|
||||
- 语言级别的检查,格式与语法检查
|
||||
- 对系统调用的检查,新的提交不能让原先没问题的测例报错
|
||||
|
||||
#### 设计分析
|
||||
|
||||
1. core-test测试Zircon系统调用,libc-test和oscomp测试Linux系统调用。
|
||||
- core-test是对Zircon内核的功能进行测试,详见fuchsia源码的`zircon/system/utest/core`目录。
|
||||
- libc-test是对Musl标准库进行的测试,源码在https://github.com/rcore-os/libc-test。
|
||||
- oscomp是全国大学生能力大赛里测试系统调用的测例,源码在https://github.com/oscomp/testsuits-for-oskernel/tree/main/riscv-syscalls-testing
|
||||
|
||||
2. 利用github action进行CI测试,新的提交不能让原先没问题的测例报错
|
||||
|
||||
- 在`.github/workflows/*.yml`使用如下配置可以在推送或提交pr的时候执行指定的操作,详见https://docs.github.com/cn/actions/using-workflows/triggering-a-workflow
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
```
|
||||
|
||||
|
||||
|
||||
3. 利用Makefile, xtask, cargo实现基本的操作
|
||||
|
||||
- xtask
|
||||
|
||||
`.cargo/config.toml`里指明了`cargo xxx`命令其实对应的`cargo run --package xtask --release --xxx`命令。
|
||||
|
||||
xtask包的源码在`./xtask`目录。
|
||||
|
||||
- `./Makefile`只是对`xtask`命令的直接封装,`./zCore/Makefile`主要用来编译、运行、调试内核,但在顶层的Makefile已提供了相应的功能。
|
||||
|
||||
- cargo
|
||||
|
||||
是对rust包进行管理的命令,xtask或Makefile最终都要调用它。
|
||||
|
||||
4. 在独立的仓库里进行自动化测试
|
||||
- 系统工程师不能即当运动员,又当裁判员。要搞定问题,而不是搞定测例。
|
||||
- 新编写的测例可以放在此仓库。
|
||||
- 系统工程师一般不需要主动修改测例的状态。如果一个测例不应该通过,应该设置内核不要让它通过,而不是把测例状态改为不通过。
|
||||
|
||||
#### 实现介绍
|
||||
|
||||
1. xtask的介绍
|
||||
|
||||
```rust
|
||||
// 使用clap库进行命令行参数的解析
|
||||
// main函数是入口,它通过match命令来分发子命令。
|
||||
// 子命令rootfs, libc-test, other-test都被分发给arg.linux_rootfs().xxx()
|
||||
// linux_rootfs()的作用是指定架构
|
||||
// xxx()函数做具体的工作
|
||||
// 1. 确保基本镜像目录的存在(下载交叉工具链,编译busybox, 复制动态链接库)
|
||||
// 2. 如果是二进制文件直接下载并解压
|
||||
// 3. 如果是源文件则把它编译成二进制文件
|
||||
// 子命令image被分发给arg.linux_rootfs().image()
|
||||
// image()函数做具体的工作
|
||||
// 1. 确保镜像目录存在
|
||||
// 2. 使用fuse命令从镜像目录生成镜像文件
|
||||
```
|
||||
|
||||
|
||||
|
||||
2. 两个Makefile的对比
|
||||
|
||||
| zCore子目录下的操作 | 顶层目录下的操作 |
|
||||
| --------------------------------------------------------- | ------------------------------------------------------------ |
|
||||
| make run LINUX=1 ARCH=riscv64 MODE=release | cargo qemu --arch riscv64 |
|
||||
| make run_d1 PLATFORM=d1 ARCH=riscv64 LINUX=1 MODE=release | cargo bin --arch riscv64 --features "linux board-d1 link-user-img" --output zcore_d1.bin |
|
||||
|
||||
|
||||
|
||||
3. 自动化测试的脚本
|
||||
|
||||
1. **utils.load_testcases函数**
|
||||
|
||||
1. core_test的全部测例放在zircon_core_test里,通过架构(目前仅x86_64)和运行模式(libos/bare)分类。每个测例还包含了运行结果的信息(OK/FAILED/TIMEOUT),这样已通过和未通过的测例就可以在一个文件里表示了。
|
||||
2. libc_test的全部测例放在linux_libc_test里,通过架构(x86_64/riscv64)和运行模式(libos/bare)分类。每个测例还包含了运行结果的信息(OK/FAILED/TIMEOUT),这样已通过和未通过的测例就可以在一个文件里表示了。
|
||||
3. 通过`load_testcases()`提取测例,它定义在`test.py`。工作过程:就是从测例文件中提取出二元组(testcase, TestStatus)。
|
||||
|
||||
2. **utils.TestRunner类**
|
||||
|
||||
```python
|
||||
# 创建对象self的时候会创建对象self.logger
|
||||
def __init__(self)
|
||||
# 销毁对象的时候执行teardown()方法。但实际上不做任何事情。
|
||||
def __del__(self)
|
||||
# 生成命令行要执行的构建命令。(注:需要重载)
|
||||
def build_cmdline(self)
|
||||
# 生成命令行要执行的测例命令。(注:需要重载)
|
||||
def run_cmdline(self)
|
||||
# 执行构建命令。最终通过subprocess.run()实现。
|
||||
def build(self)
|
||||
# 遍历testcases,依次执行测例
|
||||
def run_all(self, testcases, fast, timeout) -> bool
|
||||
```
|
||||
|
||||
3. **log.Logger类**
|
||||
|
||||
```python
|
||||
# 创建对象self的时候打开文件log_file_name
|
||||
def __init__(self, log_file_name)
|
||||
# 销毁对象的时候关闭文件
|
||||
def __del__(self)
|
||||
# 把message输出到标准输出和文件
|
||||
def print(self, message)
|
||||
# self.print(message+"\n")
|
||||
def println(self, message)
|
||||
```
|
||||
|
||||
4. **core test执行流程**
|
||||
|
||||
1. 解析参数。
|
||||
|
||||
- -l,则args.libos为True。
|
||||
- -f,则args.fast为True。
|
||||
- -t,则args.test为True。
|
||||
|
||||
2. 全局变量赋值
|
||||
|
||||
3. 创建ZirconTestRunner对象, 继承自TestRunner
|
||||
|
||||
```python
|
||||
BASE_CMD = "cd ./zCore && make MODE=release ZBI=core-tests TEST=1"
|
||||
# 如定义args.libos则返回self.BASE_CMD + "LIBOS=1"
|
||||
# 如未定义args.libos则返回self.BASE_CMD
|
||||
def build_cmdline(self) -> str
|
||||
# 如定义args.libos则返回"../target/release/zcore $(ZBI_PATH) $(CMDLINE_BASE)+name"
|
||||
# 如未定义args.libos则返回self.BASE_CMD+"CMDLINE=$(CMDLINE_BASE)+name"+"justrun"
|
||||
def run_cmdline(self, name) -> str
|
||||
```
|
||||
|
||||
|
||||
|
||||
4. 执行runner.build构建内核
|
||||
|
||||
```bash
|
||||
cd zCore
|
||||
make MODE=release ZBI=core-tests TEST=1 LIBOS=1 # if args.libos
|
||||
make MODE=release ZBI=core-tests TEST=1 # if not args.libos
|
||||
```
|
||||
|
||||
|
||||
|
||||
5. 如定义了args.test,则执行runner.run_one()只执行一个测例。
|
||||
|
||||
```bash
|
||||
# if args.libos
|
||||
../target/release/zcore ../prebuilt/zircon/x64/core-tests.zbi LOG=error:userboot=test/core-standalone-test:userboot.shutdown:core-tests=<testcase>
|
||||
# if not args.libos
|
||||
make MODE=release ZBI=core-tests TEST=1 CMDLINE='LOG=error:userboot=test/core-standalone-test:userboot.shutdown:core-tests=<testcase>' justrun
|
||||
```
|
||||
|
||||
|
||||
|
||||
6. 如没有定义args.test,则执行runner.run_all()。
|
||||
|
||||
```bash
|
||||
runner.set_logger() # 打开日志文件zircon_core_test_<arch>_<mode>.log,这样就可以将信息写入日志了
|
||||
load_testcases() # 从testcases/zircon_core_test/<arch>_<mode>.txt文件中读取测例
|
||||
|
||||
# 接下来是runner.runall()
|
||||
# 1. 在for循环中遍历所有测例,通过self.run_one()执行每个测例
|
||||
# 2. for循环执行完毕,执行print_result()打印结果
|
||||
```
|
||||
|
||||
5. **libc test执行流程**
|
||||
|
||||
1. 解析参数
|
||||
|
||||
2. 全局变量赋值
|
||||
|
||||
3. 创建LinuxTestRunner对象
|
||||
|
||||
4. 执行runner.build构建内核
|
||||
|
||||
```bash
|
||||
# if args.libos
|
||||
cd ../zCore && make MODE=release LINUX=1 TEST=1 ARCH=<arch> LIBOS=1
|
||||
# if not args.libos
|
||||
cd ../zCore && make MODE=release LINUX=1 TEST=1 ARCH=<arch>
|
||||
```
|
||||
|
||||
5. 如定义了args.test则只运行一个测例
|
||||
|
||||
```bash
|
||||
# if args.libos
|
||||
cd .. && LOG=warn ./target/release/zcore <testcase>
|
||||
|
||||
# if not args.libos
|
||||
cd ../zCore
|
||||
make MODE=release LINUX=1 TEST=1 ARCH=<arch> CMDLINE='LOG=warn:ROOTPROC=<testcase>' justrun
|
||||
```
|
||||
|
||||
|
||||
|
||||
6. 如没有定义args.test则运行常规测试或全部测试
|
||||
|
||||
```bash
|
||||
runner.setlog() # 打开日志文件linux_libc_test_<arch>_<mode>.log,用来记录日志
|
||||
load_testcases() # 把所有测例都保存在一个列表里
|
||||
|
||||
# runner.runall
|
||||
# 1. 在for循环中遍历所有测例,通过self.run_one()执行每个测例
|
||||
# 2. for循环执行完毕,执行print_result()打印结果
|
||||
```
|
||||
|
||||
|
||||
|
||||
4. github action脚本
|
||||
|
||||
1. 一个仓库的action包含一个以上的workflow,一个workflow表现为一个yml文件,每个workflow都有各自的触发条件。
|
||||
2. 一个workflow包含一个以上的job,job之间是并行的,job内部是串行的。一个workflow就是具有共同目标的job的集合。
|
||||
3. 一个job包含若干step,一个step包含若干action.action就是组成step的命令。
|
||||
4. github action里可以缓存文件,这将大大地降低测试时间。
|
||||
|
||||
|
||||
#### 思考不足
|
||||
|
||||
1. xtask在报错后定位问题困难
|
||||
|
||||
可能是系统里缺少某个命令
|
||||
|
||||
可能是依赖的文件或目录没有下载好
|
||||
|
||||
2. 标准库的运行失败了,但测例通过了
|
||||
|
||||
libc-test是为测试标准库而服务的,它假定系统调用是没问题的。然而测系统调用的时候,应该假定系统调用是有问题的。
|
||||
|
||||
#### 改进思路
|
||||
|
||||
不是测标准库,而是测系统调用。
|
||||
|
||||
oscomp的设计思想是做一层系统调用的接口,基于此接口编译应用程序就可以测试指定的系统调用。
|
||||
|
||||
oscomp的问题是只能测一种架构。oscomp里测例可以进一步发展,基于标准库,但严格地控制只使用最低层的系统调用及系统调用无关的函数。应该可以最大限度地测系统调用,且一套代码测多个平台。
|
Loading…
Reference in New Issue