BUAA-OS 实验笔记之 Lab6

一、Lab6 前言 操作系统实验的最后一篇笔记,不说什么了。本文主要讲了 Shell 的实现机制,管道通信略有说明。 二、Shell 程序的启动 这次我们还要回到 Init/init.c 文件。我们的 MOS 的所有实验都结束之后,mips_init 函数应该是这样的 void mips_init() { printk("init.c:\tmips_init() is called\n"); // lab2: mips_detect_memory(); mips_vm_init(); page_init(); // lab3: env_init(); // lab6: ENV_CREATE(user_icode); // This must be the first env! // lab5: ENV_CREATE(fs_serv); // This must be the second env! // lab3: kclock_init(); enable_irq(); while (1) { } } 其中我们使用 ENV_CREATE 创建了两个用户进程。这两个进程的代码在编译时便写入了内核 ELF 文件中。其中第二个进程 fs_serv 就是 Lab5 中用到的文件系统服务进程;而第一个进程 user_icode 则是整个操作系统中除文件系统服务进程外所有进程的共同祖先进程,该进程便用于启动 Shell 进程。user_icode 或为 “user init code” 之意。...

五月 19, 2023 · 16 分钟 · 3311 字 · Wokron

奇技淫巧:RNN 求解常微分方程组

一、前言 前一段时间接触常微分方程组拟合的时候,发现了使用 RNN 求解常微分方程组的办法。感觉很有意思,于是记录一下。 参考: 貌离神合的RNN与ODE:花式RNN简介 基于RNN的微分方程拟合 二、常微分方程组和欧拉法 所谓常微分方程,指的是只具有单一自变量的微分方程,如假设加速度 $a$ 一定,则速度 $v$ 满足微分方程 $$ \frac{dv}{dt} = a $$ 速度 $v$ 只与时间 $t$ 有关,那么该微分方程即常微分方程。与之对应的是偏微分方程,不过不在本文的讨论范围内。 类似的,路程 $s$ 也满足微分方程 $$ \frac{ds}{dt} = v $$ 那么关于 $v, s$ 的两个常微分方程就可以组成常微分方程组 $$ \begin{cases} \frac{dv}{dt} = a \\ \frac{ds}{dt} = v \end{cases} $$ 虽然上面的方程组很容易求出解析解,但许多常微分方程组却难以找到解析解,甚至解析解根本不存在。这种情况就需要求出数值解。 还举上面那个简单的物理问题为例,我们设 $t$ 时刻速度和路程为 $v_t, s_t$,那么可以令 $$ \begin{cases} v_{t_m} &= v_{t_{m-1}} + a \Delta t \\ s_{t_m} &= s_{t_{m-1}} + v_{t_m} \Delta t \end{cases} $$ 其中 $t_{m-1} < t_{m}, \Delta t = t_{m} - t_{m-1}$,这样就近似得到了速度和路程随时间变化的数值解。此种方法也是游戏物理引擎中进行运动学模拟的基本方法。因为是欧拉发明的,所以也叫做欧拉法。...

五月 11, 2023 · 5 分钟 · 873 字 · Wokron

BUAA-OS 实验笔记之 Lab5

一、Lab5 前言 这是最长的一篇文章,可就算这么长,文中出现的代码也不过本次 Lab 中新增加的代码的一小部分。幸好完成本次实验不需要熟悉所有代码,一部分练习甚至不需要熟悉要填写的代码的前后文,只需要根据注释就可以填出很多。可是我感觉本篇文章还是有帮助的,毕竟谁也不知道 Exam 会出什么题。 Lab5 主要分为四部分,分别是镜像制作工具、关于设备的系统调用、文件系统服务进程、文件操作库函数。本文对这四个方面都有所涉及,第二章主要讲镜像制作工具,第三章主要讲文件系统服务进程和文件操作库函数,最后一章讲关于设备的系统调用。 二、磁盘镜像 (1)镜像制作工具 在本次实验中我们要实现一个文件系统。广义来说,一切字节序列都可以称为文件,但本次实验中我们还是主要关注在磁盘中存储的数据,将这些数据按一定的结构组织起来,就是本次实验的主要目标。 本文依旧不按照指导书中的顺序。我们先查看位于 tools 文件夹下的磁盘镜像制作工具 fsformat 的源代码,以便我们理解磁盘以及文件系统的组织结构。 (2)磁盘数据初始化 我们查看 tools/fsformat.c 文件。找到其中的 main 函数。main 函数首先调用了 init_disk 用于初始化磁盘。 int main(int argc, char **argv) { static_assert(sizeof(struct File) == BY2FILE); init_disk(); 该函数中我们要用到一个数据结构 disk。因此我们先考察 disk。disk 是一个数组,大小为 NBLOCK,每个元素是一个结构体,其中有字段 data,是一个 BY2BLK 字节大小的空间,用于存储一个磁盘块的数据。很容易得知,NBLOCK * BY2BLK = 磁盘空间大小。这样就可以理解 disk 起到的作用了,也就是在构筑磁盘镜像时暂时存储磁盘数据,等到构筑完成后再将 disk 中 data 的内容拼接并输出为二进制镜像文件。 struct Block { uint8_t data[BY2BLK]; uint32_t type; } disk[NBLOCK]; 磁盘块是对磁盘空间的逻辑划分;扇区是对磁盘空间的物理划分 另外 Block 结构体还有一个字段 type,该字段的值为如下枚举的值 enum { BLOCK_FREE = 0, BLOCK_BOOT = 1, BLOCK_BMAP = 2, BLOCK_SUPER = 3, BLOCK_DATA = 4, BLOCK_FILE = 5, BLOCK_INDEX = 6, }; 让我们回到 init_disk。该函数中首先将第一个磁盘块类型设为 BLOCK_BOOT,表示主引导扇区。之后我们要从第三个磁盘块开始(为什么不是第二个?因为第二个磁盘块为 “超级块”,将在后面介绍),设置磁盘块的位图分配机制。在函数中我们计算了在磁盘中存储位图需要的磁盘块数量。NBLOCK 是磁盘块的总数,那么我们同样需要 NBLOCK bit 大小的位图,又因为一个磁盘块有 BIT2BLK bit,那么总共需要 NBLOCK / BIT2BLK 个磁盘块。向上取整,总共需要 (NBLOCK + BIT2BLK - 1) / BIT2BLK 个磁盘块来存储位图。现在我们已经将 0 到 nbitblock-1 的位图分配了用途,那么下一个空闲的磁盘块就是 nextbno = 2 + nbitblock 了。...

五月 2, 2023 · 23 分钟 · 4790 字 · Wokron

后端开发入门笔记之脚本

一、前言 这篇文章只是记录一些自己编写脚本时用到的零碎知识而已。这也是这一系列的最后一篇。 二、shell 的异常处理 有时我们希望在 shell 脚本命令执行出现异常时进行处理,比如说输出异常情况或退出等等。我们知道发生异常时返回值不为 0,如果是在 c 语言中我们可以这样处理 if (!do_something()) { // handle exception } 类似的在 shell 中 dosomething arg1 arg2 if [ $? -ne 0 ] then # handle exception if 但是这样编写起来太过麻烦了,我们可以采用另一种方法,那就是使用短路逻辑运算符。 我们还先以 c 语言为例,假设现在我们有两个函数,对他们取逻辑或 int r = func1() && func2() 那么在 func1 的返回值为 0 时,就会发生短路,不执行 func2。而在返回值为;而当 func1 的返回值不为 0 时,则会继续执行 func2。我们可以让 func2 完成 func1 的异常处理。 当然,在 c 语言中这种方法很是牵强,因为不同函数的栈帧并不一样,很难跨函数进行处理。可是 shell 中就不同了,所有的变量都是全局变量。 但还需要注意一点,shell 中 0 表示真,1 表示假。(因为 0 表示程序正常结束,所以为真。)所以在 shell 中就需要使用 || 而非 &&。...

四月 25, 2023 · 3 分钟 · 449 字 · Wokron

后端开发入门笔记之 Docker

一、前言 最近事情有些多,这篇短文写写停停,花费了很长时间。本来想着在五一假期结束前完成这一系列文章的,现在看来还有些困难呀。 这篇文章总结了自己使用 Docker 时用到的一些知识,可能内容不深,还请见谅。 二、镜像的构筑 (1)Docker 镜像简介 正如在“准备工作”那篇文章中所说的,镜像时静态的容器,容器是运行的镜像。我们在使用 Docker 时,需要做的就是像编写代码一样构建 Docker 镜像。 Docker 镜像中包含了一套文件系统,其所维护的文件包含了项目运行所需的环境以及项目本身。但是镜像和一般的文件系统不同,镜像由一系列层构成,每一层中存储了对之前镜像的修改信息。 这样做有一定的优点,在下载镜像时,我们不需要每次都下载完整的镜像;而只需要下载与本地镜像不相同的层就可以了。另一方面,容器基于镜像进行构筑,在容器运行时,分层设计也有利于不同容器的共享。采取写时复制技术可以减少内存的占用。 我们进行镜像的构筑时,也不需要从头开始,而可以选择在已有的镜像上添加新的层,通过修改已有环境来构建我们所需的项目运行环境。 (2)Dockerfile 简介 Docker 中使用 Dockerfile 来设置镜像构建的操作流程,这一点类似于 Makefile。这里借我的后端项目中的 Dockerfile 的内容来稍微解释一下。 # python alpine is the smallest python image FROM python:3.9-alpine COPY ./src /src COPY ./start.sh ./ COPY ./config.ini ./ RUN pip install -r ./src/requirements.txt # ash is shell for alpine CMD ash start.sh Dockerfile 中使用一系列大写关键字作为指令,构建镜像时,从头到尾依次执行。 第一句使用了 FROM 指令。该指令指定了构筑时的基础镜像,接下来的指令都是对镜像的修改,会在基础镜像上增加新的层。这里 FROM python:3.9-alpine 指定了一个镜像,来自仓库 python,标签为 3.9-alpine。该镜像包含了 python3.9 的运行环境,底层的操作系统为 alpine,这也是 Linux 的一个发行版,以体积小著称。...

四月 25, 2023 · 2 分钟 · 298 字 · Wokron

后端开发入门笔记之 Flask 简介

一、前言 这一篇文章中主要讲解了为完成后端项目所使用的 Flask 框架。对于自己所使用的功能和特性进行了着重的讨论。当然说是着重讨论,其实还是皮毛罢了。想要更加系统地了解 Flask,还请查看官方文档 二、Flask (1)Flask 介绍 Flask 是一个 Python Web 框架,十分轻量灵活,可以用于开发小型 Web 应用。Flask 高度可扩展,可以通过添加不同的组件来实现定制化的功能。对于简单的后端任务来说,Flask 十分合适。 (2)Flask 用于后端 虽然 Flask 可以实现经典的 MVC 架构,但是本项目只进行后端开发,提供一些用于数据库操作的 RESTful 接口,并不涉及显示的部分。因此只讨论 Flask 用于后端开发的方面。 对于后端项目,我们希望其向下管理数据库,向上为前端提供接口。这就需要用到 ORM 来通过对象管理数据库关系;同时通过 Route 提供 api 接口。 另外,我们还需要对项目进行管理,以合理的结构组织项目。我们需要了解 Flask 的 BluePrint。 三、ORM (1)数据库设置 Flask 使用 Flask-SQLAlchemy 组件实现 ORM。SQLAlchemy 本是独立于 Flask 的 ORM 库,但在 Flask 中使用时,又针对 Flask 进行了一定封装。 我们希望创建到数据库的连接,这需要在后端项目中进行设置。对 Flask 来说,设置以字符串的形式保存于项目的配置变量 SQLALCHEMY_DATABASE_URI 中。 app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = f'mysql+pymysql://{username}:{password}@{host}:{port}/{name}?charset=utf8mb4' 配置存储于 app.config 中。其中 app 是一个 Flask 类型的对象,用于表示 Flask 项目这个整体。...

四月 22, 2023 · 3 分钟 · 604 字 · Wokron