diff --git a/Dockerfile b/Dockerfile index 87c3ce9b185f222e4eb16e63ecbda83399f17da2..5ab85dda630994cd7463be3bc6bcde8846ab917b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,19 +11,35 @@ Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF RUN apt update && \ - apt install -y node-corepack nodejs + apt install -y node-corepack nodejs curl build-essential chromium + +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" RUN corepack enable pnpm COPY ./ /src/ WORKDIR /src/ +RUN cargo install mdbook mdbook-pdf && \ + mdbook --version && \ + mdbook build /src/mdbook/eulixos-camp-book-stage0-docs && \ + mdbook build /src/mdbook/eulixos-camp-book-stage1-docs && \ + mdbook build /src/mdbook/eulixos-camp-book-stage2-docs + RUN pnpm install RUN pnpm run build +RUN mkdir -p /usr/share/nginx/html/eulixos-camp-book-stage0-docs \ + /usr/share/nginx/html/eulixos-camp-book-stage1-docs \ + /usr/share/nginx/html/eulixos-camp-book-stage2-docs + FROM isrc.iscas.ac.cn/learningeulixos/nginx:latest COPY --from=builder /src/public /usr/share/nginx/html +COPY --from=builder /src/mdbook/eulixos-camp-book-stage0-docs/book/html /usr/share/nginx/html/eulixos-camp-book-stage0-docs +COPY --from=builder /src/mdbook/eulixos-camp-book-stage1-docs/book /usr/share/nginx/html/eulixos-camp-book-stage1-docs +COPY --from=builder /src/mdbook/eulixos-camp-book-stage2-docs/book /usr/share/nginx/html/eulixos-camp-book-stage2-docs EXPOSE 80 diff --git a/mdbook/eulixos-camp-book-stage0-docs/.gitignore b/mdbook/eulixos-camp-book-stage0-docs/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7585238efedfc33acdd9494b0269951aaf3909ec --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/mdbook/eulixos-camp-book-stage0-docs/book.toml b/mdbook/eulixos-camp-book-stage0-docs/book.toml new file mode 100644 index 0000000000000000000000000000000000000000..9db0664b9a09e0c168539450708821ef33fa1d1d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/book.toml @@ -0,0 +1,10 @@ +[book] +authors = ["XUKUNYUAN"] +language = "chinese" +multilingual = false +src = "src" +title = "第一期傲来操作系统训练营导学阶段引导" + +[output.html] + +[output.pdf] \ No newline at end of file diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/SUMMARY.md b/mdbook/eulixos-camp-book-stage0-docs/src/SUMMARY.md new file mode 100644 index 0000000000000000000000000000000000000000..f67b0ddd6a60026ab15897a1d015cf155c8a82a2 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/SUMMARY.md @@ -0,0 +1,44 @@ +# Summary + +- [前言](foreword.md) +- [介绍](introduce.md) +- [环境配置](setup.md) +- [第一章:导学阶段基本信息](ch1-00.md) + - [训练营教学系统使用引导](ch1-01.md) + - [导学阶段视频课程链接与学习引导](ch1-02.md) + - [导学课程学习资料汇总](ch1-03.md) + - [开营仪式教学安排与资料汇总](ch1-04.md) + - [教学系统常见问题与解决方案](ch1-05.md) +- [第二章:基本 Git 使用与 Gitee 流水线入门](ch2-00.md) + - [常用 Git 指令](ch2-01.md) + - [Gitee 使用入门](ch2-02.md) + - [Gitee Go 的配置与使用](ch2-03.md) + - [流水线常见问题与解决方案](ch2-04.md) +- [第三章:前置知识](ch3-00.md) + - [Linux 使用入门与配置](ch3-01.md) + - [C 语言基本语法](ch3-02.md) + - [make 基本使用与 Makefile](ch3-03.md) + - [如何在非 Linux 环境中完成实验](ch3-04.md) +- [第四章:开始实验](ch4-00.md) + - [实验代码框架讲解](ch4-01.md) + - [本地测试流程](ch4-02.md) + - [实验相关资料汇总](ch4-03.md) + - [第一题:输出 Hello World!](ch4-04.md) + - [第二题:基本循环](ch4-05.md) + - [第三题:打印九九乘法表](ch4-06.md) + - [第四题:求素数](ch4-07.md) + - [第五题:约瑟夫环](ch4-08.md) + - [导学阶段实验相关问题与解决方案](ch4-09.md) +- [第五章:提交成绩](ch5-00.md) + - [流水线测试过程](ch5-01.md) + - [排行榜成绩查看与绑定](ch5-02.md) + - [如何晋级](ch5-03.md) + - [提交过程相关问题与解决方案](ch5-04.md) +- [第六章:晋级之后](ch6-00.md) + - [拓展内容汇总](ch6-01.md) + - [后续学习指引](ch6-02.md) + - [部分优质实践资源](ch6-03.md) +- [其余常见问题与解决方案](problem.md) +- [附录A:使用 Gitee 流水线信息进行调试](ap-A.md) +- [附录B:实验所用到的其余专业前置知识](ap-B.md) +- [附录C:部分工具的使用入门](ap-C.md) diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ap-A.md b/mdbook/eulixos-camp-book-stage0-docs/src/ap-A.md new file mode 100644 index 0000000000000000000000000000000000000000..627bb145a5e46d9ee0ffc86b0a1633da48acf567 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ap-A.md @@ -0,0 +1,50 @@ +# 附录A:使用 Gitee 流水线信息进行调试 + +在软件开发过程中,持续集成和持续部署(CI/CD)是确保代码质量和加快交付速度的关键环节。Gitee 提供了流水线服务,帮助开发者自动化构建、测试和部署流程。当流水线出现问题时,如何有效地进行调试成为了一个重要课题。本章节将介绍如何使用 Gitee 流水线信息进行调试。 + +## 理解 Gitee 流水线 + +在开始调试之前,首先需要理解 Gitee 流水线的基本概念和工作流程。Gitee 流水线基于 Git 仓库的提交触发,可以配置多个阶段,每个阶段包含一系列任务。这些任务可以是编译代码、运行测试、部署应用等。 + +## 获取流水线信息 + +当流水线执行失败时,第一步是获取相关的流水线信息。在 Gitee 仓库的流水线页面,你可以看到流水线的执行历史,包括每个阶段的执行状态和日志。点击具体的流水线执行记录,可以查看详细的日志信息。 + +### 查看日志 + +日志是调试流水线问题的关键。Gitee 流水线提供了详细的日志输出,包括每个任务的输入、输出和错误信息。通过分析日志,你可以定位到问题所在。 + +### 检查配置 + +流水线的配置文件定义了流水线的行为。检查配置文件是否正确,包括环境变量、脚本命令、依赖项等,是调试的重要步骤。 + +## 调试步骤 + +### 1. 重现问题 + +如果可能,尝试重现流水线失败的情况。这通常意味着你需要模拟相同的提交或触发条件。 + +### 2. 缩小问题范围 + +通过分析日志和配置,尝试缩小问题的范围。确定是哪个任务或脚本导致了问题。 + +### 3. 本地测试 + +在本地环境中复现流水线中的任务。这可以帮助你验证假设,并确定问题是否与环境有关。 + +### 4. 修改配置 + +根据调试结果,修改流水线配置。这可能包括更新脚本、修复依赖项或调整环境变量。 + +### 5. 重新触发流水线 + +提交更改后,重新触发流水线。观察新的执行结果,确认问题是否得到解决。 + +## 使用 Gitee 流水线调试工具 + +Gitee 流水线提供了一些工具来帮助调试: + +- **环境变量**:在流水线配置中设置环境变量,可以在任务中使用这些变量进行调试。 +- **缓存**:合理使用缓存可以加快流水线执行速度,减少因依赖安装导致的失败。 +- **自定义脚本**:编写自定义脚本进行更复杂的调试操作。 + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ap-B.md b/mdbook/eulixos-camp-book-stage0-docs/src/ap-B.md new file mode 100644 index 0000000000000000000000000000000000000000..7d64affa81059a33568d0025ae2f7109bd0918a7 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ap-B.md @@ -0,0 +1,153 @@ +# 附录B:实验所用到的其余专业前置知识 + +### C++ STL + +C++ 标准模板库(Standard Template Library,STL)是 C++ 标准库的一部分,提供了一系列的模板类和函数,用于实现常用的数据结构和算法。STL 的设计理念是“泛型编程”,即通过模板技术使得算法和数据结构可以独立于具体的数据类型。STL 主要包含以下几个组件: + +#### 1. 容器(Containers) + +容器是用来存储数据的对象,STL 提供了多种容器,每种容器都有其特定的用途和性能特点。常见的容器包括: + +- **向量(vector)**:动态数组,支持随机访问。 +- **列表(list)**:双向链表,支持高效的插入和删除操作。 +- **双端队列(deque)**:双端队列,支持两端高效插入和删除。 +- **集合(set)**:存储不重复元素的集合,内部通常使用平衡二叉树实现。 +- **多重集合(multiset)**:与 set 类似,但允许元素重复。 +- **映射(map)**:键值对的集合,内部通常使用平衡二叉树实现。 +- **多重映射(multimap)**:与 map 类似,但允许键重复。 +- **栈(stack)**:后进先出(LIFO)的数据结构。 +- **队列(queue)**:先进先出(FIFO)的数据结构。 +- **优先队列(priority_queue)**:元素出队顺序由优先级决定。 + +#### 2. 迭代器(Iterators) + +迭代器是连接算法和容器的桥梁,它提供了一种统一的方式来访问容器中的元素。STL 中的迭代器分为几种类型,包括输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器。 + +#### 3. 算法(Algorithms) + +STL 提供了一系列的算法,这些算法可以作用于不同类型的容器。常见的算法包括排序(sort)、搜索(find)、合并(merge)、复制(copy)、替换(replace)等。这些算法通常都是通过迭代器来操作容器中的元素。 + +#### 4. 函数对象(Function Objects) + +函数对象,也称为仿函数,是重载了函数调用操作符 `()` 的类对象。它们可以像普通函数一样被调用,同时也可以保存状态或者进行类型检查。STL 中的许多算法都接受函数对象作为参数,以实现自定义的行为。 + +#### 5. 适配器(Adapters) + +适配器是一种特殊的容器或函数对象,它们通过改变其他容器或函数对象的接口来提供新的接口。例如,栈和队列就是通过适配器实现的,它们分别基于向量和双端队列,但提供了不同的接口。 + +6. #### **分配器(Allocators)** + +分配器负责管理容器中元素的内存分配和释放。STL 默认使用 `std::allocator`,但用户也可以自定义分配器以满足特定的内存管理需求。 + +STL 的设计使得数据结构和算法的实现与具体的数据类型无关,这大大提高了代码的复用性和灵活性。通过使用 STL,开发者可以更加专注于算法的设计和实现,而不必重复编写基本的数据结构和算法。 + +[菜鸟教程 C++](https://www.runoob.com/cplusplus/cpp-tutorial.html) + +### Shell + +Shell 是一种命令行解释器,它为用户提供了一个与操作系统内核进行交互的界面。通过 shell,用户可以输入命令,执行程序,管理文件和系统等。Shell 不仅是一个命令解释器,还是一个强大的编程环境,允许用户编写脚本来自动化复杂的任务。 + +#### 常见的 Shell 类型 + +1. **Bash (Bourne Again SHell)**:这是最常用的 shell,它是 GNU 项目的一部分,广泛用于 Linux 和 macOS 系统。 +2. **sh (Bourne Shell)**:由 Stephen Bourne 在 AT&T 开发,是许多其他 shell 的基础。 +3. **csh (C Shell)**:语法类似于 C 语言,提供了一些额外的功能,如命令历史和别名。 +4. **tcsh (TENEX C Shell)**:csh 的增强版本,增加了命令行编辑和命令补全等功能。 +5. **zsh (Z Shell)**:一个功能强大的 shell,集成了许多其他 shell 的特性,并提供了许多增强功能。 +6. **fish (Friendly Interactive SHell)**:设计用于提供用户友好的体验,包括自动建议和语法高亮。 + +#### Shell 的基本功能 + +1. **命令执行**:用户可以直接在 shell 中输入命令,shell 会解释并执行这些命令。 +2. **文件系统导航**:使用 `cd`、`ls`、`pwd` 等命令来浏览和管理文件系统。 +3. **重定向和管道**:允许用户将命令的输入输出重定向到文件或其他命令,使用 `|` 符号可以将一个命令的输出作为另一个命令的输入。 +4. **环境变量**:用户可以设置环境变量来影响命令的行为,例如 `PATH` 变量定义了命令的搜索路径。 +5. **脚本编程**:用户可以编写 shell 脚本来执行一系列命令,脚本可以包含条件判断、循环、函数等编程结构。 +6. **作业控制**:用户可以启动、停止、恢复和终止后台作业。 + +#### Shell 脚本编程 + +Shell 脚本是一种简单的脚本语言,它允许用户将一系列命令组合成一个文件,以便自动执行。脚本可以包含变量、控制结构(如 `if`、`for`、`while`)、函数和命令。以下是一个简单的 Bash 脚本示例: + +```shell +#!/bin/bash +# 这是一个简单的 Bash 脚本 + +echo "Hello, World!" + +# 使用变量 +name="Alice" +echo "Hello, $name!" + +# 使用条件判断 +if [ "$name" == "Alice" ]; then + echo "Welcome, Alice!" +fi + +# 使用循环 +for i in {1..5}; do + echo "Count: $i" +done +``` + +要运行这个脚本,首先需要给它执行权限,然后可以直接运行: + +```shell +chmod +x script.sh +./script.sh +``` + +Shell 是系统管理员和开发人员的重要工具,它提供了强大的自动化和定制能力。通过学习和使用 shell,用户可以更高效地管理和操作计算机系统。 + +### YAML + +YAML("YAML Ain't Markup Language" 的递归缩写)是一种人类可读的数据序列化标准,广泛用于配置文件、数据交换、对象持久化等领域。YAML 的设计目标是使数据在不同编程语言之间交换变得简单,同时保持良好的可读性和简洁性。 + +#### YAML 的基本语法 + +1. **缩进**:YAML 使用缩进来表示数据结构中的层次关系。通常使用空格(推荐使用两个空格),不建议使用制表符。 +2. **冒号**:用于表示键值对,冒号后面通常跟着一个空格。 +3. **短横线**:用于表示列表项,短横线后面通常跟着一个空格。 +4. **注释**:使用井号 `#` 表示单行注释。 +5. **引用**:可以使用 `&` 定义锚点,使用 `*` 引用锚点。 +6. **字符串**:字符串不需要引号,但如果包含特殊字符或需要跨行,可以使用单引号或双引号。 +7. **布尔值**:可以使用 `true`、`false`、`yes`、`no`、`on`、`off` 等。 +8. **空值**:使用 `null` 或 `~` 表示。 + +#### YAML 示例 + +以下是一个简单的 YAML 文件示例: + +```yaml +# 这是一个 YAML 示例文件 + +name: John Doe +age: 30 +is_student: false +hobbies: + - reading + - hiking + - coding +contact: + email: john.doe@example.com + phone: 123-456-7890 +address: + street: 123 Main St + city: Anytown + state: CA + zip: 12345 +``` + +在这个示例中,`name`、`age` 和 `is_student` 是键值对,`hobbies` 是一个列表,`contact` 和 `address` 是嵌套的键值对。 + +#### YAML 的应用 + +YAML 常用于配置文件,因为它比 XML 和 JSON 更加简洁和易读。许多流行的软件和框架,如 Docker、Kubernetes、Ansible、Spring Boot 等,都使用 YAML 作为配置文件格式。 + +#### 注意事项 + +- YAML 文件不区分大小写,但通常约定使用小写字母。 +- 避免在 YAML 文件中使用特殊字符,除非它们被正确转义。 +- 在处理 YAML 文件时,确保使用支持 YAML 标准的解析器。 + +YAML 是一种灵活且强大的数据序列化格式,它使得配置文件和数据交换更加简单和直观。通过学习和使用 YAML,开发者可以更有效地管理配置和数据。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ap-C.md b/mdbook/eulixos-camp-book-stage0-docs/src/ap-C.md new file mode 100644 index 0000000000000000000000000000000000000000..d3840a63b111179631849cdc562d611689526ecc --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ap-C.md @@ -0,0 +1,159 @@ +# 附录C:部分工具的使用入门 + +### vscode + +Visual Studio Code(简称 VS Code)是一个由微软开发的免费、开源的代码编辑器。它支持多种编程语言,并且可以通过安装扩展来增强其功能。VS Code 因其强大的功能、高度的可定制性和良好的用户体验而广受欢迎。以下是 VS Code 的一些基本使用方法: + +#### 安装 VS Code + +1. 访问 VS Code 官网(https://code.visualstudio.com/)。 +2. 根据你的操作系统(Windows、macOS、Linux)下载对应的安装包。 +3. 按照安装向导完成安装。 + +#### 界面概览 + +VS Code 的界面主要包括以下几个部分: + +- **菜单栏**:位于顶部,包含文件、编辑、查看等菜单。 +- **侧边栏**:包含资源管理器、搜索、源代码管理、运行和调试、扩展等视图。 +- **编辑区**:主要的工作区域,用于编辑代码。 +- **状态栏**:位于底部,显示当前文件的信息、语言模式、缩进等。 + +#### 基本操作 + +1. **打开文件或文件夹**:通过“文件”菜单或快捷键 `Ctrl+O`(Windows/Linux)或 `Cmd+O`(macOS)打开文件,通过“文件”菜单或快捷键 `Ctrl+K Ctrl+O` 打开文件夹。 +2. **编辑代码**:在编辑区中直接输入或修改代码。 +3. **保存文件**:通过“文件”菜单或快捷键 `Ctrl+S`(Windows/Linux)或 `Cmd+S`(macOS)保存文件。 +4. **代码折叠**:使用快捷键 `Ctrl+Shift+[` 和 `Ctrl+Shift+]` 折叠和展开代码块。 +5. **代码导航**:使用快捷键 `Ctrl+Tab` 在打开的文件之间切换,使用 `Ctrl+` 或 `Cmd+` 点击函数或变量跳转到定义。 + +#### 使用扩展 + +VS Code 的强大之处在于其丰富的扩展生态。你可以通过以下步骤安装和管理扩展: + +1. 点击侧边栏的“扩展”图标或使用快捷键 `Ctrl+Shift+X`。 +2. 在搜索框中输入扩展名称,找到后点击“安装”。 +3. 安装后,扩展会自动启用,你也可以在扩展管理页面进行配置或卸载。 + +#### 调试代码 + +VS Code 内置了调试功能,可以用来调试多种语言的代码: + +1. 点击侧边栏的“运行和调试”图标或使用快捷键 `Ctrl+Shift+D`。 +2. 点击“创建 launch.json”文件来配置调试环境。 +3. 设置断点,然后点击“开始调试”按钮或使用快捷键 `F5`。 + +#### 其他功能 + +- **终端**:通过“查看”菜单或快捷键 `Ctrl+` 打开集成终端,可以在不离开编辑器的情况下执行命令。 +- **版本控制**:VS Code 内置了 Git 支持,可以通过侧边栏的“源代码管理”视图进行版本控制操作。 +- **任务运行**:可以配置任务来自动化编译、测试等操作,通过“终端”菜单或快捷键 `Ctrl+Shift+B` 运行任务。 + +### vim/neovim + +Vim(Vi IMproved)和 Neovim 是两个流行的文本编辑器,它们都是从 Vi 编辑器发展而来的。Vim 和 Neovim 都以其强大的功能、高度的可定制性和高效的键盘操作而闻名。以下是 Vim 和 Neovim 的一些基本使用方法: + +#### 启动 Vim/Neovim + +在命令行中输入 `vim` 或 `nvim` 即可启动 Vim 或 Neovim。如果想要编辑一个文件,可以在命令后面加上文件名,例如 `vim file.txt` 或 `nvim file.txt`。 + +#### 模式 + +Vim/Neovim 有几种不同的模式: + +1. **普通模式(Normal Mode)**:启动 Vim/Neovim 后默认进入的模式,用于导航、执行命令和切换到其他模式。 +2. **插入模式(Insert Mode)**:用于输入文本,通过按 `i`(插入)、`a`(追加)等键进入。 +3. **命令行模式(Command-line Mode)**:用于执行命令,如保存文件、搜索替换等,通过按 `:` 进入。 +4. **可视模式(Visual Mode)**:用于选择文本,通过按 `v`(字符可视)、`V`(行可视)或 `Ctrl+v`(块可视)进入。 + +#### 基本操作 + +1. **移动光标**:在普通模式下,使用方向键或 `h`(左)、`j`(下)、`k`(上)、`l`(右)移动光标。 +2. **插入文本**:在普通模式下按 `i` 进入插入模式,输入文本后按 `Esc` 返回普通模式。 +3. **保存和退出**:在普通模式下按 `:` 进入命令行模式,输入 `w`(保存)、`q`(退出)或 `wq`(保存并退出)。 +4. **删除文本**:在普通模式下,使用 `x`(删除字符)、`dd`(删除行)等命令。 +5. **撤销和重做**:在普通模式下,使用 `u`(撤销)和 `Ctrl+r`(重做)。 + +#### 高级操作 + +1. **搜索和替换**:在命令行模式下,使用 `/` 进行搜索,使用 `:%s/old/new/g` 进行全局替换。 +2. **复制和粘贴**:在普通模式下,使用 `yy`(复制行)、`p`(粘贴)等命令。 +3. **多窗口**:在命令行模式下,使用 `:split` 或 `:vsplit` 创建新窗口。 +4. **宏录制**:在普通模式下,使用 `q` 开始录制宏,使用 `q` 停止录制,使用 `@` 播放宏。 + +#### 配置 + +Vim/Neovim 可以通过配置文件进行高度定制。Vim 的配置文件通常是 `~/.vimrc`,而 Neovim 的配置文件是 `~/.config/nvim/init.vim`。你可以在这些文件中设置选项、映射快捷键、加载插件等。 + +#### 插件管理 + +Vim/Neovim 支持通过插件扩展功能。对于 Vim,可以使用 Vundle、Pathogen 等插件管理器。对于 Neovim,可以使用 vim-plug、dein.vim 等。安装插件管理器后,你可以在配置文件中列出想要安装的插件,然后运行相应的命令来安装它们。 + +#### 学习资源 + +- **`:help`**:在 Vim/Neovim 中输入 `:` 进入命令行模式,然后输入 `help` 可以查看帮助文档。 +- **在线教程**:有许多在线资源和教程可以帮助你学习 Vim/Neovim,例如 Vim 的官方网站(https://www.vim.org/)和 Neovim 的 GitHub 页面(https://github.com/neovim/neovim)。 + +Vim/Neovim 的学习曲线可能比较陡峭,但一旦掌握了基本操作和一些高级技巧,你将能够以非常高效的方式编辑文本和代码。随着实践的深入,你会发现 Vim/Neovim 的强大之处,并能够根据自己的需求进行定制。 + +### Clion + +CLion 是由 JetBrains 开发的一款跨平台的集成开发环境(IDE),专为 C 和 C++ 开发而设计。它提供了代码编辑、调试、项目管理、代码分析等一系列功能,旨在提高开发效率。以下是 CLion 的一些基本使用方法: + +#### 安装 CLion + +1. 访问 CLion 官网(https://www.jetbrains.com/clion/)。 +2. 下载适用于你操作系统的安装包。 +3. 按照安装向导完成安装。 + +#### 界面概览 + +CLion 的界面主要包括以下几个部分: + +- **菜单栏**:位于顶部,包含文件、编辑、导航、代码、分析、重构、运行、工具、VCS 等菜单。 +- **工具栏**:包含常用的操作按钮,如新建项目、打开文件、保存、运行、调试等。 +- **编辑区**:主要的工作区域,用于编辑代码。 +- **侧边栏**:包含项目视图、结构视图、TODO 视图等。 +- **状态栏**:位于底部,显示当前文件的信息、编译状态、错误和警告等。 + +#### 基本操作 + +1. **创建项目**:通过“文件”菜单或工具栏上的“新建项目”按钮创建新项目。 +2. **打开文件**:通过“文件”菜单或工具栏上的“打开”按钮打开文件。 +3. **编辑代码**:在编辑区中直接输入或修改代码。 +4. **保存文件**:通过“文件”菜单或快捷键 `Ctrl+S`(Windows/Linux)或 `Cmd+S`(macOS)保存文件。 +5. **代码导航**:使用快捷键 `Ctrl+` 或 `Cmd+` 点击函数或变量跳转到定义,使用 `Ctrl+Alt+Left/Right` 在编辑历史中导航。 + +#### 编译和运行 + +CLion 集成了 CMake 作为项目构建系统,可以自动管理编译过程: + +1. 在侧边栏的“项目”视图中,可以看到 CMakeLists.txt 文件,这是 CMake 的配置文件。 +2. 点击工具栏上的“编译”按钮或使用快捷键 `Ctrl+F9` 编译项目。 +3. 点击工具栏上的“运行”按钮或使用快捷键 `Shift+F10` 运行项目。 + +#### 调试 + +CLion 提供了强大的调试功能,可以帮助你定位和修复代码中的问题: + +1. 在代码中设置断点,通过点击行号旁边的空白区域。 +2. 点击工具栏上的“调试”按钮或使用快捷键 `Shift+F9` 启动调试。 +3. 在调试过程中,可以使用调试工具栏上的按钮进行单步执行、查看变量值、检查调用栈等操作。 + +#### 代码分析和重构 + +CLion 提供了代码分析工具,可以帮助你发现潜在的错误和改进代码质量: + +1. 通过“代码”菜单中的“检查代码”功能,CLion 会自动分析代码并给出建议。 +2. 使用“重构”菜单中的功能,如重命名、提取函数、内联变量等,可以安全地修改代码结构。 + +#### 版本控制 + +CLion 内置了对 Git 等版本控制系统的支持: + +1. 通过“VCS”菜单或侧边栏的“版本控制”视图,可以进行提交、拉取、推送等操作。 +2. 在编辑区中,可以通过颜色标记查看文件的修改状态。 + +#### 插件和配置 + +CLion 支持通过插件扩展功能,你可以在“设置/首选项”对话框中安装和管理插件。此外,你还可以在“设置/首选项”中配置编辑器、快捷键、外观等选项,以满足个人偏好。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch1-00.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-00.md new file mode 100644 index 0000000000000000000000000000000000000000..fcfb95836b9886acdc680613f2acaa0f4612595d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-00.md @@ -0,0 +1,10 @@ +# 导学阶段基本信息 + +本章节介绍导学阶段的基本信息,主要内容如下: + +* [训练营教学系统使用引导](ch1-01.md) +* [导学阶段视频课程链接与学习引导](ch1-02.md) +* [导学课程学习资料汇总](ch1-03.md) +* [开营仪式教学安排与资料汇总](ch1-04.md) +* [教学系统常见问题与解决方案](ch1-05.md) + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch1-01.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-01.md new file mode 100644 index 0000000000000000000000000000000000000000..4bb9dd67314af74bee130233d9a40a11f93ac7e7 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-01.md @@ -0,0 +1,68 @@ +## 训练营教学系统使用引导 + +**训练营教学系统导学阶段链接** + +[第一期傲来操作系统训练营导学阶段](https://opencamp.cn/EulixOS/camp/202401/stage/0) + + + +### 个人信息管理 + +![](resource/ch1/1.png) + +如图所示,通过右上角显示的昵称,选择个人中心,进入个人信息管理。 + +![](resource/ch1/2.png) + + + +通过这里的编辑个人信息可以对自己的信息进行补充和修改。 + +**注:为了确保实验成绩在排行榜上正确显示,请确保正确填写了 GitHubName/GiteeName。** + + + +### 如何听课 + +![](resource/ch1/3.png) + +1.首先,你需要进行课程签到,如未完成签到,签到按钮会显示于红圈内“已签到”位置。 + +2.完成签到后可在上课时间点击“进入教室”听课,课程回放会在直播课程结束后于“学习视频”页面上架。 + + + +### 教室使用 + +![](resource/ch1/4.png) + +进入教室时需要确保给与当前页面足够的权限。 + +如有需要可通过左下角聊天框向老师提问。 + +如发现电脑端听课不便,可通过右上角“手机听课”在手机端听课。 + +**注:请优先使用 Chrome 浏览器听课。** + + + +### 成绩查看 + +![](resource/ch1/5.png) + +实验的最终成绩会显示在晋级榜单页面,此处会显示排名、姓名(授课系统昵称)、学校(如在个人信息内填写)、分数与其他信息。 + +**排行榜上显示的成绩将会作为个人晋级的依据。** + + + +### 组队 + +![](resource/ch1/6.png) + +训练营允许且鼓励学员组队学习,组队的操作位于“组队信息页面”。关于组队的操作说明如下: + +1. 只有队长可以创建/解散队伍,其余队员只可加入退出。 +2. 包含队长在内,不少于两人的队伍为有效组队,不允许“单人成队”。 +3. 训练营为全员晋级的队伍的队长发放奖品作为鼓励。 +4. 组队情况不影响单人晋级。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch1-02.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-02.md new file mode 100644 index 0000000000000000000000000000000000000000..8bbc6d92d88adb106c1f9d9e333fb30b46904b11 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-02.md @@ -0,0 +1,39 @@ +## 导学阶段视频链接与学习引导 + +在导学阶段,我们使用《循序渐进,学习开发一个 RISC-V 上的操作系统》系列课程作为导学的网络课程,帮助您学习和掌握与RISC-V相关的指令、操作系统知识。 + +该系列课程的视频回放上传至 bilibili 上,通过访问[《循序渐进,学习开发一个 RISC-V 上的操作系统 》课程录像](https://www.bilibili.com/video/BV1Q5411w7z5) 进行观看学习。 + +全部课程的课件位于码云上的[《循序渐进,学习开发一个 RISC-V 上的操作系统 》课件](https://gitee.com/unicornx/riscv-operating-system-mooc/tree/main/slides),可以通过浏览器访问或对该仓库进行克隆。 + +| 编号 | 课程 | 时长 | 链接 | +| ---- | --------------------------- | ---------- | -------------------------------------------------------- | +| 1 | 导论 | 25分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=1) | +| 2 | 计算机系统漫游 | 53分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=2) | +| 3 | RISC-V ISA 介绍(上) | 36分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=3) | +| 4 | RISC-V ISA 介绍(下) | 1小时6分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=4) | +| 5 | 编译与链接 | 44分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=5) | +| 6 | 嵌入式开发介绍 | 34分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=6) | +| 7 | RISC-V 汇编编程(第一部分) | 28分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=7) | +| 8 | RISC-V 汇编编程(第二部分) | 43分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=8) | +| 9 | RISC-V 汇编编程(第三部分) | 43分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=9) | +| 10 | RISC-V 汇编编程(第四部分) | 41分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=10) | +| 11 | RISC-V 汇编编程(第五部分) | 25分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=11) | +| 12 | RISC-V 汇编编程(第六部分) | 44分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=12) | +| 13 | RISC-V 汇编编程(第七部分) | 57分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=13) | +| 14 | RISC-V 汇编编程(第八部分) | 17分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=14) | +| 15 | RVOS 介绍 | 15分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=15) | +| 16 | Hello RVOS(上) | 38分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=16) | +| 17 | Hello RVOS(下) | 57分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=17) | +| 18 | 内存管理 | 55分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=18) | +| 19 | 上下文切换与协作式多任务 | 38分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=19) | +| 20 | Trap 与 Exception | 1小时7分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=20) | +| 21 | 外部设备中断 | 42分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=21) | +| 22 | 硬件定时器 | 15分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=22) | +| 23 | 抢占式多任务 | 37分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=23) | +| 24 | 任务同步和锁 | 1小时2分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=24) | +| 25 | 软件定时器 | 32分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=25) | +| 26 | 系统调用 | 45分钟 | [链接](https://www.bilibili.com/video/BV1Q5411w7z5?p=26) | + + + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch1-03.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-03.md new file mode 100644 index 0000000000000000000000000000000000000000..41e6abda678f8db8c3359f9825b2aa205d3cd175 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-03.md @@ -0,0 +1,51 @@ +## 导学课程学习资料汇总 + +### 课程相关学习资料 + +**导学阶段课程课件** + +[riscv-operating-system-mooc/slides](https://gitee.com/unicornx/riscv-operating-system-mooc/tree/main/slides) + +此链接包含导学阶段课程[《循序渐进,学习开发一个 RISC-V 上的操作系统 ](https://www.bilibili.com/video/BV1Q5411w7z5) 的全部课件,可作为视频课程的辅助之用。 + +**课程配套实验** + +[riscv-operating-system-mooc](https://gitee.com/unicornx/riscv-operating-system-mooc) + +此链接为导学阶段课程的配套实验代码仓库,实验难度不大,适合初学者了解系统编程与操作系统理论知识,推荐尝试。 + +**一个可运行课程实验的 RISCV CPU** + +[riscv-v-cpu](https://gitee.com/lizhanpeng2022/cpu_prj) + +此仓库为一位该课程[《循序渐进,学习开发一个 RISC-V 上的操作系统 》](https://www.bilibili.com/video/BV1Q5411w7z5) 的热心学员设计的 RISCV CPU,可在其上运行课程配套的实验代码。 + +**课程代码到物理机的移植记录** + +[riscv-operating-system-mooc/issues/I64EEQ](https://gitee.com/unicornx/riscv-operating-system-mooc/issues/I64EEQ) + +这里记录了该课程配套代码向物理机的移植记录,鼓励大家在完成本课程后积极参与这一活动,提高自己的系统编程与工程能力。 + +**一份来自b站本课程学员的学习笔记** + +[RISC-V入门(基础概念+汇编部分) 基于 汪辰老师的视频笔记](https://blog.csdn.net/bebebug/article/details/128039038?spm=1001.2014.3001.5501) + +### 其余学习资料 + +**uCore 实验指导书** + +[uCore OS(on RISC-V64)实验指导书](https://nankai.gitbook.io/ucore-os-on-risc-v64) + +uCore 实验同样为实现一个 RISCV 架构操作系统的教学用操作系统,其难度较于导学阶段课程颇高,但是内容完善,体系严整,可作为完成课程后的提高之用。 + +**rCore 实验指导书** + +[rCore-Tutorial-Book-v3](https://rcore-os.cn/rCore-Tutorial-Book-v3/) + +rCore 实验与 uCore 实验内容基本相同,但是 rCore 代码框架使用 Rust 语言编写,欲进行实验需要先行学习 Rust,但通过本实验可以学习 Rust base OS 这一新兴技术,建议学有余力的学员进行尝试。 + +**清华大学操作系统课程资料** + +[Tsinghua OS Slide Mdbook ](https://lzzs.fun/Tsinghua-OS-mdbook/) + +此链接整合了清华大学计算机系2024春季学习操作系统课程课堂幻灯片的主要内容,对幻灯片中的一些概念进行了简单补充。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch1-04.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-04.md new file mode 100644 index 0000000000000000000000000000000000000000..71e3f505c57e48f37250911f599cc1a6d52c4a3f --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-04.md @@ -0,0 +1,41 @@ +## 开营仪式教学安排与资料汇总 + +**训练营开营启动会** + +会议时间:2024/06/02 20:00[视频回放(密码:0602)](https://meeting.tencent.com/user-center/shared-record-info?id=cea43f5d-e061-43dd-8281-5ca765a42d7c&from=3&record_type=2) + + + +**训练营开营启动会安排** + +* 8:00 主持人唐涵主持开场,介绍与会老师与嘉宾 + +* 8:02 武延军所长致辞 +* 8:07 陈渝老师致辞 +* 8:12 于佳耕老师致辞,介绍 RISC-V 当前生态及未来发展方向展望 +* 8:27 常秉善介绍本次训练营报名及课程开设介绍 +* 8:42 王凡介绍本次训练营的基础与专业阶段并分享学习经验 +* 8:57 姬晨晨介绍本次训练营项目阶段的选题方向和实践 +* 9:12 于佳耕老师总结寄语 +* 9:15 主持人介绍组队宣传,及引导训练营问题答疑 + + + +**训练营教学安排** + +![](resource/ch1/jiaoxueanpai.png) + + + +**傲来操作系统(EulixOS)官网** + +[中科傲来](https://eulixos.com/) + +[傲来操作系统下载](https://eulixos.com/#/download) + + + +**傲来操作系统1.0使用手册** + +[EulixOS1.0使用手册](https://eulixos.com/userguide/) + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch1-05.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-05.md new file mode 100644 index 0000000000000000000000000000000000000000..d2b198fbc07be2c164603c3f0f488d3557617dbe --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch1-05.md @@ -0,0 +1,29 @@ +## 教学系统常见问题与解决方案 + +在使用教学系统进行学习或参与活动时,可能会遇到各种技术问题。以下是一些常见问题的解决方案,帮助你顺利完成学习任务和活动参与。 + +### 1.pc 端教室画面显示异常 + +![](resource/ch1/05-1.png) + +如果在使用 Edge 浏览器时遇到教室画面显示不全的问题,可以尝试切换到 Chrome 浏览器。Chrome 浏览器通常对网页的兼容性更好,可以有效避免显示问题。 + +**建议在 PC 端使用 Chrome 浏览器听课,以防止出现类似问题。** + +### 2.组队页面无法自己组建队伍 + +如果你发现自己无法在组队页面创建新的队伍,首先检查你是否已经加入或创建了另一支队伍。每个阶段通常限制每人只能参与一支队伍。如果已加入或创建了队伍,你需要先退出或解散当前队伍,然后才能创建新的队伍。 + +如果确认没有加入或创建其他队伍,但仍然无法创建新队伍,请确保你已经登录到教学系统。如果登录后问题依旧,建议联系系统管理员寻求帮助。 + +### 3.进入教室后发现没有声音 + +如果在进入教室后发现没有声音,首先检查你是否已经给予教室页面所需的权限,如麦克风和扬声器的访问权限。同时,检查你的本地设备设置,确保扬声器或耳机已正确连接并开启。 + +如果声音较小,可以在聊天区向授课老师提出,请求调整音量。 + +### 4.晋级榜单上的成绩没有准确链接到用户信息 + +为了确保你的成绩能够正确链接到你的用户信息,你需要在个人信息页面中正确填写你的 GitHubName 或 GiteeName。这些信息是用来识别你的身份并关联你的成绩的。 + +填写完毕后,重新提交你的代码。这通常可以刷新成绩和用户信息的链接,确保你的成绩正确显示在排行榜上。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch2-00.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-00.md new file mode 100644 index 0000000000000000000000000000000000000000..a0bcf2a2b9c8d321045e63a69713c29fdf0e3ca9 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-00.md @@ -0,0 +1,8 @@ +# 基本 Git 使用与 Gitee 流水线入门 + +训练营的实验基于 Gitee 平台提供的 Gitee Go 服务进行在线的评测与成绩提交,因而本章简要介绍基本的 Git 使用与 Gitee Go 服务配置,以便于大家开展实验。 + +* [常用 Git 指令](ch2-01.md) +* [Gitee 使用入门](ch2-02.md) +* [Gitee Go 的配置与使用](ch2-03.md) +* [流水线常见问题与解决方案](ch2-04.md) diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch2-01.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-01.md new file mode 100644 index 0000000000000000000000000000000000000000..fc28f4b9fbd6b5db6f148f6093d445bf003ad145 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-01.md @@ -0,0 +1,163 @@ +## 常用 Git 指令 + +Git 是一个强大的版本控制系统,广泛应用于软件开发中。以下是一些常用的 Git 指令,它们是日常工作中不可或缺的工具。 + +##### 1. 初始化仓库 + +```bash +git init +``` + +这个命令会在当前目录创建一个新的 Git 仓库。执行后,目录下会生成一个 `.git` 子目录,用于存储仓库的元数据和对象数据库。 + +##### 2. 克隆仓库 + +```bash +git clone +``` + +克隆远程仓库到本地。`` 可以是 HTTPS、SSH 或 Git 协议的 URL。例如: + +```bash +git clone https://gitee.com/username/repository.git +``` + +克隆完成后,你将拥有一个与远程仓库完全相同的本地副本。 + +##### 3. 添加文件 + +```bash +git add +``` + +这个命令将工作目录中的文件添加到暂存区(staging area),准备进行下一次提交。例如: + +```bash +git add README.md +``` + +你也可以使用 `git add .` 来添加所有更改过的文件。 + +##### 4. 提交更改 + +```bash +git commit -m "commit message" +``` + +提交暂存区的文件到本地仓库,`commit message` 是对这次提交的描述。例如: + +```bash +git commit -m "Add initial README file" +``` + +##### 5. 查看状态 + +```bash +git status +``` + +这个命令显示工作目录和暂存区的状态。你可以看到哪些文件被修改,哪些文件被添加到暂存区,以及哪些文件未被跟踪。 + +##### 6. 查看提交历史 + +```bash +git log +``` + +这个命令显示仓库的提交历史。默认情况下,它会显示每个提交的作者、提交时间、提交信息以及提交的哈希值。 + +##### 7. 推送更改 + +```bash +git push origin +``` + +这个命令将本地仓库的提交推送到远程仓库。`` 是你想要推送的分支名称。例如: + +```bash +git push origin master +``` + +##### 8. 拉取更新 + +```bash +git pull origin +``` + +这个命令从远程仓库拉取更新并尝试合并到当前分支。`` 是远程分支的名称。例如: + +```bash +git pull origin develop +``` + +##### 9. 创建分支 + +```bash +git branch +``` + +这个命令创建一个新分支,但不会自动切换到该分支。例如: + +```bash +git branch feature-x +``` + +##### 10. 切换分支 + +```bash +git checkout +``` + +这个命令切换到指定的分支。例如: + +```bash +git checkout feature-x +``` + +你也可以使用 `git checkout -b ` 来创建并切换到新分支。 + +##### 11. 合并分支 + +```bash +git merge +``` + +将指定分支的更改合并到当前分支。 + +##### 12. 删除分支 + +```bash +git branch -d +``` + +删除指定的分支。 + +注意,如果分支有未合并的更改,Git 会阻止你删除它。 + +##### 13. 撤销更改 + +```bash +git checkout -- +``` + +这个命令撤销对文件的本地更改,将其恢复到最后一次提交的状态。例如: + +```bash +git checkout -- README.md +``` + +##### 14. 撤销暂存区的更改 + +```bash +git reset HEAD +``` + +将文件从暂存区移出,但保留本地更改。 + +##### 15. 撤销提交 + +```bash +git reset --hard +``` + +撤销到指定的提交,`` 是提交的哈希值。这将重置工作目录和暂存区,使其与指定的提交完全一致。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch2-02.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-02.md new file mode 100644 index 0000000000000000000000000000000000000000..0feaf7904785345c8042c2c0425fd5d2d57c2a7d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-02.md @@ -0,0 +1,98 @@ +## Gitee 使用入门 + +Gitee 是一个基于 Git 的代码托管和协作平台,它提供了代码仓库管理、代码审查、问题跟踪、持续集成和部署等功能。以下是 Gitee 使用的一些基本步骤和技巧。 + +### Gitee 基本功能的使用 + +![](resource/ch2/02-1.png) + +打开某一仓库后的 Gitee 页面如上图所示 + +##### 1. 注册与登录 + +访问 [Gitee 官网](https://gitee.com/),点击右上角的“注册”按钮进行账号注册。你需要提供邮箱地址、用户名和密码。注册完成后,使用你的用户名和密码登录 Gitee。 + +##### 2. 创建仓库 + +登录后,点击页面右上角的“+”号,选择“新建仓库”。在创建仓库页面,填写仓库名称、选择仓库的可见性(公开或私有)、添加仓库描述、选择是否初始化仓库以及是否添加 README、.gitignore 和开源许可证等文件。完成设置后,点击“创建仓库”。 + +##### 3. 仓库管理 + +在仓库页面,通过最右侧的“管理”你可以进行多种管理操作: + +- **基本信息**:编辑仓库的名称、描述、可见性等。 +- **分支保护**:设置分支保护规则,防止重要分支被错误地修改或删除。 +- **WebHooks**:配置 WebHooks,当仓库发生特定事件(如推送代码)时,自动触发外部服务。 +- **部署密钥**:添加部署密钥,允许服务器无密码访问仓库。 +- **成员管理**:邀请其他用户加入仓库,并设置他们的权限级别。 + +##### 4. 克隆仓库 + +在仓库页面,找到“克隆/下载”按钮,选择 HTTPS 或 SSH 方式获取仓库的 URL。然后,在本地命令行中使用 `git clone` 命令将仓库克隆到本地: + +```bash +git clone https://gitee.com/username/repository.git +``` + +##### 5. 提交与推送 + +在本地仓库进行修改后,使用以下命令将更改提交到 Gitee 仓库: + +```bash +git add . +git commit -m "Update README" +git push origin master +``` + +##### 6. 拉取更新 + +如果远程仓库有更新,使用 `git pull` 命令将更新拉取到本地: + +```bash +git pull origin master +``` + +##### 7. 分支管理 + +在 Gitee 仓库页面,你可以创建、删除和管理分支。在本地,使用以下命令进行分支操作: + +```bash +git branch feature-x # 创建新分支 +git checkout feature-x # 切换到新分支 +git merge feature-x # 将 feature-x 分支合并到当前分支 +git branch -d feature-x # 删除 feature-x 分支 +``` + +##### 8. 问题跟踪 + +在仓库页面,点击“问题”标签,然后点击“新建问题”按钮创建问题。你可以为问题添加标签、指派负责人、设置截止日期等。 + +##### 9. 代码审查 + +在仓库页面,点击“合并请求”标签,然后点击“新建合并请求”按钮。选择源分支和目标分支,填写请求信息,然后提交。其他开发者可以审查代码并提供反馈。 + +##### 10. 持续集成 + +在仓库页面,点击“流水线”标签,然后点击“新建流水线”按钮。配置流水线,设置触发条件、构建环境、构建脚本等。提交后,流水线将自动执行代码的构建、测试和部署。**更详细的介绍会在之后讲解** + +##### 11. 使用 Gitee Pages + +在仓库页面,点击“服务”下的“Gitee Pages”,然后点击“启动”按钮。配置部署分支和目录,提交后,Gitee Pages 将自动部署你的静态网站。 + +##### 12. 团队协作 + +在仓库页面,点击“管理”标签,然后选择“仓库成员管理”。你可以邀请其他用户加入仓库,并设置他们的权限,如只读、写入或管理员。 + +##### 13. 使用 Gitee API + +Gitee 提供了 [API 文档](https://gitee.com/api/v5/swagger#/getV5ReposOwnerRepo),你可以通过 API 自动化管理仓库、问题、合并请求等。 + +##### 14. 安全设置 + +在仓库页面,点击“管理”标签,然后选择“仓库设置”。你可以设置仓库的访问权限、分支保护规则、部署密钥等,确保仓库安全。 + +##### 15. 学习资源 + +Gitee 提供了[帮助中心](https://gitee.com/help),你可以在那里找到各种教程、指南和最佳实践,帮助你更好地使用 Gitee。 + +**注:Gitee 平台的部分操作需要在完成实名认证后才能进行,请在注册账号后尽快进行实名认证。** diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch2-03.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-03.md new file mode 100644 index 0000000000000000000000000000000000000000..00ba1fc47448e3237ef972e9f498d47f88838444 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-03.md @@ -0,0 +1,40 @@ +## Gitee Go 的配置与使用 + +Gitee Go 是 Gitee 提供的一个持续集成(CI)服务,它允许开发者自动化代码的构建、测试和部署过程。 + +**详细信息可前往 Gitee 帮助中心查看详细文档,本文主要针对训练营实验进行引导** + +在进行导学阶段实验时,需要进行以下几个步骤的操作 + +### step1-fork 模板仓库 + +**训练营导学阶段实验仓库地址** + +[傲来操作系统训练营导学阶段实验](https://gitee.com/LearningEulixOS/class-test-ci) + + + +![](resource/ch2/02-1.png) + +通过右上角的 fork 按钮进行 fork 本仓库 + +**注:fork 操作需要在实名认证后进行,请确保你的 Gitee 账户已完成实名认证** + +### step2-开通 Gitee Go + +在完成第一步后,切换到自己 fork 的仓库,点击“流水线” + +![](resource/ch2/kaitong.jpg) + +点击”开通 Gitee GO“来使用 CI。 + +![](resource/ch2/chuagjian.jpg) + +是否创建默认流水线建议选择“不创建”。 + +**注:开通 Gitee go 后流水线页面显示无流水线为正常现象** + +**此后每次向远程仓库的 master 分支进行 push 都会触发流水线进行评测** + + + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch2-04.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-04.md new file mode 100644 index 0000000000000000000000000000000000000000..5b0e3e529c4f7bcd59f7bb66932c76aa7d1db5c2 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch2-04.md @@ -0,0 +1,28 @@ +## 流水线常见问题与解决方案 + +在使用 Gitee Go 进行代码托管和自动化测试时,可能会遇到各种问题,如流水线不执行、时间耗尽、运行时间过长等。以下是针对这些常见问题的详细解决方案: + +### 1.在开通 Gitee Go 并向仓库 push 代码后流水线不执行 + +* **检查流水线时间**:首先确认你的仓库流水线时间是否已经耗尽。如果时间耗尽,流水线将不会执行。 +* **手动创建流水线**:如果流水线时间未耗尽,尝试在流水线页面手动创建新的流水线。使用实验中的流水线配置代码,配置文件通常位于 `.workflow/test.yml`。 +* **修改配置文件**:如果手动创建流水线后问题依旧,尝试修改配置文件中的作业名称或阶段名称,然后重新提交。 +* **重新 fork 仓库**:如果上述步骤都无法解决问题,尝试重新 fork 仓库,然后再次推送代码。 +* **手动创建新仓库**:如果重新 fork 仓库后问题仍未解决,可以尝试根据当前仓库内容手动创建一个新仓库,但不进行 fork 操作。 + +### 2.在开通 Gitee Go 时不小心创建了默认流水线。 + +- **删除默认流水线**:默认流水线的运行不会影响评测流水线的运行,但会消耗时间。你可以在流水线页面手动删除默认流水线。评测流水线的默认名称为“test”。 + +### 3.仓库流水线时间耗尽怎么办 + +- **再 fork 一个仓库**:如果仓库的流水线时间耗尽,最简单的解决方案是再 fork 一个仓库,然后继续使用。 + +### 4.流水线时间运行过长怎么办 + +- **手动终止运行**:如果流水线运行时间过长,可以手动终止运行。然后查看流水线日志,确定导致运行时间过长的原因。理论上,单次流水线运行时间不应超过半个小时。 + +### 5.流水线日志无法生成 + +- **尝试其他设备或浏览器**:如果流水线日志无法生成,尝试使用手机或其他浏览器查看。有时候,问题可能是由于浏览器兼容性或缓存问题导致的。 + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch3-00.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-00.md new file mode 100644 index 0000000000000000000000000000000000000000..92f634bc263f74b83c3f66e863bdc55ca52d99a0 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-00.md @@ -0,0 +1,8 @@ +# 前置知识 + +本章介绍完成实验所需的部分前置知识,这些知识在之后的正式实验中也会用到。 + +* [Linux 使用入门与配置](ch3-01.md) +* [C 语言基本语法](ch3-02.md) +* [make 基本使用与 Makefile](ch3-03.md) +* [如何在非 Linux 环境中完成实验](ch3-04.md) diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch3-01.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-01.md new file mode 100644 index 0000000000000000000000000000000000000000..5ba2528461378d8a95c290377f9805be8189e3e6 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-01.md @@ -0,0 +1,125 @@ +## Linux 使用入门与配置 + +Linux 是一个广泛使用的开源操作系统,以其稳定性、安全性和灵活性而闻名。本节将介绍 Linux 的基本使用和配置,帮助读者为后续的实验做好准备。 + + + +### Linux 简介 + +Linux 最初由 Linus Torvalds 在1991年开发,它基于 Unix 操作系统。Linux 的核心(Kernel)是操作系统的核心部分,负责管理硬件资源和提供系统服务。Linux 操作系统通常包括核心以及一系列的系统工具、应用软件和图形用户界面(GUI)。 + +### 安装 Linux + +在开始使用 Linux 之前,首先需要安装一个 Linux 发行版。常见的 Linux 发行版包括 Ubuntu、Fedora、Debian 和 ArchLinux 等。 + +我们推荐使用 Ubuntu 发行版作为实验环境。 + +**物理机 Linux** + +* 首先,各位需要根据个人需求和偏好选择一个 Linux 发行版安装,一般来说不同的 Linux 发行版其最明显的区别在于软件包管理器的不同,例如 Debian 系发行版主要采用 apt 进行软件包管理,Arch 发行版则主要采用 pacman 进行软件包管理。 + * [最适合程序员的 10 款 Linux 发行版](https://linux.cn/article-14547-1.html) + * [Linux 各种发行版介绍](https://blog.csdn.net/tugouxp/article/details/129459799) +* 确定好自己要使用的发行版之后,一般需要到其官网下载安装镜像。 + * [Ubuntu 下载](https://cn.ubuntu.com/download) + * [ArchLinux 下载](https://archlinux.org/download/) +* 创建启动盘:使用工具如 Rufus 或 Etcher 将 ISO 镜像写入 USB 驱动器或 DVD。 +* 启动安装程序:通过启动盘启动计算机,并按照屏幕上的指示进行安装。 + * [Ubuntu 安装教程](https://blog.csdn.net/weixin_70137390/article/details/124724957) + * [ArchLinux 安装教程](https://archlinuxstudio.github.io/ArchLinuxTutorial/#/) + * [manajro 安装教程](https://www.cnblogs.com/Tsingwaa/articles/16254542.html) + +**新手玩家一般推荐尝试 ArchLinux,如希望尝试 Arch 系发行版可尝试 manjaro** + + + +**双系统安装** + +大多数情况下我们不得不使用Windows,那么我们可以考虑在自己的设备上安装 Windows + Linux 双系统。 + +*建议在设备拥有两块可用硬盘的情况下尝试,将两个系统安装在不同的硬盘上* + +***为防止出现错误,请备份数据*** + +[Ubuntu 双系统安装](https://blog.csdn.net/NeoZng/article/details/122779035) + +双系统安装完成后可能出现系统时间不同步的问题,可参考如下链接解决: + +[解决双系统时间不同步](https://www.cnblogs.com/xiaotong-sun/p/16138941.html) + + + +**虚拟机/ wsl** + +如担心直接安装物理机或双系统可能带来的问题,或者担心易用性,可以尝试使用虚拟机或 wsl(Windows 的 Linux 子系统)。 + +关于 wsl 的安装,请参考如下链接: + +[适用于 Linux 的 Windows 子系统文档](https://learn.microsoft.com/zh-cn/windows/wsl/) + + + +### Linux 基本命令 + +#### 文件和目录操作命令 + +- **`ls`**:列出目录内容。 + - 示例:`ls -l` 以长格式列出当前目录下的所有文件和目录。 + - 示例:`ls -a` 显示所有文件,包括隐藏文件。 + - 示例:`ls /home/user` 列出指定目录 `/home/user` 的内容。 +- **`cd`**:改变当前目录。 + - 示例:`cd /home/user` 切换到 `/home/user` 目录。 + - 示例:`cd ..` 切换到上一级目录。 + - 示例:`cd` 切换到用户的主目录。 +- **`pwd`**:显示当前工作目录的路径。 + - 示例:`pwd` 显示当前所在的完整目录路径。 +- **`mkdir`**:创建新目录。 + - 示例:`mkdir new_directory` 在当前目录下创建名为 `new_directory` 的新目录。 + - 示例:`mkdir -p parent/child/grandchild` 创建多级目录,如果父目录不存在则一并创建。 +- **`rm`**:删除文件或目录。 + - 示例:`rm file.txt` 删除名为 `file.txt` 的文件。 + - 示例:`rm -r directory` 删除名为 `directory` 的目录及其内容。 + - 示例:`rm -f file.txt` 强制删除 `file.txt` 文件,不提示确认。 +- **`cp`**:复制文件或目录。 + - 示例:`cp source.txt destination.txt` 将 `source.txt` 复制为 `destination.txt`。 + - 示例:`cp -r source_directory destination_directory` 复制整个目录及其内容。 +- **`mv`**:移动或重命名文件或目录。 + - 示例:`mv old.txt new.txt` 将 `old.txt` 重命名为 `new.txt`。 + - 示例:`mv file.txt /home/user` 将 `file.txt` 移动到 `/home/user` 目录。 + +#### 文件查看和编辑命令 + +- **`cat`**:连接文件并打印到标准输出。 + - 示例:`cat file.txt` 显示 `file.txt` 的内容。 + - 示例:`cat file1.txt file2.txt > combined.txt` 将 `file1.txt` 和 `file2.txt` 的内容合并到 `combined.txt`。 +- **`more`** 或 **`less`**:分页查看文件内容。 + - 示例:`more file.txt` 逐页显示 `file.txt` 的内容。 + - 示例:`less file.txt` 更高级的分页查看,支持向前和向后翻页。 +- **`nano`** 或 **`vi`**:文本编辑器。 + - 示例:`nano file.txt` 使用 nano 编辑器打开 `file.txt`。 + - 示例:`vi file.txt` 使用 vi 编辑器打开 `file.txt`。 + +#### 系统信息和进程管理命令 + +- **`uname`**:显示系统信息。 + - 示例:`uname -a` 显示所有系统信息,包括内核版本和系统架构。 +- **`top`**:实时显示系统资源使用情况和运行中的进程。 + - 示例:`top` 打开 top 命令,查看系统资源和进程信息。 +- **`ps`**:显示当前运行的进程。 + - 示例:`ps aux` 显示所有用户的所有进程。 +- **`kill`**:终止进程。 + - 示例:`kill PID` 终止指定 PID 的进程。 + - 示例:`kill -9 PID` 强制终止指定 PID 的进程。 + +#### 网络命令 + +- **`ping`**:测试网络连接。 + - 示例:`ping google.com` 测试与 [google.com](http://google.com/) 的网络连接。 +- **`ifconfig`** 或 **`ip`**:显示和配置网络接口。 + - 示例:`ifconfig` 或 `ip addr` 显示所有网络接口的配置信息。 +- **`netstat`**:显示网络连接、路由表和接口统计等。 + - 示例:`netstat -an` 显示所有网络连接和端口状态。 + +通过这些命令,您可以有效地管理文件和目录,查看和编辑文件内容,监控系统资源和进程,以及进行基本的网络配置和故障排查。这些命令是 Linux 系统管理和开发工作的基础。 + + + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch3-02.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-02.md new file mode 100644 index 0000000000000000000000000000000000000000..43791122500cbf7233203f954b7ab63f14409b8f --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-02.md @@ -0,0 +1,228 @@ +## C 语言基本语法 + +C语言是一种广泛使用的编程语言,以其效率和灵活性而著称。本节将介绍C语言的基本语法,为后续的实验和开发工作打下基础。 + +### C语言简介 + +C语言由Dennis Ritchie在1972年开发,最初是为了编写UNIX操作系统而设计的。C语言具有简洁的语法和强大的功能,使其成为系统编程、嵌入式系统和高级应用程序的首选语言。 + +### 基本数据类型 + +C语言的基本数据类型包括整型、浮点型、字符型和布尔型。每种类型都有其特定的内存占用和取值范围。 + +- **整型**:`int` 通常占用4个字节,但也可以是2个字节,这取决于编译器和系统。`short` 和 `long` 可以用来指定不同大小的整数。 + - `short int`:通常占用2个字节。 + - `long int`:通常占用4个或8个字节。 + - `long long int`:通常占用8个字节。 +- **浮点型**:`float` 和 `double` 分别用于表示单精度和双精度浮点数。 + - `float`:通常占用4个字节。 + - `double`:通常占用8个字节。 +- **字符型**:`char` 用于存储单个字符,占用1个字节。 + - `char`:占用1个字节,用于存储字符。 +- **布尔型**:`_Bool` 用于存储布尔值(true或false),占用1个字节。 + - `_Bool`:占用1个字节,用于存储布尔值。 + + + +### 变量和常量 + +变量是存储数据值的容器,而常量是固定值。 + +- **变量声明和初始化**: + + ```c + int age = 25; // 声明一个整型变量并初始化为25 + float pi = 3.14159; // 声明一个浮点型变量并初始化为3.14159 + char letter = 'A'; // 声明一个字符型变量并初始化为'A' + ``` + +- **常量声明**: + + ```c + const int MAX_VALUE = 100; // 声明一个整型常量 + const float PI = 3.14159; // 声明一个浮点型常量 + ``` + +### 运算符 + +C语言提供了丰富的运算符,用于执行各种操作。 + +- **算术运算符**: + + ```c + int a = 10, b = 3; + int sum = a + b; // 加法 + int diff = a - b; // 减法 + int product = a * b; // 乘法 + float quotient = (float)a / b; // 除法 + int remainder = a % b; // 取模 + ``` + +- **关系运算符**: + + ```c + int x = 5, y = 10; + if (x == y) { // 等于 + printf("x is equal to y\n"); + } + if (x != y) { // 不等于 + printf("x is not equal to y\n"); + } + if (x > y) { // 大于 + printf("x is greater than y\n"); + } + if (x < y) { // 小于 + printf("x is less than y\n"); + } + if (x >= y) { // 大于等于 + printf("x is greater than or equal to y\n"); + } + if (x <= y) { // 小于等于 + printf("x is less than or equal to y\n"); + } + ``` + +- **逻辑运算符**: + + ```c + int p = 1, q = 0; + if (p && q) { // 逻辑与 + printf("Both p and q are true\n"); + } + if (p || q) { // 逻辑或 + printf("Either p or q is true\n"); + } + if (!p) { // 逻辑非 + printf("p is false\n"); + } + ``` + +- **位运算符**: + + ```c + int m = 12, n = 5; + int and_result = m & n; // 按位与 + int or_result = m | n; // 按位或 + int xor_result = m ^ n; // 按位异或 + int not_result = ~m; // 按位取反 + int left_shift = m << 1; // 左移 + int right_shift = m >> 1; // 右移 + ``` + +### 控制结构 + +控制结构用于控制程序的执行流程。 + +- **条件语句**: + + ```c + int score = 85; + if (score >= 90) { + printf("优秀\n"); + } else if (score >= 80) { + printf("良好\n"); + } else if (score >= 70) { + printf("中等\n"); + } else if (score >= 60) { + printf("及格\n"); + } else { + printf("不及格\n"); + } + ``` + +- **循环语句**: + + ```c + // for循环 + for (int i = 0; i < 5; i++) { + printf("%d\n", i); + } + // while循环 + int j = 0; + while (j < 5) { + printf("%d\n", j); + j++; + } + // do-while循环 + int k = 0; + do { + printf("%d\n", k); + k++; + } while (k < 5); + ``` + +- **跳转语句**: + + ```c + // break语句 + for (int i = 0; i < 10; i++) { + if (i == 5) { + break; // 跳出循环 + } + printf("%d\n", i); + } + // continue语句 + for (int i = 0; i < 10; i++) { + if (i == 5) { + continue; // 跳过当前循环的剩余部分,进入下一次循环 + } + printf("%d\n", i); + } + ``` + +### 函数 + +函数是C语言中的基本构建块,用于封装可重用的代码。 + +- 函数定义和调用: + + ```c + // 函数定义 + int add(int a, int b) { + return a + b; + } + // 函数调用 + int result = add(3, 4); + printf("The result is %d\n", result); + ``` + +### 数组和指针 + +数组用于存储相同类型的多个元素,而指针是存储变量地址的变量。 + +- **数组操作**: + + ```c + int numbers[5] = {1, 2, 3, 4, 5}; + for (int i = 0; i < 5; i++) { + printf("%d\n", numbers[i]); + } + ``` + +- **指针操作**: + + ```c + int *p; + int age = 25; + p = &age; // 将age的地址赋给指针p + printf("The value of age is %d\n", *p); // 通过指针访问age的值 + ``` + +### 结构体和联合体 + +结构体允许用户定义自己的数据类型,它可以将不同类型的数据组合在一起。联合体是一种特殊的结构体,它允许在相同的内存位置存储不同的数据类型。 + +- 结构体定义和使用: + + ```c + // 结构体定义 + struct Person { + char name[50]; + int age; + }; + // 结构体使用 + struct Person person1; + strcpy(person1.name, "Alice"); + person1.age = 30; + printf("Name: %s, Age: %d\n", person1.name, person1.age); + ``` diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch3-03.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-03.md new file mode 100644 index 0000000000000000000000000000000000000000..0ca35178b424de9b7026f58f23b44e345a8a5ef7 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-03.md @@ -0,0 +1,123 @@ +## make 基本使用与 Makefile + +在软件开发中,自动化构建过程是提高效率的关键。`make` 是一个在类Unix系统中广泛使用的构建自动化工具,它通过读取 `Makefile` 中的规则来自动化编译和链接程序。本节将介绍 `make` 的基本使用和 `Makefile` 的编写。 + +### Makefile 基础 + +`Makefile` 是一个文本文件,它包含了一系列规则,每条规则定义了目标文件、依赖文件和生成目标文件的命令。 + +- 规则格式: + + ```Makefile + target: dependencies + command + ... + ``` + + - `target`:通常是需要生成的目标文件。 + - `dependencies`:生成目标文件所依赖的文件。 + - `command`:生成目标文件的命令。 + +### 基本 Makefile 示例 + +假设我们有一个简单的C语言项目,包含两个源文件 `main.c` 和 `hello.c`,以及一个头文件 `hello.h`。我们可以创建一个 `Makefile` 来编译这个项目。 + +- Makefile 示例: + + ```Makefile + # 定义变量 + CC = gcc + CFLAGS = -Wall -g + + # 默认目标 + all: hello + + # 目标依赖和命令 + hello: main.o hello.o + $(CC) $(CFLAGS) -o hello main.o hello.o + + # 编译规则 + main.o: main.c hello.h + $(CC) $(CFLAGS) -c main.c + + hello.o: hello.c hello.h + $(CC) $(CFLAGS) -c hello.c + + # 清理规则 + clean: + rm -f hello *.o + ``` + +### make 命令的使用 + +`make` 命令不仅可以用来编译项目,还可以用来执行 `Makefile` 中定义的其他任务。 + +- **指定目标**: + + ```bash + make target + ``` + + 这里的 `target` 是 `Makefile` 中定义的一个目标。如果不指定目标,`make` 会默认执行第一个目标(通常是 `all`)。 + +- **递归 make**: 在大型项目中,可能会有多个子目录,每个子目录都有自己的 `Makefile`。可以使用 `make -C` 命令来递归地调用子目录中的 `make`。 + + ```bash + make -C subdir + ``` + +- **显示命令**: 默认情况下,`make` 在执行命令时不会显示命令本身,只会显示输出。使用 `-n` 或 `--just-print` 选项可以只显示命令而不执行。 + + ```bash + make -n + ``` + +- **忽略错误**: 使用 `-k` 或 `--keep-going` 选项可以让 `make` 在遇到错误时继续执行后续的命令。 + + ```bash + make -k + ``` + +### Makefile 的高级特性 + +`Makefile` 提供了许多高级特性,使得构建过程更加灵活和强大。 + +- **自动变量**: `make` 提供了一些特殊的自动变量,如 `$@`(目标文件)、`$<`(第一个依赖文件)和 `$^`(所有依赖文件)。 + + ```Makefile + %.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + ``` + +- **伪目标**: 伪目标不是真正的文件,而是代表一组操作。例如,`clean` 通常用来清理编译生成的文件。 + + ```Makefile + .PHONY: clean + clean: + rm -f hello *.o + ``` + +- **内置函数**: `make` 提供了许多内置函数,如 `wildcard`、`patsubst`、`subst` 等。 + + ```makefile + SRCS := $(wildcard *.c) + OBJS := $(patsubst %.c, %.o, $(SRCS)) + ``` + +- **条件语句**: 条件语句允许根据不同条件执行不同的命令。 + + ```makefile + ifeq ($(OS), Windows_NT) + RM := del + else + RM := rm -f + endif + ``` + +- **宏定义**: 宏定义允许在 `Makefile` 中定义复杂的表达式,并在需要时展开。 + + ```makefile + MACRO = $(CC) $(CFLAGS) -c $< -o $@ + %.o: %.c + $(MACRO) + ``` diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch3-04.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-04.md new file mode 100644 index 0000000000000000000000000000000000000000..ab2049193a5dbb7b93146bf7e9cf547512fa3ee2 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch3-04.md @@ -0,0 +1,53 @@ +## 如何在非 Linux 环境中完成实验 + +在软件开发和计算机科学的实验中,Linux 环境因其强大的命令行工具和开源软件的支持而广受欢迎。然而,并非所有开发者或学生都有条件或偏好使用 Linux。本节将探讨如何在非 Linux 环境(如 Windows 或 macOS)中完成实验,确保实验的顺利进行。 + +### 使用虚拟机 + +虚拟机是一种在现有操作系统上模拟完整计算机系统的技术。通过虚拟机,用户可以在非 Linux 系统上运行 Linux 操作系统,从而获得一个隔离的实验环境。 + +- **安装虚拟机软件**: + - 在 Windows 上,可以使用 VMware Workstation 或 VirtualBox。 + - 在 macOS 上,可以使用 VMware Fusion 或 Parallels Desktop。 +- **创建 Linux 虚拟机**: + - 下载 Linux 发行版的 ISO 镜像文件。 + - 使用虚拟机软件创建新的虚拟机,并安装 Linux。 +- **在虚拟机中进行实验**: + - 启动虚拟机,进入 Linux 环境。 + - 安装所需的开发工具和软件包。 + - 进行实验,所有的操作和数据都将在虚拟机内部进行,不会影响宿主机的环境。 + +### 使用容器技术 + +容器技术如 Docker 提供了一种轻量级的虚拟化方法,允许用户在非 Linux 环境中运行 Linux 容器。容器共享宿主机的操作系统内核,因此启动更快,资源消耗更少。 + +- **安装 Docker**: + - 在 Windows 和 macOS 上,可以从 Docker 官网下载 Docker Desktop。 +- **拉取 Linux 容器镜像**: + - 使用 Docker 命令从 Docker Hub 拉取所需的 Linux 容器镜像。 +- **运行容器进行实验**: + - 使用 Docker 命令启动容器,并在容器中进行实验。 + - 容器内的操作和数据与宿主机隔离,确保实验环境的一致性和可重复性。 + +### 使用 WSL(适用于 Windows) + +Windows Subsystem for Linux(WSL)是 Windows 10 的一个功能,允许用户在 Windows 上运行原生的 Linux 二进制文件。WSL 提供了一个兼容 Linux 的子系统,使得在 Windows 上进行 Linux 开发变得更加容易。 + +- **启用 WSL**: + - 在 Windows 上,通过 PowerShell 以管理员身份运行 `wsl --install` 命令来启用 WSL。 +- **安装 Linux 发行版**: + - 从 Microsoft Store 安装所需的 Linux 发行版,如 Ubuntu。 +- **在 WSL 中进行实验**: + - 启动 Linux 发行版,进行实验。 + - WSL 提供了一个完整的 Linux 环境,可以运行大多数 Linux 命令和应用程序。 + +### 使用跨平台工具和框架 + +许多现代的开发工具和框架都是跨平台的,可以在 Windows、macOS 和 Linux 上运行。选择这些工具可以减少环境配置的复杂性。 + +- **选择跨平台工具**: + - 例如,使用跨平台的 IDE(如 Visual Studio Code)和构建工具(如 CMake 或 Gradle)。 +- **配置环境变量**: + - 确保所有必要的工具和库都已安装,并正确配置环境变量。 +- **进行实验**: + - 在非 Linux 环境中使用跨平台工具进行实验,确保实验结果的可移植性。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-00.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-00.md new file mode 100644 index 0000000000000000000000000000000000000000..8242be82e18aea4d6cdb0e008bc826a502ee3894 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-00.md @@ -0,0 +1,15 @@ +# 开始实验 + +本章我们将正式开始进行实验。 + +* [实验代码框架讲解](ch4-01.md) +* [本地测试流程](ch4-02.md) +* [实验相关资料汇总](ch4-03.md) +* [第一题:输出 Hello World!](ch4-04.md) +* [第二题:基本循环](ch4-05.md) +* [第三题:打印九九乘法表](ch4-06.md) +* [第四题:求素数](ch4-07.md) +* [第五题:约瑟夫环](ch4-08.md) + +* [导学阶段实验相关问题与解决方案](ch4-09.md) + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-01.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-01.md new file mode 100644 index 0000000000000000000000000000000000000000..1182e251b00f6aaaf2129f5a25fd90d3c124d312 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-01.md @@ -0,0 +1,166 @@ +## 实验代码框架讲解 + +### 项目结构 + +```bash +. +├── exercises //所有习题都在此文件夹下 +├── LICENSE +├── Makefile +├── README.en.md +├── README.md +└── test //所有测例都在此文件夹下 +``` + +- **exercises**:这个文件夹包含了所有的习题代码。每个习题可以作为一个子文件夹,其中包含习题的源代码、数据文件和任何相关的资源。 +- **LICENSE**:项目的许可证文件,定义了项目的使用、修改和分发的法律条款。 +- **Makefile**:一个自动化构建脚本,用于编译、测试和清理项目。通过运行 `make` 命令,可以执行 Makefile 中定义的任务。 +- **test**:这个文件夹包含了所有的测试用例。每个测试用例可以作为一个子文件夹或文件,用于验证习题代码的正确性。 + +### Makefile 内容 + +实验框架的 Makefile: + +```Makefile +# Makefile for C language exercise project + +# Define the directory where the exercise files are stored +EXERCISE_DIR = ./exercises + +# Define the test directory +TEST_DIR = ./test + +# Define the build directory +BUILD_DIR = ./build + +# Ensure the test and build directories exist +$(shell mkdir -p $(TEST_DIR) $(BUILD_DIR)) + +# Define the list of exercises +EXERCISES = $(wildcard $(EXERCISE_DIR)/*.c) + +# Define the list of executables +EXECUTABLES = $(patsubst $(EXERCISE_DIR)/%.c, $(BUILD_DIR)/%, $(EXERCISES)) + +# Define compiler and linker flags +CC = gcc +CFLAGS = -Wall -Wextra -std=c99 +LDFLAGS = -lm + +# Default target: build all executables +all: $(EXECUTABLES) clean + +# Build rule for each executable +$(BUILD_DIR)/%: $(EXERCISE_DIR)/%.c + $(CC) -o $@ $< $(CFLAGS) $(LDFLAGS) + +# Clean rule to remove all executables and object files +clean: + rm -f $(EXECUTABLES) $(BUILD_DIR)/*.o + +# Generate test cases rule +generate-test-cases: $(EXECUTABLES) + @for exe in $(EXECUTABLES); do \ + ./$$exe > $(TEST_DIR)/$$(basename $$exe).out; \ + done + @$(MAKE) clean + +# Test rule to compare output with expected results +test-output: $(EXECUTABLES) + @for exe in $(EXECUTABLES); do \ + exercise_name=$$(basename $$exe); \ + expected=$$(cat $(TEST_DIR)/$${exercise_name}.out); \ + actual=$$($$exe); \ + if [ "$$expected" = "$$actual" ]; then \ + echo "Test for $${exercise_name} passed."; \ + else \ + echo "Test for $${exercise_name} failed."; \ + echo "Expected:"; echo "$$expected"; \ + echo "Actual:"; echo "$$actual"; \ + fi; \ + done + @$(MAKE) clean + +# New target to save test results and count pass rate in JSON format +save-test-results: $(EXECUTABLES) + @total=0; \ + passed=0; \ + > $(BUILD_DIR)/test_results.json; \ + for exe in $(EXECUTABLES); do \ + exercise_name=$$(basename $$exe); \ + expected=$$(cat $(TEST_DIR)/$${exercise_name}.out); \ + actual=$$($$exe); \ + total=$$((total+1)); \ + if [ "$$expected" = "$$actual" ]; then \ + passed=$$((passed+1)); \ + fi; \ + done; \ + echo "{\"channel\": \"gitee\",\"courseId\": 1558,\"ext\": \"aaa\",\"name\": \"\",\"score\": $$passed,\"totalScore\": 5}" > $(BUILD_DIR)/test_results.json + @$(MAKE) clean + +.PHONY: all clean generate-test-cases test-output save-test-results + +``` + +- **目录定义**: + - `EXERCISE_DIR`:存放练习文件的目录。 + - `TEST_DIR`:存放测试文件的目录。 + - `BUILD_DIR`:存放构建产物的目录。 +- **文件和目录操作**: + - `mkdir -p $(TEST_DIR) $(BUILD_DIR)`:确保测试和构建目录存在。 +- **编译和链接**: + - `CC = gcc`:定义编译器为gcc。 + - `CFLAGS = -Wall -Wextra -std=c99`:编译选项,包括警告和C99标准。 + - `LDFLAGS = -lm`:链接选项,链接数学库。 +- **构建规则**: + - `$(BUILD_DIR)/%: $(EXERCISE_DIR)/%.c`:为每个练习文件生成对应的执行文件。 +- **清理规则**: + - `clean`:删除所有执行文件和构建目录下的对象文件。 +- **测试规则**: + - `generate-test-cases`:生成测试用例。 + - `test-output`:运行测试并比较输出结果。 + - `save-test-results`:保存测试结果和通过率到JSON文件。 + +**使用 makefile 进行实验** + +在实验过程中,Makefile提供了自动化构建和测试的便利。以下是如何使用这个Makefile进行实验的步骤: + +1. **编译所有练习**: + + ```bash + make + ``` + + 这个命令会编译所有练习文件,并在`BUILD_DIR`目录下生成对应的执行文件。 + +2. **生成测试用例**: + + ```bash + make generate-test-cases + ``` + + 这个命令会运行所有执行文件,并将输出保存为测试用例。 + +3. **运行测试**: + + ```bash + make test-output + ``` + + 这个命令会比较每个执行文件的输出与预期结果,并显示测试结果。 + +4. **保存测试结果**: + + ```bash + make save-test-results + ``` + + 这个命令会计算测试的通过率,并将结果保存到`test_results.json`文件中。 + +5. **清理**: + + ```bash + make clean + ``` + + 这个命令会删除所有构建产物和对象文件,保持环境整洁。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-02.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-02.md new file mode 100644 index 0000000000000000000000000000000000000000..09b828a643cfbd6591ea4683ff7de9b9ec572c3b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-02.md @@ -0,0 +1,40 @@ +## 本地测试流程 + +在实验过程中,本地测试是验证代码正确性的关键步骤。以下是本地测试的具体流程: + +- **运行所有题目的测试**: + + ```bash + make test-output + ``` + + 这个命令会执行所有题目的测试,并输出测试结果。确保你的代码在提交之前通过了所有本地测试。 + +- **测试并保存结果**: + + ```bash + mkdir build + make save-test-results + ``` + + 这个命令会创建一个名为 `build` 的目录,并在该目录下生成一个 `test_results.json` 文件,用于保存测试结果。这个文件对于记录和跟踪实验进度非常有用。 + +### 测试结果文件格式 + +`test_results.json` 文件的格式如下: + +```json +{ + "channel": "gitee", // 提交渠道 + "courseId": 1558, // 课程ID + "ext": "aaa", + "name": "xukunyuan-star", // 提交者信息 + "score": 5, // 提交者当前分数 + "totalScore": 5 // 当前实验总分 +} +``` + +这个文件包含了提交渠道、课程ID、扩展信息、提交者名称、当前分数以及实验总分等关键信息。确保每次测试后都更新这个文件,以便于跟踪实验的进度和成绩。 + +**注意,在本地测试的过程中,请注意不要破坏测例** + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-03.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-03.md new file mode 100644 index 0000000000000000000000000000000000000000..cf390cb5b01a6ba805fd1f25531bcf73b3c907b3 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-03.md @@ -0,0 +1,43 @@ +## 实验相关资料汇总 + +导学阶段的实验相对简单,以下是一部分实验相关资料的汇总: + +**make 与 Makefile 教程** + +[make 命令教程-阮一峰](https://www.ruanyifeng.com/blog/2015/02/make.html) + +阮一峰的这篇博客详细介绍了make命令及其配置文件Makefile的使用。make是一个构建自动化工具,它根据Makefile中的规则来编译和链接程序。这篇文章适合那些希望提高项目构建效率的开发者。 + +[跟我一起写 Makefile](https://seisman.github.io/how-to-write-makefile/) + +这本书籍风格的教程深入浅出地讲解了如何编写Makefile。它从基础规则到高级技巧,逐步引导读者掌握Makefile的编写,适合希望深入理解make工具的读者。 + +**C语言教程** + +[C语言教程-菜鸟教程](https://www.runoob.com/cprogramming/c-tutorial.html) + +菜鸟教程提供了全面的C语言学习资源,从基础语法到高级特性,适合初学者和希望复习C语言的开发者。这个教程的特点是实例丰富,易于理解。 + +**Linux 教程** + +[Linux教程-菜鸟教程](https://www.runoob.com/linux/linux-tutorial.html) + +菜鸟教程的Linux部分覆盖了从Linux基础命令到系统管理的各个方面。这个教程适合希望快速入门Linux操作系统的用户。 + +[Linux系统教程(笔记)](https://zq99299.github.io/linux-tutorial/) + +这是一个个人笔记式的Linux教程,内容详实,适合有一定基础并希望深入学习Linux系统的用户。 + +[PA实验-Linux入门教程](https://nju-projectn.github.io/ics-pa-gitbook/ics2021/linux.html) + +鼎鼎大名的 PA 实验的开头章节,很适合用于 Linux 使用的上手。 + +**算法与数据结构** + +[数据结构与算法-菜鸟教程](https://www.runoob.com/data-structures/data-structures-tutorial.html) + +菜鸟教程的数据结构与算法部分提供了基础的数据结构和算法知识,适合初学者学习。 + +[ALGORITHM-TUTORIAL](https://github.com/dunwu/algorithm-tutorial) + +这是一个GitHub上的算法教程项目,包含了多种算法的实现和解释,适合希望提高算法能力的开发者。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-04.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-04.md new file mode 100644 index 0000000000000000000000000000000000000000..5b180dc41c4c0c2b25d89edd2ae77450ba7b974b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-04.md @@ -0,0 +1,50 @@ +## 第一题:输出 Hello World! + +**实验题目** + +补全代码框架,向控制台打印“Hello World!” + +**实验要求** + +1. **编写一个C程序**:使用C语言编写一个简单的程序。 +2. **打印输出**:在控制台(或命令行界面)上打印出“Hello, World!”这条消息。 + +**实验代码框架** + +```c +//01_helloworld.c + +#include + +int main(){ + + // Print "Hello World!" to the console + + + + + return 0; +} +``` + +### 实验解析 + +**代码实现** + +```c +#include + +int main() { + // 打印 "Hello, World!" 到控制台 + printf("Hello, World!\n"); + + return 0; +} +``` + +**代码解释** + +- **`#include `**:这是一个预处理指令,用于包含标准输入输出头文件`stdio.h`。这个头文件包含了`printf`函数的声明,`printf`函数用于在控制台上输出文本。 +- **`int main()`**:这是C程序的主函数,程序从这里开始执行。`int`表示这个函数返回一个整数值。 +- **`printf("Hello, World!\n");`**:这是一个函数调用,`printf`是C标准库中的一个函数,用于格式化输出。在这个例子中,它被用来输出字符串“Hello, World!”。`\n`是一个转义字符,表示换行,使得下一次输出从新的一行开始。 +- **`return 0;`**:这是`main`函数的返回语句。在C语言中,`main`函数返回0通常表示程序正常结束。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-05.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-05.md new file mode 100644 index 0000000000000000000000000000000000000000..1e33c124c6e0c6dff4828b878f9f5e796bd78b7c --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-05.md @@ -0,0 +1,89 @@ +## 第二题:基本循环 + +**实验题目** + +编写一个C程序,打印出从1到10的整数序列。 + +使用循环实现迭代,并且每次迭代结束后,通过标准输出打印当前的计数值,每个数值打印在新的一行上。 + +**实验要求** + +1. **循环结构**:使用循环(如`for`循环、`while`循环或`do-while`循环)来实现迭代。 +2. **打印输出**:每次迭代结束后,通过标准输出(通常是终端或命令行界面)打印当前的计数值。 +3. **格式要求**:每个数值打印在新的一行上。 + +**实验代码框架** + +```C +//02_loop.c + +#include + +/** + * 编写一个C程序,打印出从1到10的整数序列。 + * 使用循环实现迭代,并且每次迭代结束后,通过标准输出打印当前的计数值,每个数值打印在新的一行上。 +*/ + +int main(void) +{ + //TODO + + return 0; +} +``` + +### 实验解析 + +**代码实现** + +#### 使用`for`循环实现 + +```c +#include + +int main(void) +{ + // 使用for循环打印1到10的整数 + for (int i = 1; i <= 10; i++) { + printf("%d\n", i); + } + + return 0; +} +``` + +#### 使用`while`循环实现 + +```c +#include + +int main(void) +{ + int i = 1; + // 使用while循环打印1到10的整数 + while (i <= 10) { + printf("%d\n", i); + i++; + } + + return 0; +} +``` + +#### 使用`do-while`循环实现 + +```c +#include + +int main(void) +{ + int i = 1; + // 使用do-while循环打印1到10的整数 + do { + printf("%d\n", i); + i++; + } while (i <= 10); + + return 0; +} +``` diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-06.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-06.md new file mode 100644 index 0000000000000000000000000000000000000000..871cf0bee1f8f886849819d9d3edb7e80b13c1bf --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-06.md @@ -0,0 +1,76 @@ +## 第三题:打印九九乘法表 + +**实验题目** + +编写一个C程序,打印出9x9乘法表的一部分。 + +程序应该使用两个嵌套的 for 循环来实现。外层循环控制乘法表的行数,从1到9;内层循环控制每行中的列数,列数应该等于当前的行数。 + +在每个内层循环中,程序应该打印出当前列数和行数的乘积,并且每个乘积后面跟随一个制表符 \t 以保持格式整齐。 + +每行结束后,程序应该打印一个换行符 \n 以开始新的一行。 + +参考输出格式 printf("%d*%d=%d\t", j, i, i * j); + +**实验要求** + +1. **使用嵌套循环**:程序需要使用两个嵌套的`for`循环来实现乘法表的打印。 +2. **外层循环**:外层循环控制乘法表的行数,从1到9。 +3. **内层循环**:内层循环控制每行中的列数,列数应该等于当前的行数。 +4. **打印乘积**:在每个内层循环中,程序应该打印出当前列数和行数的乘积,并且每个乘积后面跟随一个制表符`\t`以保持格式整齐。 +5. **换行**:每行结束后,程序应该打印一个换行符`\n`以开始新的一行。 + +**实验代码框架** + +```c +//03_nested_loops.c + +#include + +/** + * 题目描述: + * 编写一个C程序,打印出9x9乘法表的一部分。 + * 程序应该使用两个嵌套的 for 循环来实现。外层循环控制乘法表的行数,从1到9;内层循环控制每行中的列数,列数应该等于当前的行数。 + * 在每个内层循环中,程序应该打印出当前列数和行数的乘积,并且每个乘积后面跟随一个制表符 \t 以保持格式整齐。 + * 每行结束后,程序应该打印一个换行符 \n 以开始新的一行。 + * 参考输出格式 printf("%d*%d=%d\t", j, i, i * j); + */ + +int main(void) +{ + //TODO + + return 0; +} +``` + +### 实验解析 + +**代码实现** + +```c +#include + +int main(void) +{ + // 外层循环控制行数 + for (int i = 1; i <= 9; i++) { + // 内层循环控制列数,列数等于当前行数 + for (int j = 1; j <= i; j++) { + // 打印乘积和制表符 + printf("%d*%d=%d\t", j, i, i * j); + } + // 每行结束打印换行符 + printf("\n"); + } + + return 0; +} +``` + +**代码解释** + +- **外层循环**:`for (int i = 1; i <= 9; i++)`,这个循环从1到9,代表乘法表的行数。 +- **内层循环**:`for (int j = 1; j <= i; j++)`,这个循环的次数由外层循环的当前值`i`决定,即每行的列数等于当前的行数。 +- **打印乘积**:`printf("%d*%d=%d\t", j, i, i * j);`,这个语句打印出当前列数`j`和行数`i`的乘积,并且每个乘积后面跟随一个制表符`\t`,以保持输出格式整齐。 +- **换行**:`printf("\n");`,这个语句在每行结束后打印一个换行符`\n`,以开始新的一行。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-07.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-07.md new file mode 100644 index 0000000000000000000000000000000000000000..9cf6c452b7c4fa2234514d75d711c87e4681722e --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-07.md @@ -0,0 +1,82 @@ +## 第四题:求素数 + +**实验题目** + +查找100以内的最大素数 + +**实验要求** + +1. **查找素数**:程序需要查找100以内的所有素数。 +2. **找到最大素数**:在找到的所有素数中,确定最大的一个。 + +**实验代码框架** + +```c +// 04_prime_number.c + +#include +#include + +//查找100以内的最大素数 + +int main(void) +{ + int i, j; + int max = 0; + + for (i = 1; i <= 100; i++) + { + //TODO + } + + printf("max = %d\n", max); + + return 0; +} +``` + +### 实验解析 + +**代码实现** + +```c +#include +#include + +int main(void) +{ + int i, j; + int max = 0; + + for (i = 1; i <= 100; i++) + { + // 检查i是否为素数 + int isPrime = 1; // 假设i是素数 + for (j = 2; j <= sqrt(i); j++) + { + if (i % j == 0) + { + isPrime = 0; // i不是素数 + break; + } + } + // 如果i是素数且大于当前的最大素数,更新max + if (isPrime && i > max) + { + max = i; + } + } + + printf("max = %d\n", max); + + return 0; +} +``` + +**代码解释** + +- **外层循环**:`for (i = 1; i <= 100; i++)`,这个循环遍历1到100之间的所有数字。 +- **内层循环**:`for (j = 2; j <= sqrt(i); j++)`,这个循环用于检查当前的`i`是否为素数。内层循环从2开始,到`sqrt(i)`结束,因为一个数如果可以被小于或等于其平方根的数整除,那么它就不是素数。 +- **检查素数**:`if (i % j == 0)`,如果`i`能被`j`整除,则`i`不是素数,将`isPrime`设置为0,并跳出内层循环。 +- **更新最大素数**:`if (isPrime && i > max)`,如果`i`是素数且大于当前的最大素数`max`,则更新`max`为`i`。 +- **输出结果**:`printf("max = %d\n", max);`,循环结束后,打印出找到的最大素数。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-08.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-08.md new file mode 100644 index 0000000000000000000000000000000000000000..67ca979c6051977420b526c49e54ae14c521460d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-08.md @@ -0,0 +1,104 @@ +## 第五题:约瑟夫环 + +**实验题目** + +给定100个人站成一圈,从第1个人开始依次报数。 + +每数到3的人将会被淘汰,然后继续从下一个人开始报数。 + +这个过程会一直持续,直到所有的人都被淘汰。 + +请编写一个C语言程序来模拟这个过程,并且输出每一个被淘汰人的编号。 + +要求:输出每一个被淘汰人的编号,每淘汰一个人输出一行,格式为:"%d out \n"(每输出一次换行) + +**实验要求** + +1. **模拟约瑟夫环**:程序需要模拟100个人围成一圈,从第1个人开始报数,每数到3的人被淘汰的过程。 +2. **输出淘汰顺序**:程序需要输出每一个被淘汰人的编号,每淘汰一个人输出一行,格式为:`"%d out \n"`。 + +**实验代码框架** + +```c +// 05_josephus_ring.c + +#include + +/** + * 给定100个人站成一圈,从第1个人开始依次报数。 + * 每数到3的人将会被淘汰,然后继续从下一个人开始报数。 + * 这个过程会一直持续,直到所有的人都被淘汰。 + * 请编写一个C语言程序来模拟这个过程,并且输出每一个被淘汰人的编号。 + * 要求:输出每一个被淘汰人的编号,每淘汰一个人输出一行,格式为:"%d out \n"(每输出一次换行) +*/ + +#define ALL_NUM 100 +#define COUNT_NUM 3 +#define OUT_NUM 3 + +/* people id array such as (1,2,3,4,5,6) */ +int people[ALL_NUM]; + +int main(void) +{ + int left; /* 剩余人数 */ + int pos; /* 当前报数位置 */ + int step; /* 当前报数 */ + + //TODO + + + + return 0; +} +``` + +### 实验解析 + +**代码实现** + +```c +#include + +#define ALL_NUM 100 // 总人数 +#define COUNT_NUM 3 // 报数到3的人被淘汰 +#define OUT_NUM 3 // 淘汰的数字 + +int people[ALL_NUM]; // 人员编号数组 + +int main(void) +{ + int left = ALL_NUM; // 剩余人数 + int pos = 0; // 当前报数位置 + int step = 0; // 当前报数 + + while (left > 0) + { + // 模拟报数 + step++; + if (step == COUNT_NUM) + { + // 输出被淘汰的人的编号 + printf("%d out\n", people[pos] + 1); + // 淘汰这个人 + for (int i = pos; i < left - 1; i++) + { + people[i] = people[i + 1]; + } + left--; + step = 0; // 重置报数 + } + // 移动到下一个人 + pos = (pos + 1) % left; + } + + return 0; +} +``` + +**代码解释** + +- **初始化**:定义了总人数`ALL_NUM`,淘汰数字`COUNT_NUM`,以及一个数组`people`来存储每个人的编号。 +- **模拟报数**:使用一个循环来模拟报数过程,直到所有人都被淘汰。 +- **报数和淘汰**:`step`变量用于记录当前报数,当`step`等于`COUNT_NUM`时,表示当前报数的人应该被淘汰。此时,输出该人的编号,并从数组中移除这个人,然后重置`step`。 +- **移动到下一个人**:使用`pos = (pos + 1) % left`来移动到下一个人,这里使用了取模运算来保证在数组长度变化时,`pos`始终指向有效的位置。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch4-09.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-09.md new file mode 100644 index 0000000000000000000000000000000000000000..ee5bc67e866c3c1a7bb79bc24134c12084da6fd7 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch4-09.md @@ -0,0 +1,20 @@ +## 导学阶段实验相关问题与解决方案 + +在导学阶段的实验中,学生们可能会遇到各种问题,这些问题可能涉及代码实现、测试案例管理以及实验顺序等方面。以下是对这些常见问题的详细解答和建议解决方案: + +### 1.我按照题目的要求完成了代码,但是并没有通过测试 + +当你发现自己的代码没有通过测试时,首先应该回顾题目描述,特别是关于输出格式的部分。确保你的程序输出严格遵循了题目要求的格式。例如,如果题目要求输出结果时每行一个数字,并且每个数字后面跟着一个空格,那么你的程序输出也应该如此。如果输出格式不正确,即使算法逻辑正确,也可能导致测试失败。因此,仔细检查并调整输出格式是解决此类问题的第一步。 + +### 2.我不小心破坏了测例怎么办 + +在实验过程中,如果你不小心修改或删除了测试案例,不必过于担心。你可以选择以下两种方法之一来恢复测试案例: + +- **重新从模板仓库 fork**:如果你之前是从一个模板仓库 fork 出来的,你可以再次 fork 一个新的仓库,这样你将获得一个包含完整测试案例的新副本。 +- **直接获取测例**:如果模板仓库提供了测试案例的下载,你可以直接下载最新的测试案例文件,并将其添加到你的项目中。 + +这两种方法都可以帮助你快速恢复测试环境,继续进行实验。 + +### 3.是否可以不按照习题文件的顺序完成习题 + +答案是肯定的。虽然习题文件可能按照一定的逻辑顺序排列,但这并不意味着你必须严格按照这个顺序来完成习题。你可以根据自己的学习进度和理解程度,选择先完成你认为更重要的或者更感兴趣的习题。这种灵活性可以帮助你更好地掌握知识点,并根据自己的学习节奏进行调整。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch5-00.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-00.md new file mode 100644 index 0000000000000000000000000000000000000000..3cc28352b7855194c2269a973ce6ec9991ddc57d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-00.md @@ -0,0 +1,8 @@ +# 提交成绩 + +经过上一章的学习与讲解,相信大家都已经顺利完成了实验,本章将会引导大家提交实验的成绩。 + +* [流水线测试过程](ch5-01.md) +* [排行榜成绩查看与绑定](ch5-02.md) +* [如何晋级](ch5-03.md) +* [提交过程相关问题与解决方案](ch5-04.md) diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch5-01.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-01.md new file mode 100644 index 0000000000000000000000000000000000000000..58cab40097f81a01da59fb2d5ecaec46c8944cd6 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-01.md @@ -0,0 +1,93 @@ +## 流水线测试过程 + +**流水线配置** + +```yaml +version: '1.0' +name: test +displayName: test +triggers: + trigger: auto + push: + branches: + prefix: + - '' +variables: + POST_API: *** +stages: + - name: build-test + displayName: build-test + strategy: naturally + trigger: auto + executor: [] + steps: + - step: build@gcc + name: build_gcc + displayName: build + gccVersion: '9.4' + commands: + - apt-get update + - '' + - apt-get install -y curl + - '' + - mkdir build + - '' + - make test-output + - '' + - make save-test-results + - '' + - gitee_id="$GITEE_PIPELINE_TRIGGER_USER" + - '' + - json_file="build/test_results.json" + - '' + - existing_json=$(cat "$json_file") + - '' + - updated_json=$(echo "$existing_json" | jq --arg gitee_id "$gitee_id" '.name = $gitee_id') + - '' + - echo "$updated_json" > "$json_file" + - '' + - cat build/test_results.json + - '' + - 'curl -X POST "$POST_API" -H "accept: application/json;charset=utf-8" -H "Content-Type: application/json" -d "$(cat build/test_results.json)" -v' + - '' + - '' + artifacts: + - name: test_results + path: + - ./build/test_results.json + caches: [] + notify: [] + strategy: + retry: '0' + +``` + +流水线的配置文件定义了一个名为“test”的流水线,它具有自动触发的特性。该流水线包含一个阶段(stage),名为“build-test”,其目的是构建和测试代码。 + +其包含以下几个步骤: + +**环境准备** + +在“build-test”阶段中,首先执行的是环境准备步骤。这包括更新包列表(apt-get update)和安装必要的软件(apt-get install -y curl)。这些步骤确保了构建和测试环境的一致性和可用性。 + +**构建与测试** + +接下来,流水线创建一个名为“build”的目录,并在其中执行构建命令(make test-output)和保存测试结果的命令(make save-test-results)。这些命令是构建和测试过程的核心。 + +**结果处理** + +测试完成后,流水线会读取build/test_results.json文件中的测试结果,并使用jq工具更新其中的giteeName字段。这一步骤确保了测试结果中包含了执行流水线的用户的Gitee ID。 + +**结果提交** + +更新后的测试结果通过curl命令被发送到指定的API($POST_API)。这一步骤实现了测试结果的自动化提交,使得测试结果可以被远程服务器接收和处理。 + +**结果归档** + +最后,流水线将build/test_results.json文件作为构建产物(artifact)归档,以便后续的分析和审查。 + +### 如何进行流水线测试 + +完成实验后,请上传至远程仓库运行 CI。详细执行情况与输出可在流水线页面查看。 + +![](resource/ch5-02-01.png) diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch5-02.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-02.md new file mode 100644 index 0000000000000000000000000000000000000000..f5638d8585f344c34978827c0231eec7d01bed0f --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-02.md @@ -0,0 +1,36 @@ +## 排行榜成绩查看与绑定 + +**导学阶段排行榜地址** + +为了方便参与者查看自己的成绩和排名,训练营提供了一个专门的排行榜页面。你可以通过以下链接访问导学阶段的晋级榜单: + +[导学阶段晋级榜单](https://opencamp.cn/EulixOS/camp/202401/stage/0?tab=rank) + +在通过在线的 CI 评测后,你的成绩会被发送到训练营的排行榜上。 + +![](resource/ch1/5.png) + +#### 排行榜信息展示 + +当你访问排行榜页面时,你会看到一个包含多个列的表格,其中详细列出了每位参与者的相关信息: + +- **排名**:显示你在所有参与者中的相对位置。 +- **姓名(昵称)**:显示你的姓名或昵称,用于标识个人。 +- **分数**:显示你通过在线 CI 评测获得的成绩。 +- **最后提交时间**:显示你最近一次提交代码的时间。 + +这些信息帮助你了解自己在训练营中的表现,并与他人进行比较。 + +#### 成绩绑定的重要性 + +为了确保你的成绩能够正确地显示在排行榜上,并在训练营中得到认可,你需要在训练营网站上正确填写你的 GitHubName 或 GiteeName。这些信息用于将你的提交与你的个人账户关联起来。如果这些信息填写不正确或不完整,可能会导致成绩无法正确绑定,从而影响你的排名和晋级机会。 + +#### 操作步骤 + +1. **访问个人信息页面**:登录训练营网站,找到并访问个人信息页面。 +2. **填写 GitHubName/GiteeName**:在个人信息页面中,找到相关的字段,填写你的 GitHubName 或 GiteeName。确保信息的准确性。 +3. **保存更改**:填写完毕后,保存你的更改。 +4. **提交代码**:在完成代码编写并通过本地测试后,提交你的代码到训练营的评测系统。 +5. **查看排行榜**:提交后,访问排行榜页面,查看你的成绩和排名是否正确显示。 + +通过遵循这些步骤,你可以确保你的成绩被正确地绑定到排行榜上,从而在训练营中获得公正的评价和机会。如果有任何疑问或遇到问题,不要犹豫,及时联系训练营的技术支持团队寻求帮助。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch5-03.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-03.md new file mode 100644 index 0000000000000000000000000000000000000000..c14d9261bb261b47cd74a375e9e9322c41cede53 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-03.md @@ -0,0 +1,19 @@ +## 如何晋级 + +#### 晋级要求 + +在导学阶段,我们鼓励每位学员积极参与,但并没有设定强制性的晋级要求。为了确保学员能够顺利过渡到正式阶段,我们建议学员在进入正式阶段之前,完成基础阶段的实验与课程。这不仅有助于巩固基础知识,也为后续的实验和项目打下坚实的基础。 + +#### 晋级依据 + +晋级的依据主要基于训练营教学系统排行榜上显示的分数。与正式阶段不同,导学阶段并不强制要求达到特定的分数。只要学员报名参加了训练营,并积极参与了导学阶段的学习,就有资格晋级到正式阶段。 + +#### 晋级过程 + +为了顺利进行后续的学习和交流,晋级过程中需要加入对应的微信群聊。学员可以通过联系训练营的班主任或管理人员来加入这些群聊。具体的加群方式和相关信息,可以在训练营教学系统中找到。 + +#### 队伍晋级 + +对于组队学习的学员,队伍晋级是一个重要的环节。为了确保队伍的有效性,全队所有学员都必须完成个人晋级。只有当队伍中的所有成员都成功晋级后,整个队伍才能晋级。值得注意的是,队伍晋级并不影响个人晋级,这意味着即使队伍未能晋级,个人仍然有机会根据个人表现晋级。 + +为了保证队伍的有效性,我们规定两人及两人以上的队伍才能被视为有效组队。这有助于确保团队合作的质量,并鼓励学员之间的交流与协作。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch5-04.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-04.md new file mode 100644 index 0000000000000000000000000000000000000000..be928c46c41a1f9faa9aed4738e095f6cc3f8302 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch5-04.md @@ -0,0 +1,17 @@ +## 提交过程相关问题与解决方案 + +### 1.晋级榜单上的成绩没有准确链接到用户信息 + +如果你发现晋级榜单上的成绩没有正确链接到你的用户信息,首先应该检查你的个人信息页面。确保你已经正确填写了你的 GitHubName 或 GiteeName。这些信息是用来关联你的成绩和你的账户的。如果信息填写正确,但问题依旧存在,尝试重新提交你的代码。这通常可以刷新成绩和用户信息的链接。如果问题仍然没有解决,可以联系技术支持寻求帮助。 + +### 2.本地代码无法提交到远程仓库 + +当你遇到本地代码无法提交到远程仓库的问题时,首先应该检查你的网络连接是否正常。网络问题是导致提交失败的常见原因。如果网络没有问题,建议使用 SSH 协议进行提交,因为 SSH 通常比 HTTPS 更稳定,尤其是在处理大量数据时。如果使用 SSH 后问题依旧,那么可能是由于内容冲突导致的。内容冲突通常发生在多人在同一分支上工作时。解决冲突的方法是手动合并代码,确保每个更改都被正确地包含在提交中。 + +### 3.使用 wsl 进行实验,但因 wsl 网络问题无法提交 + +如果你在使用 Windows Subsystem for Linux (WSL) 进行实验时遇到网络问题,导致无法提交代码,可以尝试以下解决方案: + +- **复制仓库到 Windows 文件系统**:将你的本地仓库从 WSL 复制到 Windows 文件系统中,然后使用 Git 客户端进行提交。这种方法可以绕过 WSL 的网络限制,因为 Windows 文件系统通常有更好的网络兼容性。 +- **检查 WSL 网络设置**:确保 WSL 的网络设置正确,包括防火墙设置和网络配置。有时候,简单的网络设置调整就可以解决问题。 + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch6-00.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch6-00.md new file mode 100644 index 0000000000000000000000000000000000000000..c64e26bf40665cc81718f0e78302884d8a884ee8 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch6-00.md @@ -0,0 +1,7 @@ +## 晋级之后 + +经过之前的学习,相信大家已经完成了导学阶段的学习与晋级要求,本章将引导大家在下一阶段正式开始之前的余下时间可以拓展的内容,以及对后续学习的引导。 + +* [拓展内容汇总](ch6-01.md) +* [后续学习指引](ch6-02.md) +* [部分优质实践资源](ch6-03.md) diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch6-01.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch6-01.md new file mode 100644 index 0000000000000000000000000000000000000000..b6dcafc829b28fd569a73e833b7f4d724b180f10 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch6-01.md @@ -0,0 +1,113 @@ +## 拓展内容与学习资源汇总 + +*本节内容持续更新* + +### 导学课程相关学习资料 + +**导学阶段课程课件** + +[riscv-operating-system-mooc/slides](https://gitee.com/unicornx/riscv-operating-system-mooc/tree/main/slides) + +此链接包含导学阶段课程[《循序渐进,学习开发一个 RISC-V 上的操作系统 ](https://www.bilibili.com/video/BV1Q5411w7z5) 的全部课件,可作为视频课程的辅助之用。 + +**课程配套实验** + +[riscv-operating-system-mooc](https://gitee.com/unicornx/riscv-operating-system-mooc) + +此链接为导学阶段课程的配套实验代码仓库,实验难度不大,适合初学者了解系统编程与操作系统理论知识,推荐尝试。 + +**一个可运行课程实验的 RISCV CPU** + +[riscv-v-cpu](https://gitee.com/lizhanpeng2022/cpu_prj) + +此仓库为一位该课程[《循序渐进,学习开发一个 RISC-V 上的操作系统 》](https://www.bilibili.com/video/BV1Q5411w7z5) 的热心学员设计的 RISCV CPU,可在其上运行课程配套的实验代码。 + +**课程代码到物理机的移植记录** + +[riscv-operating-system-mooc/issues/I64EEQ](https://gitee.com/unicornx/riscv-operating-system-mooc/issues/I64EEQ) + +这里记录了该课程配套代码向物理机的移植记录,鼓励大家在完成本课程后积极参与这一活动,提高自己的系统编程与工程能力。 + +**一份来自b站本课程学员的学习笔记** + +[RISC-V入门(基础概念+汇编部分) 基于 汪辰老师的视频笔记](https://blog.csdn.net/bebebug/article/details/128039038?spm=1001.2014.3001.5501) + +### 其余学习资料 + +**uCore 实验指导书** + +[uCore OS(on RISC-V64)实验指导书](https://nankai.gitbook.io/ucore-os-on-risc-v64) + +uCore 实验同样为实现一个 RISCV 架构操作系统的教学用操作系统,其难度较于导学阶段课程颇高,但是内容完善,体系严整,可作为完成课程后的提高之用。 + +**rCore 实验指导书** + +[rCore-Tutorial-Book-v3](https://rcore-os.cn/rCore-Tutorial-Book-v3/) + +rCore 实验与 uCore 实验内容基本相同,但是 rCore 代码框架使用 Rust 语言编写,欲进行实验需要先行学习 Rust,但通过本实验可以学习 Rust base OS 这一新兴技术,建议学有余力的学员进行尝试。 + +**清华大学操作系统课程资料** + +[Tsinghua OS Slide Mdbook ](https://lzzs.fun/Tsinghua-OS-mdbook/) + +此链接整合了清华大学计算机系2024春季学习操作系统课程课堂幻灯片的主要内容,对幻灯片中的一些概念进行了简单补充。 + +**南京大学计算机系统基础实验(PA)** + +[南京大学 计算机科学与技术系 计算机系统基础课程实验 2024](https://ysyx.oscc.cc/docs/ics-pa/) + +**南京大学操作系统课程** + +[操作系统:设计与实现 (2023 春季学期)](https://jyywiki.cn/OS/2023/index.html) + +**静态程序分析** + +[Static Program Analysis Book](https://ranger-nju.gitbook.io/static-program-analysis-book) + +**有趣的 Git 学习平台** + +[Learn Git branch](https://learngitbranching.js.org/?locale=zh_CN) + +**make 与 Makefile 教程** + +[make 命令教程-阮一峰](https://www.ruanyifeng.com/blog/2015/02/make.html) + +阮一峰的这篇博客详细介绍了make命令及其配置文件Makefile的使用。make是一个构建自动化工具,它根据Makefile中的规则来编译和链接程序。这篇文章适合那些希望提高项目构建效率的开发者。 + +[跟我一起写 Makefile](https://seisman.github.io/how-to-write-makefile/) + +这本书籍风格的教程深入浅出地讲解了如何编写Makefile。它从基础规则到高级技巧,逐步引导读者掌握Makefile的编写,适合希望深入理解make工具的读者。 + +**C语言教程** + +[C语言教程-菜鸟教程](https://www.runoob.com/cprogramming/c-tutorial.html) + +菜鸟教程提供了全面的C语言学习资源,从基础语法到高级特性,适合初学者和希望复习C语言的开发者。这个教程的特点是实例丰富,易于理解。 + +[笨办法学C 中文版](https://wizardforcel.gitbooks.io/lcthw/content/) + +**Linux 教程** + +[Linux教程-菜鸟教程](https://www.runoob.com/linux/linux-tutorial.html) + +菜鸟教程的Linux部分覆盖了从Linux基础命令到系统管理的各个方面。这个教程适合希望快速入门Linux操作系统的用户。 + +[Linux系统教程(笔记)](https://zq99299.github.io/linux-tutorial/) + +这是一个个人笔记式的Linux教程,内容详实,适合有一定基础并希望深入学习Linux系统的用户。 + +[PA实验-Linux入门教程](https://nju-projectn.github.io/ics-pa-gitbook/ics2021/linux.html) + +鼎鼎大名的 PA 实验的开头章节,很适合用于 Linux 使用的上手。 + +[The Missing Semester of Your CS Education](https://missing.csail.mit.edu/) + +**算法与数据结构** + +[数据结构与算法-菜鸟教程](https://www.runoob.com/data-structures/data-structures-tutorial.html) + +菜鸟教程的数据结构与算法部分提供了基础的数据结构和算法知识,适合初学者学习。 + +[ALGORITHM-TUTORIAL](https://github.com/dunwu/algorithm-tutorial) + +这是一个GitHub上的算法教程项目,包含了多种算法的实现和解释,适合希望提高算法能力的开发者。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch6-02.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch6-02.md new file mode 100644 index 0000000000000000000000000000000000000000..8b5affe05f84c60a83d08420cc82a95e8b6cacd9 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch6-02.md @@ -0,0 +1,124 @@ +## 后续学习指引 + +### 正式实验题目列表 + +**基础阶段** + +**[2024-exercises-stage-1](https://gitee.com/LearningEulixOS/2024-exercises-stage-1)** + +| 编号 | 位置 | 简介 | 考察点 | 难度 | +| ---- | ----------------- | ------------------------------------------ | --------------- | ---- | +| 01 | `src/exercise-01` | 编写第一个 Makefile | 程序构建 | 入门 | +| 02 | `src/exercise-02` | 编写第一个测试 | 程序构建 | 入门 | +| 03 | `src/exercise-03` | 使用 Makefile 静态链接程序 | 程序构建 | 入门 | +| 04 | `src/exercise-04` | 使用 Makefile 构建第一个静态链接库 | 程序构建 | 入门 | +| 05 | `src/exercise-05` | 使用 Makefile 构建第一个动态链接库 | 程序构建 | 入门 | +| 11 | `src/exercise-11` | 编写一个简单的 ld 文件并指定内存区域 | 程序构建 | 基础 | +| 12 | `src/exercise-12` | 编写一个简单的 ld 文件并指定 text 起始地址 | 程序构建 | 基础 | +| 13 | `src/exercise-13` | 编写一个简单的 ld 文件并指定自定义 symbol | 程序构建 | 基础 | +| 14 | `src/exercise-14` | 编写一个简单的 ld 文件并指定自定义 section | 程序构建 | 基础 | +| 20 | `src/exercise-20` | 合并两个任务队列 | 数据结构 | 基础 | +| 21 | `src/exercise-21` | 按组反转一个任务队列 | 数据结构 | 基础 | +| 30 | `src/exercise-30` | 编写一个内核模块打印 hello world | 内核模块 | 入门 | +| 31 | `src/exercise-31` | 编写一个内核模块实现阶乘计算 | 内核模块 | 基础 | +| 32 | `src/exercise-32` | 编写一个内核模块实现字符串反转 | 内核模块 | 基础 | +| 33 | `src/exercise-33` | 编写一个内核模块实现平均数计算 | 内核模块 | 基础 | +| 34 | `src/exercise-34` | 编写一个内核模块实现线性查找 | 内核模块 | 基础 | +| 35 | `src/exercise-35` | 编写一个内核模块延时打印字符串 | 内核模块 | 入门 | +| 40 | `src/exercise-40` | 使用 RISC-V 内联汇编实现条件返回 | RISC-V 基础指令 | 基础 | +| 41 | `src/exercise-41` | 使用 RISC-V 内联汇编实现最大公因数求解 | RISC-V 基础指令 | 基础 | +| 42 | `src/exercise-42` | 使用 RISC-V 内联汇编实现数组元素查找 | RISC-V 基础指令 | 中等 | + +**进阶阶段** + +**[2024-exercises-stage-2](https://gitee.com/LearningEulixOS/2024-exercises-stage-2)** + +| 编号 | 位置 | 简介 | 考察点 | 难度 | +| ---- | ----------------- | -------------------------------------------------- | ------------------------------ | ---- | +| 22 | `src/exercise-22` | 矩阵相乘 | 矩阵 | 基础 | +| 23 | `src/exercise-23` | 2D 卷积操作 | 矩阵 | 基础 | +| 24 | `src/exercise-24` | 矩阵的原地转置 | 矩阵 | 基础 | +| 25 | `src/exercise-25` | 包含 0 的行列进行矩阵置零 | 矩阵 | 基础 | +| 26 | `src/exercise-26` | 查找矩阵中第 K 个最小的元素 | 矩阵 | 基础 | +| 36 | `src/exercise-36` | 编写一个内核模块求最大值 | 内核模块 | 基础 | +| 37 | `src/exercise-37` | 编写一个内核模块启动一个定时器 | 内核模块 | 基础 | +| 38 | `src/exercise-38` | 编写一个内核模块创建一个虚拟字符设备 | 内核模块 | 中等 | +| 39 | `src/exercise-39` | 编写一个内核模块实现一个简单的文件操作函数 | 内核模块 | 中等 | +| 43 | `src/exercise-43` | 使用内联 RISCV 汇编实现计算斐波那契数列的第 n 个数 | RISC-V 基础指令RISC-V 基础指令 | 中等 | +| 44 | `src/exercise-44` | 使用内联 RISCV 汇编实现整数数组求和 | RISC-V 基础指令 | 中等 | +| 45 | `src/exercise-45` | 使用内联 RISCV 汇编实现查找整数数组最大值 | RISC-V 基础指令 | 中等 | +| 46 | `src/exercise-46` | 使用内联 RISCV 汇编实现判断给定数组是否有序 | RISC-V 基础指令 | 中等 | +| 47 | `src/exercise-47` | 使用内联 RISCV 汇编实现给定数组目标元素个数 | RISC-V 基础指令 | 中等 | +| 50 | `src/exercise-50` | 模拟 FIFO 页面置换算法 | 操作系统 | 中等 | +| 51 | `src/exercise-51` | 模拟 LRU 页面置换算法 | 操作系统 | 中等 | +| 52 | `src/exercise-52` | 获取容器主机名 | 操作系统 | 基础 | +| 53 | `src/exercise-53` | 简单虚拟地址到物理地址的转换 | 操作系统 | 中等 | +| 54 | `src/exercise-54` | 模拟时间片轮转调度算法 | 操作系统 | 中等 | +| 55 | `src/exercise-55` | 模拟虚拟内存限制 | 操作系统 | 中等 | + +### 学习指引 + +#### 基础阶段 + +**[2024-exercises-stage-1](https://gitee.com/LearningEulixOS/2024-exercises-stage-1)** + +1. **Makefile 基础** + - **实验编号**:01, 02, 03, 04, 05 + - **学习目标**:掌握 Makefile 的基本使用,包括编写简单的 Makefile、静态和动态链接库的构建。 + - 学习步骤: + - 阅读相关文档,了解 Makefile 的基本语法和结构。 + - 逐步完成每个实验,理解每个命令的作用。 + - 尝试修改 Makefile,观察构建结果的变化。 +2. **链接器脚本编写** + - **实验编号**:11, 12, 13, 14 + - **学习目标**:学习如何编写链接器脚本,指定内存区域、text 起始地址、自定义符号和节。 + - 学习步骤: + - 理解链接器脚本的基本概念和作用。 + - 完成实验,注意观察链接结果与脚本的关系。 +3. **数据结构应用** + - **实验编号**:20, 21 + - **学习目标**:掌握基本的数据结构操作,如队列的合并和反转。 + - 学习步骤: + - 复习队列的基本操作。 + - 实现代码,测试不同情况下的队列操作。 +4. **内核模块开发** + - **实验编号**:30, 31, 32, 33, 34, 35 + - **学习目标**:学习内核模块的基本开发流程,包括打印信息、实现简单算法等。 + - 学习步骤: + - 了解内核模块的基本概念和开发环境配置。 + - 逐步完成每个实验,理解内核模块与用户态程序的区别。 +5. **RISC-V 内联汇编** + - **实验编号**:40, 41, 42 + - **学习目标**:学习 RISC-V 内联汇编的基本使用,实现简单的算法。 + - 学习步骤: + - 学习 RISC-V 指令集基础。 + - 编写汇编代码,理解汇编与 C 语言的交互。 + +#### 进阶阶段 + +**[2024-exercises-stage-2](https://gitee.com/LearningEulixOS/2024-exercises-stage-2)** + +1. **矩阵操作** + - **实验编号**:22, 23, 24, 25, 26 + - **学习目标**:深入理解矩阵操作,包括矩阵乘法、卷积、转置和元素查找。 + - 学习步骤: + - 学习矩阵的基本数学性质。 + - 实现代码,优化算法性能。 +2. **内核模块高级应用** + - **实验编号**:36, 37, 38, 39 + - **学习目标**:学习内核模块的高级应用,如定时器、虚拟设备和文件操作。 + - 学习步骤: + - 深入理解内核模块的工作原理。 + - 完成实验,注意安全性和稳定性。 +3. **RISC-V 内联汇编高级应用** + - **实验编号**:43, 44, 45, 46, 47 + - **学习目标**:掌握 RISC-V 内联汇编的高级应用,实现复杂算法。 + - 学习步骤: + - 深入学习 RISC-V 指令集。 + - 编写复杂的汇编代码,优化性能。 +4. **操作系统原理** + - **实验编号**:50, 51, 52, 53, 54, 55 + - **学习目标**:理解操作系统的基本原理,如页面置换算法、地址转换和调度算法。 + - 学习步骤: + - 学习操作系统的基本概念和原理。 + - 实现模拟算法,理解操作系统的工作机制。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/ch6-03.md b/mdbook/eulixos-camp-book-stage0-docs/src/ch6-03.md new file mode 100644 index 0000000000000000000000000000000000000000..2d8674d395ad949f3c2c6ef0b91eded9754c7e01 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/ch6-03.md @@ -0,0 +1,36 @@ +## 部分优质实践资源 + +**清华大学开源操作系统训练营** + +[2024春夏季开源操作系统训练营](https://opencamp.cn/os2edu/camp/2024spring) + +[训练营 GitHub 主页](https://github.com/LearningOS) + +开源操作系统训练营是由清华大学的陈渝老师和向勇老师于2020年发起,旨在通过使用Rust语言编写操作系统的实践,培养全国高校学生的操作系统开发技能。训练营全程免费,指导学员参与开源项目实战,探索设计与构建新一代安全高性能操作系统。 + +**自动驾驶OS开发训练营** + +[自动驾驶OS开发训练营](https://cicvedu.com/) + +自动驾驶OS开发训练营是国家智联网联汽车创新中心(简称“创新中心”)和清华大学携手打造的前沿技术培训项目,课程内容汲取了清华大学Rust OS训练营的精髓,紧密结合自动驾驶操作系统领域的应用实践,致力于为在校学生及自动驾驶领域的从业人员提供专业、系统的自动驾驶OS开发技能与知识培训,成为符合行业需求的优秀工程师。 + +**一生一芯** + +[一生一芯官网](https://ysyx.oscc.cc/) + +针对我国计算机专业当前面临的较为突出的人才培养问题,以及计算机处理器芯片设计人才不足的问题,中国科学院大学(简称“国科大”)计算机科学与技术学院立足已有的理论课堂与实验教学,联合中国科学院计算技术研究所(简称“计算所”)的科研工程支撑团队,于2019年8月启动了“一生一芯”开源处理器芯片教学流片实践项目计划。 + +[一生一芯介绍](https://ysyx.oscc.cc/project/intro.html) + +**全国大学生计算机系统能力大赛** + +[全国大学生计算机系统能力大赛官网](https://compiler.educg.net/#/) + +全国大学生计算机系统能力大赛是由系统能力培养研究专家组发起、由全国高校计算机教育研究会主办,面向高校大学生举办的全国性大赛,以学科竞赛推动专业建设和计算机领域创新人才培养体系改革、培育我国高端芯片及核心系统的技术突破与产业化后备人才为目标,是我国高校系统能力相关的高水平学科竞赛。目前,大赛设置CPU设计赛、操作系统赛、编译系统赛、数据库设计赛多个赛道。 + +**开源之夏** + +[开源之夏](https://summer-ospp.ac.cn/) + +开源之夏是由中国科学院软件研究所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,培养和发掘更多优秀的开发者,促进优秀开源软件社区的蓬勃发展,助力开源软件供应链建设。 + diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/foreword.md b/mdbook/eulixos-camp-book-stage0-docs/src/foreword.md new file mode 100644 index 0000000000000000000000000000000000000000..50a9e398797a1eef9661dad423a0aad81be18061 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/foreword.md @@ -0,0 +1,13 @@ +# 前言 + +**本文档尚在编写和调整中** + +随着信息技术的迅猛发展,操作系统作为计算机系统的基石和核心软件,其地位日益显著。在这样一个时代背景下,中科院软件所/中科南京软件技术研究院团队凭借其深厚的科研实力,基于我国自主研发的开源操作系统openEuler,精心打造了一款高性能、安全、易用的操作系统——傲来操作系统(EulixOS)。这款操作系统不仅凝聚了我国软件领域的最新科研成果,更在满足开源爱好者、科研人员以及学生们需求的同时,为在线服务、高性能计算、AI计算等多元化使用场景提供了卓越的体验。 + +为了进一步推广和普及傲来操作系统,让更多人了解并掌握这一先进技术,我们特别策划并举办了傲来操作系统训练营。此次训练营旨在为广大参与者提供一个系统学习、实践操作的平台,帮助他们在短时间内掌握傲来操作系统的使用方法。本书正是为此训练营的学员量身定制的学习引导手册,从导学阶段开始,为学员们的学习之路保驾护航。 + +本书采用mdbook进行编写,以简洁明了的排版风格,便于学员阅读与学习。同时,我们已将本书部署至线上地址:https://kunyuanxu-star.github.io/eulix-camp-book-stage0/,让学员们可以随时随地进行学习,充分利用碎片时间提升自己。此外,我们还为学员们提供了详尽的本地使用方法,确保在无法访问网络的情况下,学员们依然可以通过本地文档进行自学。 + +在内容编排上,本书首先对傲来操作系统的基本情况以及训练营的概况进行了详细介绍,让学员对整个学习过程有清晰的认识。随后,本书逐步引导学员进行环境配置、Git使用、前置知识学习等准备工作,为后续学习打下坚实基础。在此基础上,通过一系列精心设计的实验题目,帮助学员们掌握基本的编程技能,并熟练运用傲来操作系统。最后,本书还提供了晋级后的拓展内容、后续学习指引以及常见问题解决方案,助力学员们在操作系统领域不断深入探索。 + +我们衷心希望本书能成为学员们学习傲来操作系统的得力助手,陪伴他们在实践中不断进步,为我国操作系统的研究和应用贡献自己的力量。同时,我们也期待与各位学员共同探讨、交流,共同推动傲来操作系统的发展和完善,为我国软件产业的发展献出一份力量。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/introduce.md b/mdbook/eulixos-camp-book-stage0-docs/src/introduce.md new file mode 100644 index 0000000000000000000000000000000000000000..b8762490c81cc0a531d417f7aefa31ee641078e3 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/introduce.md @@ -0,0 +1,65 @@ +# 介绍 + +傲来操作系统(EulixOS)是由中科院软件所 / 中科南京软件技术研究院团队基于 openEuler 打造的操作系统发行版,其开发目标是集成软件所的最新科研成果,面向开源爱好者、科研人员和学生,为在线服务、高性能计算、AI 计算等使用场景提供一款安全、易用的操作系统。 + + + +本书用于为参与傲来操作系统训练营的学员提供导学阶段学习引导。 + +本书基于 mdbook 编写,已部署至 https://kunyuanxu-star.github.io/eulix-camp-book-stage0/ + +## 文档本地使用方法 + +需要 Rust 环境。 + +```shell +git clone https://github.com/kunyuanxu-star/eulix-camp-book-stage0.git +cd arceos-tutorial-book +cargo install mdbook +mdbook serve docs +``` + +## 文档大纲 + +- 前言 +- 介绍 +- 环境配置 +- 第一章:导学阶段基本信息 + - 训练营教学系统使用引导 + - 导学阶段视频课程链接与学习引导 + - 导学课程学习资料汇总 + - 开营仪式教学安排与资料汇总 + - 教学系统常见问题与解决方案 +- 第二章:基本 Git 使用与 Gitee 流水线入门 + - 常用 Git 指令 + - Gitee 使用入门 + - Gitee Go 的配置与使用 + - 流水线常见问题与解决方案 +- 第三章:前置知识 + - Linux 使用入门与配置 + - C 语言基本语法 + - make 基本使用与 Makefile + - 如何在非 Linux 环境中完成实验 +- 第四章:开始实验 + - 实验代码框架讲解 + - 本地测试流程 + - 实验相关资料汇总 + - 第一题:输出 Hello World! + - 第二题:基本循环 + - 第三题:打印九九乘法表 + - 第四题:求素数 + - 第五题:约瑟夫环 + - 导学阶段实验相关问题与解决方案 +- 第五章:提交成绩 + - 流水线测试过程 + - 排行榜成绩查看与绑定 + - 如何晋级 + - 提交过程相关问题与解决方案 +- 第六章:晋级之后 + - 拓展内容汇总 + - 后续学习指引 + - 部分优质实践资源 +- 其余常见问题与解决方案 +- 附录A:使用 Gitee 流水线信息进行调试 +- 附录B:实验所用到的其余专业前置知识 +- 附录C:部分工具的使用入门 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/problem.md b/mdbook/eulixos-camp-book-stage0-docs/src/problem.md new file mode 100644 index 0000000000000000000000000000000000000000..5a384ac9ca6017d4a81de02070c09e8cb95406e3 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/problem.md @@ -0,0 +1,17 @@ +# 其余常见问题与解决方案 + +### 1.导学阶段的实验是一定要完成的吗? + +虽然导学阶段的实验并非强制性要求完成,但我们强烈推荐每位学员积极参与。导学阶段的实验设计旨在帮助学员熟悉正式阶段实验的评测流程,包括代码提交、版本控制系统的使用以及持续集成(CI)的评测机制。通过完成这些实验,学员可以提前预演在正式阶段可能遇到的各种情况,从而减少正式实验时的困惑和错误,提高学习效率。 + +### 2.导学阶段的课程一定要全部完成吗? + +与实验类似,导学阶段的课程也是推荐完成而非强制。这些课程涵盖了操作系统的基础知识和关键概念,对于没有相关背景的学员来说,是理解后续复杂概念和实验的基础。完成导学阶段的课程可以帮助学员建立坚实的知识基础,为后续更深入的学习和实践打下良好的基础。 + +### 3.导学阶段实验的环境配置是否与正式实验相同? + +导学阶段的环境配置相对简单,主要是为了让学员快速上手并熟悉实验流程。而正式实验的环境配置则更为复杂,除了基本的环境设置外,还需要配置交叉编译工具链和本地QEMU环境。为了简化这一过程,我们提供了可以直接在QEMU中运行的工具链镜像。这意味着,一旦完成了导学阶段的实验,学员可以利用这些资源快速过渡到正式实验的环境配置,减少不必要的时间浪费。 + +### 4.为何要在导学阶段设置实验? + +导学阶段的实验设置主要是为了帮助学员适应远程实验的模式,特别是对于那些初次接触通过提交代码到远程仓库并通过CI进行评测的学员。这种模式在现代软件开发中非常常见,因此提前熟悉这一流程对于学员未来的学习和职业生涯都有很大帮助。此外,导学阶段的实验还可以帮助学员建立信心,通过实际操作来验证和巩固理论知识,为正式阶段的实验打下坚实的基础。 diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/05-1.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/05-1.png new file mode 100644 index 0000000000000000000000000000000000000000..375f55d220b1b6267afdc3e397bc77780f807a12 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/05-1.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/1.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/1.png new file mode 100644 index 0000000000000000000000000000000000000000..4da0c6ac67407d444b197b492114096db6055698 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/1.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/2.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/2.png new file mode 100644 index 0000000000000000000000000000000000000000..16ae6b7ad93f0786e9966e1cbaf937ad3bdb3957 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/2.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/3.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/3.png new file mode 100644 index 0000000000000000000000000000000000000000..2bff5750da4d809e4fdfa8317fbca4c531f55bf4 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/3.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/4.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/4.png new file mode 100644 index 0000000000000000000000000000000000000000..f036f1a7f391d393f0429edad48a486d45c3034f Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/4.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/5.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/5.png new file mode 100644 index 0000000000000000000000000000000000000000..8bee03ad9c5020bfbd270e2267f39c156e4dfca8 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/5.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/6.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/6.png new file mode 100644 index 0000000000000000000000000000000000000000..9eb03c001dbd57040fdc7c769f74a6cbd41cd343 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/6.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/7 b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/7 new file mode 100644 index 0000000000000000000000000000000000000000..6bf95dfb9c4f8f7cbdde770725ac392c5567da8b Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/7 differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/jiaoxueanpai.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/jiaoxueanpai.png new file mode 100644 index 0000000000000000000000000000000000000000..eac59e824ebbc972c2be0d2368c8f502302a4f74 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch1/jiaoxueanpai.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/02-1.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/02-1.png new file mode 100644 index 0000000000000000000000000000000000000000..da179a3504ee72d2f168551e95d6d42b97b360c3 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/02-1.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/chuagjian.jpg b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/chuagjian.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b40854e832cb1e89d53d59f60f563ebc70436c27 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/chuagjian.jpg differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/kaitong.jpg b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/kaitong.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7d03db416a8a9525fcdaa16e995d67741a8ccab3 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch2/kaitong.jpg differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch5-02-01.png b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch5-02-01.png new file mode 100644 index 0000000000000000000000000000000000000000..a5ba6417609be1a0175cbcb41de6aaedf1dafedd Binary files /dev/null and b/mdbook/eulixos-camp-book-stage0-docs/src/resource/ch5-02-01.png differ diff --git a/mdbook/eulixos-camp-book-stage0-docs/src/setup.md b/mdbook/eulixos-camp-book-stage0-docs/src/setup.md new file mode 100644 index 0000000000000000000000000000000000000000..eeec0b345fc4fec888740e3b1fdcb15f34370f3e --- /dev/null +++ b/mdbook/eulixos-camp-book-stage0-docs/src/setup.md @@ -0,0 +1,66 @@ +# 环境配置 + +为了顺利进行导学阶段的实验,为之后正式学习的实验做好准备,需要先进行一些简单的环境配置。 + +我们推荐在 Linux 环境下完成实验,如果不在 Linux 环境下完成实验可能会遇到部分问题,具体可参考[如何在非 Linux 环境中完成实验](ch3-04.md)。 + + + +## 配置 Git + +实验的最终成绩需要提交到远程 Gitee 仓库进行评测,你需要确保本地拥有 Git。 + +Ubuntu/Debian 发行版安装 Git: + +```bash +sudo apt install git +``` + +Arch 发行版安装 Git: + +```bash +sudo pacman -Syu git +``` + +Windows 下需要安装 Git for Windows,可从如下链接安装: + +https://gitforwindows.org/ + +在完成 Git 的安装后需要对 Git 进行基本的配置: + +```bash +git config --global user.name "你的 gitee 账户名/自定义" +git config --global user.email "你的 gitee 账户默认的邮箱地址/常用邮箱地址" +``` + + + +## 配置 C 工具链 + +导学阶段实验由5到基础的 C 语言语法题组成,为了完成实验以及在本地进行调试,需要配置 C 工具链。 + +Ubuntu/Debain 发行版配置: + +```bash +sudo apt install build-essential gdb #安装 GNU 工具链与调试工具 +``` + +Arch 发行版配置: + +```bash +sudo pacman -S base-devel gdb +``` + +Windows 需要安装 mingw 等编译工具链。 + +mingw 官网:https://www.mingw-w64.org/ + +在完成安装之后,可使用如下指令检测 gcc 是否被正确配置: + +```bash +gcc --version +``` + + + +至此,导学阶段实验所需要的基本环境配置已完成,接下来,让我们正式进入导学阶段的学习。 \ No newline at end of file diff --git a/mdbook/eulixos-camp-book-stage1-docs/.gitignore b/mdbook/eulixos-camp-book-stage1-docs/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7585238efedfc33acdd9494b0269951aaf3909ec --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/mdbook/eulixos-camp-book-stage1-docs/book.toml b/mdbook/eulixos-camp-book-stage1-docs/book.toml new file mode 100644 index 0000000000000000000000000000000000000000..758285011e0441213f7f1b570617df2e9b253349 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["XUKUNYUAN"] +language = "Chinese" +multilingual = false +src = "src" +title = "第一期傲来操作系统训练营基础阶段学习引导" diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/SUMMARY.md b/mdbook/eulixos-camp-book-stage1-docs/src/SUMMARY.md new file mode 100644 index 0000000000000000000000000000000000000000..2bcb28cd3f617ac0a7e6da022d0c0b231c609a2d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/SUMMARY.md @@ -0,0 +1,49 @@ +# Summary + +- [前言](./foreword.md) +- [介绍](./introduce.md) +- [第一章 - 第 1 课(上) - 操作系统简介](./ch1-00.md) + - [什么是操作系统](./ch1-01.md) + - [为什么要有操作系统](./ch1-02.md) + - [RISC-V:操作系统的未来发展方向](./ch1-03.md) +- [第二章 - 第 1 课(下) - 操作系统结构](./ch2-00.md) + - [结构的重要性](./ch2-01.md) + - [宏内核 & 微内核](./ch2-02.md) + - [外核 + libOS](./ch2-03.md) + - [多内核 / 复内核](./ch2-04.md) +- [第三章 - 第 2 课(全) - 中断、异常与系统调用](./ch3-00.md) + - [中断和异常](./ch3-01.md) + - [系统调用](./ch3-02.md) +- [第四章 - 第 3 课(上) - 系统初始化](./ch4-00.md) + - [计算机的启动](./ch4-01.md) + - [BIOS](./ch4-02.md) + - [EFI / UEFI](./ch4-03.md) + - [Grub、Init 与 RunLevel](./ch4-04.md) +- [第五章 - 第 3 课(下) - Risc-V 的初始化](./ch5-00.md) + - [启动流程和链接脚本](./ch5-01.md) + - [初始化](./ch5-02.md) + - [Risc-V 设备从上电到运行](./ch5-03.md) +- [第六章 - 题目分析[1 - 9]](ch6-00.md) + - [实验代码框架讲解](./ch6-01.md) + - [第一题 - MakeFile - 编写第一份 MakeFile](./ch6-02.md) + - [第二题 - MakeFile - 编写用于测试的 MakeFile](./ch6-03.md) + - [第三题 - MakeFile - 静态链接程序](./ch6-04.md) + - [第四题 - MakeFile - 构建第一个静态链接库](./ch6-05.md) + - [第五题 - MakeFile - 构建第一个动态链接库](./ch6-06.md) + - [第六题 - ld 脚本 - 指定内存区域](./ch6-07.md) + - [第七题 - ld 脚本 - 指定 text 起始地址](./ch6-08.md) + - [第八题 - ld 脚本 - 指定自定义 symbol](./ch6-09.md) + - [第九题 - ld 脚本 - 指定自定义 section](./ch6-10.md) +- [第七章 - 题目分析[10 - 20]](ch7-00.md) + - [第十题 - 数据结构(队列) - 合并两个队列](./ch7-01.md) + - [第十一题 - 数据结构(队列) - 按组反转一个队列](./ch7-02.md) + - [第十二题 - 内核模块 - 打印 hello world](./ch7-03.md) + - [第十三题 - 内核模块 - 阶乘计算](./ch7-04.md) + - [第十四题 - 内核模块 - 字符串反转](./ch7-05.md) + - [第十五题 - 内核模块 - 平均数计算](./ch7-06.md) + - [第十六题 - 内核模块 - 线性查找](./ch7-07.md) + - [第十七题 - 内核模块 - 延时打印字符串](./ch7-08.md) + - [第十八题 - Risc-V 汇编 - 条件返回](./ch7-09.md) + - [第十九题 - Risc-V 汇编 - 最大公因数求解](./ch7-10.md) + - [第二十题 - Risc-V 汇编 - 数组元素查找](./ch7-11.md) +- [附录 A - 可供参考的各种资料汇总](./ap-A.md) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ap-A.md b/mdbook/eulixos-camp-book-stage1-docs/src/ap-A.md new file mode 100644 index 0000000000000000000000000000000000000000..35cf2714855c212af90bc7afb0df7a2474c9d8d0 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ap-A.md @@ -0,0 +1,29 @@ +# 附录 A - 可供参考的各种资料汇总 + +## 本节目录 + +- [附录 A - 可供参考的各种资料汇总](#附录-a---可供参考的各种资料汇总) + - [本节目录](#本节目录) + - [Risc-V 参考文献](#risc-v-参考文献) + - [Linux 内核文档](#linux-内核文档) + - [Risc-V 开发板上的 Linux 启动流程](#risc-v-开发板上的-linux-启动流程) + - [更详细的操作系统教程](#更详细的操作系统教程) + +## Risc-V 参考文献 + +![Risc-V参考文献](./resource/RV参考文献.png) + +- 参考二:[https://github.com/isrc-cas/riscv-isa-manual-cn](https://github.com/isrc-cas/riscv-isa-manual-cn) +- 参考四:[https://github.com/riscv/riscv-plic-spec](https://github.com/riscv/riscv-plic-spec) + +## Linux 内核文档 + +[https://www.kernel.org/doc/html/v6.9-rc7/index.html](https://www.kernel.org/doc/html/v6.9-rc7/index.html) + +## Risc-V 开发板上的 Linux 启动流程 + +[https://linux-sunxi.org/Allwinner_Nezha](https://linux-sunxi.org/Allwinner_Nezha) + +## 更详细的操作系统教程 + +[https://rcore-os.cn/rCore-Tutorial-Book-v3/index.html](https://rcore-os.cn/rCore-Tutorial-Book-v3/index.html) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch1-00.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch1-00.md new file mode 100644 index 0000000000000000000000000000000000000000..dc09b9554991ffe5f6e518b26e1f00bad42d8bd3 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch1-00.md @@ -0,0 +1,19 @@ +# 第一章 - 第 1 课(上) - 操作系统简介 + +## 本章概要 + +在本章中,本文档将深入探索操作系统的基本概念,以及它在计算机领域中的核心地位。 + +首先,在第一节,本文档将从“什么是操作系统”这一基础问题出发,为初学者揭开操作系统的神秘面纱。本文档将解释操作系统的定义,分析操作系统的功能,了解操作系统在计算机里的角色。 + +本文档将会在第二节探讨“为什么要有操作系统”这一关键问题。本文档已经介绍过了操作系统的功能,在这部分,本文档会通过一些实际场景,为读者介绍操作系统在现在社会的重要作用。 + +在本章的最后一节中,本文档将介绍操作系统的未来发展方向:RISC-V。RISC-V 是一种基于精简指令集(RISC)的开源硬件指令集架构(ISA),它正逐渐成为操作系统开发的新星。本文档将介绍 RISC-V 架构的重要价值。 + +通过本章的学习,读者将对操作系统的基本概念、历史发展、核心功能以及未来趋势有一个全面的了解,为后续深入学习操作系统打下坚实的基础。 + +## 本章目录 + +- [什么是操作系统](./ch1-01.md) +- [为什么要有操作系统](./ch1-02.md) +- [RISC-V:操作系统的未来发展方向](./ch1-03.md) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch1-01.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch1-01.md new file mode 100644 index 0000000000000000000000000000000000000000..44427a3f7439551bbe265fb166291440621ea376 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch1-01.md @@ -0,0 +1,64 @@ +# 什么是操作系统 + +## 本节目录 + +- [什么是操作系统](#什么是操作系统) + - [本节目录](#本节目录) + - [硬件和应用之间的软件层](#硬件和应用之间的软件层) + - [为应用提供的部分服务](#为应用提供的部分服务) + - [应用的部分管理功能](#应用的部分管理功能) + +## 硬件和应用之间的软件层 + +操作系统是硬件和应用之间的软件层。 +一方面它提供了硬件操作接口和系统调用,来帮助应用程序来访问和利用硬件资源,还负责应用程序的加载和调度,来确保硬件资源的有效利用; +另一方面它还控制硬件设备来完成各种功能,并且对硬件进行抽象,使得应用程序的开发者,不再需要关注于硬件的差异。 + +对于操作系统的位置和作用,可以参考下图: +![操作系统是什么](./resource/操作系统是什么.png) + +通过上面的图,我们可以看到,操作系统与硬件之间的关系比较好理解,所以,我们接下来将会详细看看,操作系统到底为了应用提供了哪些服务和管理。 + +## 为应用提供的部分服务 + +在实际应用中,操作系统主要为应用提供了下面列出的一系列服务。但由于想要举出例子,必须涉及到操作系统的一些特殊名词,在编撰本文档时,默认同学们已经通过导学阶段,对于操作系统的一下名词有了初步的认识,所以建议同学们在遇到看不懂的地方时,可以复习一下涉及到的名词。 + +以下是操作系统为应用程序提供的具体服务内容: + +- **计算资源的抽象** + - CPU:通过**进程**或**线程**的调度,应用程序不再直接受限于物理 CPU 的数量。 + - 内存:提供**虚拟内存**机制,使得应用程序的内存大小、连续性和隔离性不再受物理内存的限制。 + - I/O 设备:将各种 I/O 设备统一抽象为**文件**接口,为应用程序提供统一的访问方式。 +- **线程间的同步** + - 应用程序可以实现自己的同步原语(如**lock**),但操作系统通常提供更高效、更底层的同步机制。 + - 操作系统提供的同步原语(如**futex**)与线程切换紧密结合,以优化性能。 +- **进程间的通信** + - 应用程序可以利用网络进行进程间通信(如通过**loopback 设备**)。 + - 操作系统提供了更高效的本地通信机制(如**pipe**),这些机制通常具有更丰富的语义和更优化的性能。 + +其它服务不再举例,有兴趣的同学可以自行了解。 + +## 应用的部分管理功能 + +现代应用程序普遍支持高并发,而这种对高并发的处理能力在很大程度上依赖于操作系统的支持。以下是操作系统为应用程序提供的一些关键管理功能: + +- 应用生命周期管理 + + - **加载**:操作系统负责将应用程序加载到内存中,并准备其执行环境。 + - **迁移**:在某些情况下,操作系统可以将正在运行的应用程序从一个物理或虚拟环境迁移到另一个环境,以保持服务的连续性和负载均衡。 + - **销毁**:当应用程序不再需要或发生错误时,操作系统负责清理其占用的资源并终止其执行。 + +- 计算资源分配 + + - **CPU**:操作系统通过调度机制,根据优先级、时间片轮转或其他策略来分配 CPU 资源给不同的应用程序。 + - **内存**:操作系统管理内存分配,确保每个应用程序有足够的内存空间来执行其任务,同时避免内存泄漏和溢出。 + - **I/O 设备**:操作系统提供设备的复用和分配机制,确保多个应用程序可以安全、高效地访问共享的 I/O 设备。 + +- 安全与隔离 + + - **应用程序内部**:操作系统提供访问控制机制,确保应用程序只能访问其被授权的资源,防止内部的安全漏洞和错误。 + - **应用程序之间**: + - **错误隔离**:操作系统通过进程、线程或容器等技术实现应用程序之间的错误隔离,确保一个应用程序的错误不会影响其他应用程序的正常运行。 + - **性能隔离**:操作系统通过资源配额、优先级调度等技术实现应用程序之间的性能隔离,确保每个应用程序都能获得其所需的计算资源,避免资源竞争导致的性能下降。 + +通过上述管理功能,操作系统为应用程序提供了一个稳定、高效、安全的运行环境,使其能够支持高并发并应对各种复杂的业务场景。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch1-02.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch1-02.md new file mode 100644 index 0000000000000000000000000000000000000000..96221624167091b6131902041005d072f51b2a62 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch1-02.md @@ -0,0 +1,72 @@ +# 为什么要有操作系统 + +## 本节目录 + +- [为什么要有操作系统](#为什么要有操作系统) + - [本节目录](#本节目录) + - [应用与操作系统的解耦](#应用与操作系统的解耦) + - [特权级的引入](#特权级的引入) + - [操作系统的分化](#操作系统的分化) + - [对硬件(CPU)的要求](#对硬件cpu的要求) + - [操作系统的两种演化](#操作系统的两种演化) + - [目前操作系统的关键作用](#目前操作系统的关键作用) + +## 应用与操作系统的解耦 + +在最早的时候,是完全没有“操作系统”这个概念的。这时的应用程序,将所有的功能都汇聚到了一起,开发人员身兼数职,从磁盘读写到程序逻辑,一切都需亲手操持。 + +然而,随着技术的进步和需求的增长,应用程序的功能日益丰富,代码量也如滚雪球般不断膨胀。开发人员发现,维护这样一个庞大的、功能混杂的程序变得越来越困难。为了解决这个问题,人们开始尝试将应用程序的底层存储和逻辑工作分开,将它们各自封装成独立的模块。 + +这种“分化”带来了革命性的变化。开发人员可以将一些常用的底层存储和逻辑工作(如磁盘读写、内存管理等)封装成特定的模块,这些模块通过统一的接口与其他模块进行交互。这样,开发人员就可以更加专注于应用程序的核心逻辑,而无需过多关注底层细节。而这些被封装好的模块,提供了非常丰富的功能,使得我们在开发时,只需要调用这些库的接口,就能获得所需的功能,而无需从头开始编写代码。 + +看到这里,你是不是想到了什么?没错,这时的这些模块,其实就类似我们现在常用的“库”,只需要调用其接口,就能得到想要的结果。但是,问题来了,既然这些“库”就已经能够较优的构成应用程序了,那么为什么还需要操作系统呢? + +## 特权级的引入 + +想要回答上面的问题,我们必须了解一段计算机的发展史。最早的计算机,使用纸带进行参数配置、代码录入等工作,这极为不便。后续,人们为了解决计算机计算速度远高于人类录入速度的矛盾,开发了批处理系统。但批处理系统一时间只能处理一个任务,无法同时处理多个任务,导致一旦某任务过程中,发出了 I/O 请求,CPU 就必须等待该请求完成,这段时间就导致了 CPU 资源的浪费。因此,人们再次升级计算机,开发了多道程序系统。 + +多道程序系统,通过时分复用计算资源的方式,在一台计算机上同时运行多个应用程序。但同时运行,就带来了一个新的问题:如何保证不同应用间的隔离?假设有两个程序,它们都能随意使用硬件设备,第一个应用希望关机,第二个应用则希望格式化硬盘,那么最终的结果就很可能是,两个应用彼此干扰,硬盘只格式化了一半。 + +为了解决上面的问题,人们只好引入了“特权级”的概念,至少具有两个特权级,即,位于低权限时,不能对计算机的全局状态进行任何改变;而高权限时,则集中运行能够操控整个计算机的代码。我们称这部分代码所执行后,能改变全局状态的操作,为**特权操作**,在高权限状态,慢慢分化出了**操作系统**(的内核)。 + +有兴趣了解更多操作系统历史的同学们,可以通过[此链接](https://www.cnblogs.com/Dominic-Ji/articles/10929381.html),查看更多内容。 + +## 操作系统的分化 + +操作系统的分化阶段,大致如下图所示: +![操作系统的分化](./resource/操作系统的分化.png) + +## 对硬件(CPU)的要求 + +在引入权限级后,对硬件也提出了更多的要求。 + +对于 CPU 来说,具体的改变如下所示: + +- 将 CPU 对软件提供的接口称为 ISA + - ISA: Instruction Set Architecture,中文译作**指令集架构**。 + - ISA 内,包含软件可见、可操作的接口,这些接口内包含各种指令和寄存器等。 +- CPU 也相应分化出两个模式:非特权模式和特权模式 + - 非特权模式 ISA:应用可使用的指令和寄存器 + - 特权模式 ISA:只有操作系统才可使用的指令和寄存器 + +通常,我们将特权模式称为**内核态**,将非特权模式称为**用户态**。将从用户态切换到内核态的指令,称为**特权指令**。 + +## 操作系统的两种演化 + +- 外部 -> 接口的演化:更好地应对新场景 + - POSIX 接口:定义了一组系统调用的接口,用来为应用提供兼容性 + - Linux:系统调用不断有新的加入、旧的退出 + - 鸿蒙:分布式软总线等 +- 内部 -> 架构的演化: + - 更好地应对复杂性:宏内核架构、微内核架构、外核架构、多内核架构等 + - 为了更好的扩展性、容错性、安全性、兼容性、灵活性、性能等 + +对于上面的演化内容,大家可以依照自己兴趣和时间安排,进行了解。 + +## 目前操作系统的关键作用 + +以下部分,引用了老师的 PPT: + +![](./resource/OS作用1.png) + +![](./resource/OS作用2.png) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch1-03.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch1-03.md new file mode 100644 index 0000000000000000000000000000000000000000..9d690ff12178749be353f498c71a4adbfb0d3b23 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch1-03.md @@ -0,0 +1,26 @@ +# RISC-V:操作系统的未来发展方向 + +## 本节目录 + +- [RISC-V:操作系统的未来发展方向](#risc-v操作系统的未来发展方向) + - [本节目录](#本节目录) + - [什么是“指令集”](#什么是指令集) + - [Risc-V 的重要价值](#risc-v-的重要价值) + +## 什么是“指令集” + +指令集架构(Instruction Set Architecture,ISA),简称指令集,是计算机系统中硬件与软件之间分界线和交互规范标准,,也是软硬件生态的起始原点。其位于计算机内的地位如下图所示: + +![](./resource/指令集.png) + +对于当前的指令集领域,尚未形成一套国际通用的标准。与操作系统采用 Posix 作为标准、数据库采用 SQL 作为标准的情况不同,指令集尚未实现标准化。这意味着,指令集的标准化工作仍在进行之中。其中,RISC-V 作为一种新兴的指令集架构,正逐渐崭露头角。 + +RISC-V 指令集=基础指令集+标准扩展指令集+用户自定义扩展指令集,而软件则是目前 Risc-V 生态中最关注的部分。现在已形成的 RISC-V 软件生态栈如下图所示: + +![](./resource/RV生态栈.png) + +## Risc-V 的重要价值 + +对于我国而言,我们的基础软件领域暂时存在一定的短板,而 RISC-V 带来的“造轮子”的机会,恰好可以弥补这一空白。其可能的应用如下图所示: + +![](./resource/RV应用.png) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch2-00.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-00.md new file mode 100644 index 0000000000000000000000000000000000000000..14da917682a27a287cd61f8c81372cb85c8b1092 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-00.md @@ -0,0 +1,20 @@ +# 第二章 - 第 1 课(下) - 操作系统结构 + +## 本章概要 + +在本章中,本文档将进一步深入探索操作系统的结构部分。操作系统的结构决定了其如何管理计算机硬件和软件资源,如何为用户提供高效、安全和稳定的服务。本章将详细阐述操作系统结构的重要性,分析不同的操作系统架构,并探讨它们对系统性能和功能的影响。 + +首先,本文档将在第一节讨论操作系统结构的重要性。随着计算机系统的不断发展和复杂化,操作系统的结构也变得越来越重要。一个合理的操作系统结构可以有效地降低系统的复杂性,提高系统的可维护性和可扩展性。本文档将通过一些实际例子(如 Workplace),说明复杂性对操作系统设计和实现的影响,并探讨如何通过分离策略与机制来降低复杂性。 + +接下来,本文档将按照操作系统架构的历史演进,进行剩余节的讲解。第二节将会较为详细的介绍现代的宏内核、微内核架构。第三节将会主要介绍外核+库 OS 架构,第四节将会介绍多内核/复内核架构。 + +本文档将在各自的节,分析不同架构的特点和优缺点,并探讨它们在不同应用场景下的适用性。通过这些讲解,读者将会进一步理解在第一节中给出的,不同架构的示意图和实例,和各种架构之间的差异和联系。 + +通过对本章的学习,读者将对操作系统的结构有更深入的理解,掌握不同架构的特点和优缺点,为后续项目阶段的选择提供基本的介绍。 + +## 本章目录 + +- [结构的重要性](./ch2-01.md) +- [宏内核 & 微内核](./ch2-02.md) +- [外核 + libOS](./ch2-03.md) +- [多内核 / 复内核](./ch2-04.md) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch2-01.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-01.md new file mode 100644 index 0000000000000000000000000000000000000000..16f0a6609186722c11799179c7beede61a72522b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-01.md @@ -0,0 +1,29 @@ +# 结构的重要性 + +## 本节目录 + +- [结构的重要性](#结构的重要性) + - [本节目录](#本节目录) + - [复杂性](#复杂性) + - [想办法降低其复杂性](#想办法降低其复杂性) + - [历史上的架构与其演进](#历史上的架构与其演进) + +## 复杂性 + +1991-1995 年,IBM 公司投入 20 亿美元打造 Workplace 操作系统,然而,由于目标过于宏伟,系统过于复杂等种种原因,导致该项目最终失败,20 亿美元的投资打了水漂。同时,此项目的失败,也间接导致 IBM 全力投入扶植 Linux 操作系统。 + +上面的这个例子,应该可以让同学们看到,整个操作系统的复杂性有多么可怕。而在深入分析其“复杂性”来源时,我们可以发现:在操作系统内部,不同目标之间往往存在冲突,而在不同需求之间又需要进行权衡。 + +对于用户来说,操作系统应该方便使用、容易学习、功能齐全、安全流畅;但对于整个系统目标来说,操作系统应该容易设计与实现,容易维护,具有高灵活性、高可靠性和高效性。这就导致操作系统的复杂性居高不下。 + +## 想办法降低其复杂性 + +而为了降低其复杂性,计算机科学家们提出:要将策略和机制进行分离,操作系统只能通过调整策略而不是机制来适配解决实际问题。 + +以调度算法为例,RR、FIFO 等都是策略,但调度队列、对线程的表示等,都是机制。 + +## 历史上的架构与其演进 + +接下来,我们会给大家展示架构的发展历史和各种架构的对比。 +![](./resource/架构演进.png) +![](./resource/架构对比.png) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch2-02.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-02.md new file mode 100644 index 0000000000000000000000000000000000000000..bfd9c62e660209ff6fd2a9e6c65b39577336099b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-02.md @@ -0,0 +1,90 @@ +# 宏内核 & 微内核 + +## 本节目录 + +- [宏内核 \& 微内核](#宏内核--微内核) + - [本节目录](#本节目录) + - [什么是宏内核](#什么是宏内核) + - [宏内核的优点](#宏内核的优点) + - [宏内核的缺点](#宏内核的缺点) + - [什么是微内核](#什么是微内核) + - [微内核的优点](#微内核的优点) + - [微内核的缺点](#微内核的缺点) + - [宏内核 vs 微内核](#宏内核-vs-微内核) + - [混合内核架构](#混合内核架构) + +## 什么是宏内核 + +在宏内核架构下,整个系统被分为内核与应用两层,内核运行在特权级,集中控制所有计算资源,应用运行在非特权级,受内核管理,使用内核提供的各种服务。更简明的解释如下图所示: + +![](./resource/宏内核.png) + +## 宏内核的优点 + +- **丰富的沉淀和积累**:宏内核经过多年的发展,积累了丰富的功能和优化。 +- **巨大的统一的社区和生态**:拥有庞大的用户群体和开发者社区,生态完善。 +- **针对不同场景的优化**:经过 30 年的优化,宏内核能够针对各种场景提供高效的支持。 + +## 宏内核的缺点 + +- **安全性与可靠性问题**:模块之间缺乏强隔离机制,可能导致安全隐患和可靠性问题。 +- **实时性支持**:由于系统复杂,难以进行最坏情况时延分析,不利于实时应用。 +- **系统过于庞大**:如 Linux 等宏内核系统代码行数庞大,可能阻碍了创新和灵活性。 +- **扩展性** + - **向上扩展**:难以剪裁和扩展宏内核系统以支持从 KB 级别到 TB 级别的不同场景。 + - **向下扩展**:同样存在扩展性挑战,难以满足极小化系统的需求。 +- **硬件异构性**:难以长期支持定制化的方式解决特定硬件问题。 +- **功能安全**:例如 Linux,很难通过严格的功能安全认证(如汽车安全完整性认证 ASIL-D)。 +- **信息安全**:单点错误可能导致整个系统出错,存在大量已知的安全问题(CVE)。 +- **确定性时延**:虽然 Linux 花费了多年时间合并实时补丁,但目前仍不确定是否能支持确定性时延要求。 + +## 什么是微内核 + +在微内核架构下,采用“最小化内核功能”的方式,将操作系统功能移到用户态,称为"服务"(Server),而在用户模块之间,使用消息传递机制通信。下图所示为微内核架构: + +![](./resource/微内核.png) + +## 微内核的优点 + +- **易于扩展**:直接添加一个用户进程即可为操作系统增加服务。 +- **易于移植**:大部分模块与底层硬件无关。 +- **更加可靠**:在内核模式运行的代码量大大减少。 +- **更加安全**:即使存在漏洞,服务与服务之间存在进程粒度隔离。 +- **更加健壮**:单个模块出现问题不会影响到系统整体。 + +## 微内核的缺点 + +- **性能较差**:内核中的模块交互由函数调用变成了进程间通信。 +- **生态欠缺**:尚未形成像 Linux 一样具有广泛开发者的社区。 +- **重用问题**:重用宏内核操作系统提供兼容性,带来新问题。 + +下面的图可以直观展示出微内核的性能缺陷: +![](./resource/微内核性能.png) + +有兴趣研究微内核的同学,可以自行查找相关资料学习。 + +## 宏内核 vs 微内核 + +下面,我们对比一下宏内核和微内核。 + +它们的一个非常大的区别,在于共享数据状态。宏内核中,大量的共享状态在内核态;而微内核里,部分数据在内核态,部分在用户态。 + +![](./resource/宏内核微内核对比.png) + +其次,在性能上,我们在上一部分已经展示出,对于某些情况下,微内核的性能会比宏内核差很多。 + +对于实际的操作流程,以“创建文件”为例: +![](./resource/宏内核微内核文件.png) + +## 混合内核架构 + +既然宏内核和微内核各有优缺点,那么有没有一种架构,能够结合两者的优点,同时避免两者的缺点呢? + +答案是有的,这就是混合内核架构。 + +![](./resource/混合内核.png) + +混合内核架构,将需要性能的模块重新放回内核态,所以可以同时具备宏内核和微内核的优点,同时避免两者的缺点。 + +- Windows NT:微内核+内核态的系统服务+系统框架 +- macOS/i0S:Mach 微内核+BSD4.3+系统框架 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch2-03.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-03.md new file mode 100644 index 0000000000000000000000000000000000000000..09981562bb5133589392e0afb2a044a16f492202 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-03.md @@ -0,0 +1,74 @@ +# 外核 + libOS + +## 本节目录 + +- [外核 + libOS](#外核--libos) + - [本节目录](#本节目录) + - [什么是外核与 libOS](#什么是外核与-libos) + - [单内核](#单内核) + - [外核的优点](#外核的优点) + - [外核的缺点](#外核的缺点) + - [外核 VS 虚拟机](#外核-vs-虚拟机) + +## 什么是外核与 libOS + +外核: + +- Exokernel 不提供硬件抽象 + - “只要内核提供抽象,就不能实现性能最大化" + - 只有应用才知道最适合的抽象(end-to-end 原则) +- Exokernel 不管理资源,只管理应用 + - 负责将计算资源与应用的绑定,以及资源的回收 + - 保证多个应用之间的隔离 + +库 OS(Libos): + +- 策略与机制分离:将对硬件的抽象以库的形式提供 +- 高度定制化:不同应用可使用不同的 LibOS,或完全自定义 +- 更高性能:Lib0s 与应用其他代码之间通过函数调用直接交互 + +对于外核,图示如下: + +![](./resource/外核.png) + +外核拥有与宏内核和微内核不同的功能,因此也提出了新的技术。 + +为了追踪计算资源的拥有权,采用了安全绑定(Secure binding);为了保证资源的保护,采用了显式回收(Visible revocation);同时,为了能够回收对资源的访问权,额外增加了中止协议(Abort protocol)。建议同学们对这部分内容进行自学。 + +## 单内核 + +LibOS 可以进一步拓展为单内核,即一个内核管理多个应用。 + +单内核可以看做虚拟化环境下的 LibOS,每个虚拟机只使用内核态,在内核态中只运行一个应用和 LibOS,同时,通过虚拟化层实现不同实例间的隔离。 + +单内核非常适合容器应用场景,因为每个容器就是一个虚拟机,而且每个容器运行定制的 LibOS,可以有效提高性能。 + +对于单内核的开源项目,我们搜集到以下内容,感兴趣的同学可以进行自学: + +- Rumprun:POSIX 接口,BSD 兼容的运行时环境,运行在 Xen 虚拟化平台之上 +- Drawbridge:来自微软,兼容 Win32 接口的运行时环境 +- OSv:与 Linux 兼容的应用环境,单地址空间 + +同时,既然涉及到“LibOS”,就可以回到我们上一章提到的问题:操作系统能不能作为一个库?对于这个问题,有人提出了“Linux as a LibOS”,将 Linux 作为 LibOS 或 Unikernel 运行,但引入了很多新问题: + +- Linux 是否适合作为 LibOS/unikernel? +- fork()如何处理? + +此技术尚在探索阶段,感兴趣的同学可以进行自学。一个类似的例子是:LKL-Linux kernel library(https://github.com/lkl) + +## 外核的优点 + +- OS 无抽象,能在理论上提供最优性能 +- 应用对计算有更精确的实时等控制 +- LibOS 在用户态更易调试,调试周期更短 + +## 外核的缺点 + +- 对计算资源的利用效率主要由应用决定 +- 定制化过多,导致维护难度增加 + +## 外核 VS 虚拟机 + +以下是对于外核与虚拟机的对比,感兴趣的同学可以搜集相关资料自学。 + +![](./resource/外核虚拟机.png) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch2-04.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-04.md new file mode 100644 index 0000000000000000000000000000000000000000..8182ce2c7e72ee7ad4ab12385a30edb24f5a538c --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch2-04.md @@ -0,0 +1,35 @@ +# 多内核 / 复内核 + +## 本节目录 + +- [多内核 / 复内核](#多内核--复内核) + - [本节目录](#本节目录) + - [使用多内核架构的背景](#使用多内核架构的背景) + - [使用多内核架构的原因](#使用多内核架构的原因) + - [使用多内核架构的实例](#使用多内核架构的实例) + +## 使用多内核架构的背景 + +- 现在的 OS 内部维护很多共享状态 + - Cache 一致性的保证越来越难 + - 可扩展性非常差,核数增多,性能不升反降 +- GPU 等设备越来越多 + - 设备本身越来越智能--设备有自己的 CPU + - 通过 PCle 连接,主 CPU 与设备 CPU 之间通信非常慢 + - 通过系统总线连接,异构 SoC(Syslem on Chip) + +对于 System on Chip,来说,其图解如下所示: +![System on Chip](./resource/SoC.png) + +以上的一系列背景,就使得使用多内核架构逐渐成为更优的解决方案。 + +## 使用多内核架构的原因 + +- 可以适配众核硬件平台 +- 现有的 OS 架构在多核情况下,存在内核资源争用问题 +- 现有的 OS 架构难以应对,应用混合部署下的性能隔离问题 +- 现有的 OS 架构在异构硬件环境下资源利用率低 + +## 使用多内核架构的实例 + +![](./resource/多核例子.png) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch3-00.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch3-00.md new file mode 100644 index 0000000000000000000000000000000000000000..00c493d4d4a5a6caa9ad5bd30ea975fbdf0f65fe --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch3-00.md @@ -0,0 +1,16 @@ +# 第三章 - 第 2 课(全) - 中断、异常与系统调用 + +## 本章概要 + +本章主要讨论了操作系统中三个核心概念:中断、异常和系统调用。这些概念在操作系统的设计和实现中起着至关重要的作用,它们共同构成了操作系统内核与用户空间程序之间交互的基础。 + +第一节内,我们讲了中断和异常。中断是计算机系统中一种重要的同步机制,用于通知 CPU 有外部事件或内部条件发生,需要 CPU 立即处理。当中断发生时,CPU 会暂停当前正在执行的程序,保存应用上下文,并跳转到相应的中断服务程序(ISR)去处理中断事件。处理完毕后,CPU 会恢复之前保存的现场信息,并继续执行被中断的程序。中断使得 CPU 能够高效地处理多任务,实现并发执行。 + +而异常是程序执行过程中发生的错误或异常条件,如除零错误、非法指令等。它与中断不同,异常是由 CPU 内部检测到的,并且通常是由程序错误导致的。CPU 会跳转到相应的异常处理程序去处理异常事件。异常处理完成后,CPU 会根据异常的性质选择恢复程序执行或终止程序。我们在第一节的末尾,将两者进行了对比。 + +之后的第二节,我们涉及到了系统调用。它是用户空间程序请求操作系统内核服务的一种机制。系统调用是用户空间与内核空间之间交互的桥梁,它使得用户空间程序能够访问底层硬件资源、执行特权操作以及实现进程管理、文件访问等功能。系统调用的实现依赖于中断机制,当用户空间程序发出系统调用请求时,CPU 会陷入内核态并执行相应的系统调用处理程序。我们在节内,进一步讨论了提高其性能的可能性。 + +## 本章目录 + +- [中断和异常](./ch3-01.md) +- [系统调用](./ch3-02.md) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch3-01.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch3-01.md new file mode 100644 index 0000000000000000000000000000000000000000..a82f511d6a9e645b45d463468fbc9819e7af921a --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch3-01.md @@ -0,0 +1,101 @@ +# 中断和异常 + +## 本节目录 + +- [中断和异常](#中断和异常) + - [本节目录](#本节目录) + - [中断与轮询](#中断与轮询) + - [中断与异常的定义](#中断与异常的定义) + - [处理流程](#处理流程) + - [trap 定义](#trap-定义) + - [在处理 trap 时使用到的寄存器](#在处理-trap-时使用到的寄存器) + - [异常处理函数](#异常处理函数) + - [中断和异常的对比](#中断和异常的对比) + +## 中断与轮询 + +首先,我们假想一下,我们正在设计一个操作系统,现在,我们需要为它提供键盘支持。那么,我们如何实现这个功能? + +一种方法是,我们使用轮询的方式,即,我们定期检查键盘的状态,如果键盘有输入,我们就处理它。但是,这种方式有一个问题,就是,如果键盘没有输入,那么,我们就会白白浪费 CPU 资源。那么,有没有更好的方法呢?有的,这就是中断。 + +中断是一种异步的事件处理机制。当某个事件发生时,操作系统会自动调用相应的处理函数来处理该事件。这样,我们就不用一直轮询键盘的状态了。 + +从上面这个例子,可以举一反三到更多的场景,如下图所示: + +![](./resource/轮询.png) + +## 中断与异常的定义 + +中断和异常的通用概念如下: + +- 中断(Interrupt) —— 异步异常 + - 外部硬件设备所产生的信号 + - 异步:产生原因和当前执行指令无关,如程序被磁盘读打断 +- 异常(Exception) —— 同步异常 + - 软件的程序执行而产生的事件 + - 同步:产生和当前执行或试图执行的指令相关 + +## 处理流程 + +CPU 的执行逻辑很简单: + +1. 以 PC 的值为地址从内存中获取一条指令并执行 +2. PC+=4,goto 1 + +而在上面的执行过程中,可能发生以下两种情况: + +1. 指令执行出现错误,比如除零或缺页(同步异常) +2. 外部设备触发中断(异步异常) + +上面的这两种情况,在 RISC-V 平台称为「异常」和「中断」。在检测到上面两种情况时,OS 会进入“trap”。 + +![](./resource/trap流程.png) + +具体的 trap 处理各阶段的工作,需要同学们根据老师上课教学内容,进行学习。 + +## trap 定义 + +- 控制流(Control Flow): + +1. Branch +2. Jump + +- 异常控制流(ExceptionalControlFlow,ECF): + +1. exception +2. interrupt + +在 Risc-V 下,ECF 统称为 trap。 + +对于 OS 来说,想要实现 trap,总共分为下面两步: + +1. 实现对异常向量表的设置 + +- CPU 上电后立即执行,这是系统初始化的主要工作之一 +- 在开启中断和启动第一个应用之前执行完毕 + +2. 实现对不同异常(中断)的处理函数 + +- 处理应用程序出错的情况:如除零、缺页 +- 一类特殊的同步异常:系统调用(我们会在下一节详细介绍),由应用程序来主动触发 +- 处理来自外部设备的中断:如收取网络包、获取键盘输入等 + +## 在处理 trap 时使用到的寄存器 + +![](./resource/trap寄存器.png) + +相关寄存器的参数内容,请同学们参考 RISC-V 手册等参考资料进行自学。 + +## 异常处理函数 + +异常处理函数在核心态,可以访问所有资源。处理器将异常类型存储在指定寄存器中(例如,`cause`),表明发生的是哪一种异常。异常处理函数根据异常类型执行不同逻辑。 + +当异常处理函数完成异常处理后,可以通过以下操作之一转移控制权: + +1. 回到发生异常时正在执行的指令 +2. 回到发生异常时的下一条指令 +3. 结束当前进程 + +## 中断和异常的对比 + +![](./resource/中断异常.png) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch3-02.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch3-02.md new file mode 100644 index 0000000000000000000000000000000000000000..8a35c50e793184c03331ef6b3ec2e8f2bff1d22d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch3-02.md @@ -0,0 +1,62 @@ +# 系统调用 + +## 本节目录 + +- [系统调用](#系统调用) + - [本节目录](#本节目录) + - [特权级](#特权级) + - [跟踪系统调用](#跟踪系统调用) + - [参数与返回值](#参数与返回值) + - [提高性能](#提高性能) + +## 特权级 + +系统调用需要特权级机制的支持。在 Risc-V 中,特权级如下表所示: +![](./resource/特权级.png) + +## 跟踪系统调用 + +假设有代码: + +```c +int main() { + write(1,"Hello world!\n",13); + return 0; +} +``` + +我们可以通过下面命令跟踪系统调用: + +```bash +$ strace -o hello.out ./hello +``` + +## 参数与返回值 + +系统调用的参数和返回值都是通过寄存器传递的。 + +- 最多允许 8 个参数:a0-a7 +- a7 用于存放系统调用编号 +- 调用者保存的寄存器必须在用户态保存 +- 返回值存放于 a0 寄存器中 + +但是,如果出现,系统调用的传参过多,无法直接使用寄存器放下,该怎么办呢? + +朴素的解决方案是,通过将指针写入寄存器,然后由内核去访问指针指向的空间,但这可能会导致一系列的安全问题。因此,就需要验证其是否合法。对于此部分内容,需要同学们跟着老师学习,并且掌握 Linux 解决此问题的方案。 + +## 提高性能 + +在对系统调用进行性能分析时,能够发现,主要的开销在于系统调用切换。因此,为了进一步提高性能,系统调用的时延不可忽略,尤其是调用非常频繁的那些,如 `gettimeofday()`。它的大部分时延都是由于 U->S/M 的模式切换带来的,如果没有模式切换,那么就不需要保存回复状态,就能够大幅降低系统调用的时延。 + +对于其,可以采用如下方式优化: +将 gettimeofday 的代码加载到一块与应用共享的内存页,这个页称为:vDSO(Virtual Dynamic Shared Object),然后,将 Time 的值同样映射到用户态空间(只读),但只有在内核态才能更新这个值。 + +不仅仅是 gettimeofday(),其它大部分的系统调用的大量时延,都是用来做状态的切换(保存和恢复状态、权限的切换)。还有一部分的时延,是因为(Cache pollution)。那么,是否有可能在不切换状态的情况下实现系统调用? + +一个可行的解决方案是,引入了一个新的 syscall 机制: + +- 引入 system call page,由 user & kernel 共享 +- User threads 可以将系统调用的请求 push 到 system call page +- `kernel threads`会从`system call page` poll `system call 请求` + +性能提高的剩余内容,需要同学们跟随老师讲解学习。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch4-00.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-00.md new file mode 100644 index 0000000000000000000000000000000000000000..ce90dc4301223559f3895ad6378be0916d26ee53 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-00.md @@ -0,0 +1,18 @@ +# 第四章 - 第 3 课(上) - 系统初始化 + +## 本章概述 + +本章将深入探讨计算机从完全关闭状态到完全加载操作系统和用户界面的完整初始化过程。我们将首先在第一节简要了解计算机的启动流程,包括加电自检(POST)和内核启动等关键步骤。 + +接着,我们将在第二章简要介绍 BIOS(基本输入输出系统)的作用、功能、物理位置以及基本结构,因为它是计算机启动过程中不可或缺的一部分。 + +随后,我们将主要介绍 EFI(可扩展固件接口)和 UEFI(统一可扩展固件接口)的介绍,这是现代计算机启动过程中逐渐取代 BIOS 的新技术。我们将探讨 EFI/UEFI 的简介、它们与 BIOS 的主要区别、其结构以及当前在业界的应用进展。 + +最后一节内容,我们将会基于 Linux 系统启动的过程,介绍剩下的阶段,包括 Grub、Init、RunLevel 等。同时,会简要介绍目前这些阶段的进展,以及未来可能的发展方向。 + +## 本章目录 + +- [计算机的启动](./ch4-01.md) +- [BIOS](./ch4-02.md) +- [EFI / UEFI](./ch4-03.md) +- [Grub、Init 与 RunLevel](./ch4-04.md) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch4-01.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-01.md new file mode 100644 index 0000000000000000000000000000000000000000..913e83b8b6981919012a127c5d1dd31da1584829 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-01.md @@ -0,0 +1,43 @@ +# 计算机的启动 + +## 本节目录 + +- [计算机的启动](#计算机的启动) + - [本节目录](#本节目录) + - [流程概要](#流程概要) + - [硬件 - 加电自检](#硬件---加电自检) + - [软件 - 内核启动](#软件---内核启动) + +## 流程概要 + +![](./resource/启动流程.png) + +在计算机启动的过程中,BIOS 会首先进行加电自检,然后加载操作系统内核。计算机先进行硬件部分的加载,然后进行对于软件部分(即 OS)的加载,然后跳转到内核中执行。 + +## 硬件 - 加电自检 + +在这一部分,我们会介绍加电自检。它的基本过程如下: + +1. 初始化 BIOS +2. 检查 CPU 寄存器检查 BIOS 代码的完整性 +3. 检查 DMA、timer、interrupt controller +4. 检查系统内存 +5. 检查系统总线和外部设备 +6. 跳转到下一级 BIOS(如 VGA-BIOS)执行并返回 +7. 识别可以启动的设备(CD-ROM?USB?HDD?) + +但这一部分就涉及到了一个问题:由谁来执行这些检查?这一问题,我们将在下一节回答。 + +## 软件 - 内核启动 + +在 BIOS 执行完加电自检之后,它会将控制权交给操作系统内核。此时,操作系统主要会进行两个任务: + +- 配置页表并开启虚拟内存机制,允许使用虚拟地址 +- 配置异常向量表并打开中断,允许“双循环” + +同学们通过听课学习,应该要掌握: + +- 页表究竟该如何具体配置? +- 异常向量表如何配置? +- 开启地址翻译的前一行指令使用物理地址,开启后立即使用虚拟地址,前后如何衔接? +- 打开后,异常处理的指令流如何流动? diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch4-02.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-02.md new file mode 100644 index 0000000000000000000000000000000000000000..8966f07de16451933d07e922022ad4a4ad0330b4 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-02.md @@ -0,0 +1,54 @@ +# BIOS + +## 本节目录 + +- [BIOS](#bios) + - [本节目录](#本节目录) + - [BIOS 简介](#bios-简介) + - [BIOS 的作用](#bios-的作用) + - [BIOS 的功能](#bios-的功能) + - [BIOS 的结构](#bios-的结构) + - [BIOS 的物理结构](#bios-的物理结构) + - [BIOS 代码的结构](#bios-代码的结构) + +## BIOS 简介 + +BIOS:Basic Input/Output System,基本输入输出系统 + +BIOS 主要兴盛于 20 世纪 70 年代到 90 年代,它是一个固件(Firmware)程序,用于管理计算机硬件设备,并提供一些基本的输入输出功能。 + +## BIOS 的作用 + +- 在计算机开机时对系统各组件进行检查 +- 加载引导程序或操作系统 +- 向操作系统提供系统配置信息 +- 向操作系统提供硬件访问接口,向操作系统隐感硬件的变化 +- 现代操作系统会忽略 BIOS 提供的抽象层并直接访问硬件 + +## BIOS 的功能 + +BIOS 中主要存放以下程序段: + +1. 自诊断程序: + 通过读取 CMOSRAM 中的内容,识别硬件配置,并对其进行自检和初始化。 +2. CMOS 设置程序: + 引导过程中,用特殊热键启动,进行设置后,存入 CMOS RAM 中。 +3. 系统自检装载程序: + 在自检成功后,将磁盘 0 磁道 0 扇区上的引导程序装入内存,让其运行以装入系统。 +4. 主要 IO 设备的驱动程序和中断服务 + +## BIOS 的结构 + +### BIOS 的物理结构 + +BIOS 的物理结构主要体现在其存储方式上。BIOS 代码通常存储在一个只读存储器(Read-Only Memory,ROM)芯片中,这种芯片能够在计算机断电后仍然保留数据,因此,计算机每次启动时都能执行相同的 BIOS 代码。这种存储方式保证了 BIOS 的稳定性和可靠性。 + +### BIOS 代码的结构 + +BIOS 代码的结构主要指的是 BIOS 程序的组织方式。BIOS 程序通常包括以下几个部分: + +1. 启动自检程序(POST):计算机启动时首先执行的是自检程序,它负责检测计算机硬件设备是否正常,包括内存检测、显卡检测、硬盘检测等。 +2. 设置程序:如果自检通过,BIOS 会执行设置程序,允许用户配置计算机的一些基本参数,如启动顺序、内存速度等。 +3. 启动加载程序:设置完成后,BIOS 会根据用户设定的启动顺序,从硬盘、光驱或其他存储设备中加载引导记录(Boot Record),并将控制权转交给引导记录中的操作系统。 +4. 中断服务程序:BIOS 还提供了一系列的中断服务程序(Interrupt Service Routines,ISRs),这些程序可以在操作系统运行期间被调用,用于处理硬件设备的输入输出请求。 +5. CMOS 配置:BIOS 使用 CMOS(互补金属氧化物半导体)电路来存储计算机的配置信息。CMOS 是一种低功耗的静态随机存储器(SRAM),它由一个小型电池供电,即使在计算机关闭的情况下也能保持数据不丢失。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch4-03.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-03.md new file mode 100644 index 0000000000000000000000000000000000000000..357c651c03f584c3eb8e1dc00577a27463a8fde9 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-03.md @@ -0,0 +1,58 @@ +# EFI / UEFI + +## 本节目录 + +- [EFI / UEFI](#efi--uefi) + - [本节目录](#本节目录) + - [EFI / UEFI 简介](#efi--uefi-简介) + - [EFI / UEFI 与 BIOS 的区别](#efi--uefi-与-bios-的区别) + - [EFI / UEFI 的结构](#efi--uefi-的结构) + - [EFI / UEFI 当前进展](#efi--uefi-当前进展) + +## EFI / UEFI 简介 + +在上一节提到,BIOS 主要流行于 1970s 到 1990s,因为计算机硬件设备种类繁多,而 BIOS 只能识别硬件设备,无法识别软件设备,而不再能满足计算机硬件设备日益复杂的需求。因此,1990s 后,出现了 EFI / UEFI。 + +Intel 提出了,使用 EFI(Extensible Firmware Interface)取代 BlOS interface 。 + +2005 年,Intel 再次提出用 UEFI(Unified Extensible Firmware Interface)取代 EFI。 + +下图展示了 EFI 的架构位置: + +![](./resource/EFI.png) + +## EFI / UEFI 与 BIOS 的区别 + +首先,对于编程语言来说,BIOS 主要使用汇编语言,而 EFI 主要使用 C 语言。EFI 是用模块化的思想,借助动态链接的形式构建的系统,较 BIOS 而言更易于实现,容错和纠错特性更强,缩短了研发时间。 + +其次,对于 BIOS 具有的三大任务: + +1. 初始化硬件 +2. 提供硬件的软件抽象 +3. 启动操作系统 + +UEFI 具有三大优势: + +1. 标准接口 +2. 开放统一 +3. 开源 + +同时,EFI 运行于 32 位或 64 位模式,突破了传统 16 位代码的寻址能力。而 BIOS 的硬件服务程序都以 16 位代码的形式存在,这就给运行于增强模式的操作系统访问其服务造成了困难。 + +而且,EFI 系统下的驱动并不是由可以直接运行在 CPU 上的代码组成的,而是用 EFIByte Code 编写而成的。这是一组专用于 EFI 驱动的虚拟机器语言,必须在 EFI 驱动运行环境下被解释运行。这就保证了充分的向下兼容性。 + +因此,EFI 和 UEFI 才能够被广泛接受。 + +## EFI / UEFI 的结构 + +更加具体的 EFI 的架构位置如下图所示: +![](./resource/EFI结构.png) + +## EFI / UEFI 当前进展 + +当前,对于 UEFI 应用的一个典型例子是 LinuxBoot: +![](./resource/EFI发展.png) + +感兴趣的同学们可以参考: + +- [LinuxBoot](https://www.kernel.org/doc/html/v6.9-rc7/arch/loongarch/booting.html) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch4-04.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-04.md new file mode 100644 index 0000000000000000000000000000000000000000..e0ddfa23c05a73328538c6352308a441480820de --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch4-04.md @@ -0,0 +1,137 @@ +# Grub、Init 与 RunLevel + +## 本章目录 + +- Grub 简介 +- Init 简介 +- RunLevel 简介 +- 目前进展(init->systemd) + +## Linux 启动流程 + +在 Linux 系统下,启动流程如下: + +![](./resource/Linux启动.png) + +## 引导程序 - bootloader + +bootloader,即引导程序,是开机时,引导操作系统启动的程序。 + +BIOS 在完成硬件检测和资源分配后,将硬盘 MBR 中的 bootloader 读到系统的 RAM 中,然后将控制权交给 bootloader。 + +bootoader 的主要任务就是将操作系统内核从硬盘加载到 RAM 中,然后跳转到内核的入口点去执行,即启动操作系统。 + +常见的 bootloader 有:Grub,isolinux,uboot,openSBl,ntldr(用于启动 Windows 系统),**Linuxboot**等。 + +操作系统需要被加载到内存中正确的位置,且需要程序为操作系统提供启动参数,以实现定制化启动,所以需要 bootloader。 + +## Grub 简介 + +Grub 是 Linux 上最常用的 bootloader。它是一个来自 GNU 项目的启动引导程序。GRUB 允许用户可以在计算机内同时拥有多个操作系统,并在计算机启动时选择希望运行的操作系统,也就是说,我们可以通过 Grub,使用链式引导来引导 Windows 系统。 + +它支持所有的 Linux 文件系统,也支持 Windows 的 FAT 和 NTFS 文件系统,也支持图形界面,可定制启动菜单和背景图片,支持鼠标。同时,它拥有丰富的终端命令,用户可以查看硬盘分区的细节,修改分区设置,临时重新映射磁盘顺序,从任何用户定义的配置文件启动。 + +感兴趣的同学们可以去自行深入了解 Grub。 + +## Init 简介 + +初始化(Init)程序负责操作系统的初始化操作,它基于`/etc/inittab`(定义了系统默认运行级别)设定的动作来执行脚本。以下是初始化流程的主要步骤: + +1. **执行`/etc/rc.d/rc.sysinit`脚本** + + - 真正的 OS 初始化脚本。 + +2. **激活 udev 和 SELinux** + + - udev 管理设备节点和它们的符号链接。 + - SELinux(如果启用)是 Linux 的一个安全模块。 + +3. **设定内核参数** + + - 根据`/etc/sysctl.conf`文件来设定内核参数。 + +4. **设定系统时钟** + + - 确保系统时间正确。 + +5. **装载硬盘映射** + + - 加载硬盘相关的映射或配置。 + +6. **启用交换分区** + + - 激活交换空间以扩展系统内存。 + +7. **设置主机名** + + - 设置系统在网络中的名称。 + +8. **根文件系统检测与重新挂载** + + - 以读写方式重新挂载根文件系统,并在挂载前进行检测。 + +9. **激活 RAID 和 LVM 设备** + + - 如果配置有 RAID 或 LVM,则激活这些设备。 + +10. **启用磁盘配额** + + - 根据配置,启用文件系统上的磁盘配额。 + +11. **检查并挂载其他文件系统** + + - 根据`/etc/fstab`文件,检查并挂载其他文件系统。 + +12. **清理过期的锁和 PID 文件** + + - 清除在系统启动过程中可能不再需要的锁文件和 PID 文件。 + +13. **执行对应启动级别的脚本** + + - 根据配置的启动级别,执行对应目录(如`/etc/rcX.d/`,其中`X`是运行级别)下的脚本。 + +14. **执行`/etc/rc.d/rc.local`脚本** + - 这是系统初始化过程中的最后一个通用脚本,用户可以在这里添加自定义的启动命令。 + +**系统启动完成** + +- 执行完以上步骤后,系统启动完成,等待用户登录或其他进一步的操作。 + +## RunLevel 简介 + +runlevel,即运行级别,是 Linux 系统中的一个概念。 +Linux 的启动级别定义了系统启动时应该运行哪些服务。 + +运行级别有如下的分类: + +- **0**: 关机模式 +- **1**: 单一用户模式(直接以管理员身份进入) +- **2**: 多用户模式(无网络) +- **3**: 多用户模式(命令行) +- **4**: 保留(通常不使用) +- **5**: 多用户模式(图形界面) +- **6**: 重启 + +运行级别对应的脚本目录如下所示: + +- **Run level 0**: `/etc/rc.d/rc0.d/` +- **Run level 1**: `/etc/rc.d/rc1.d/` +- **Run level 2**: `/etc/rc.d/rc2.d/` +- **Run level 3**: `/etc/rc.d/rc3.d/` +- **Run level 4**: `/etc/rc.d/rc4.d/` +- **Run level 5**: `/etc/rc.d/rc5.d/` +- **Run level 6**: `/etc/rc.d/rc6.d/` + +在上述目录中,脚本的命名遵循以下规则: + +- **K** 开头的文件:表示在启动时需要关闭的服务。 +- **S** 开头的文件:表示在启动时需要启动的服务。 + +## 目前进展(init->systemd) + +对于 Init 阶段来说,它具有一些问题: + +- 启动时间长。ini 进程是串行启动,只有前一个进程启动完才会启动下一个进程。 +- 启动脚本复杂。脚本需要自己处理各种情况,这往往使得脚本变得很长。 + +为了解决上述问题,Linux 系统引入了 Systemd 系统。感兴趣的同学们可以自行了解。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch5-00.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch5-00.md new file mode 100644 index 0000000000000000000000000000000000000000..50434311cb7defeecc7164ca2264880d14696c39 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch5-00.md @@ -0,0 +1,17 @@ +# 第五章 - 第 3 课(下) - Risc-V 的初始化 + +## 本章概述 + +在上一章内容里,我们介绍了系统的初始化流程,并且以 Linux 系统为例,介绍了 Linux 系统启动的各个阶段。本章则详细介绍了 Risc-V 架构下的系统初始化过程,包括从启动流程、链接脚本、页表和异常向量的设置等内容。 + +在第一节,我们会讲一下 Risc-V 设备的一般的启动流程。我们会介绍到 bootloader 和链接脚本(Linker Script),它们扮演着重要的角色。链接脚本定义了如何将程序的不同部分(如代码、数据和栈)映射到内存中的不同位置,也是我们编写内核过程中需要掌握的终点。 + +在第二节,我们延续上一章介绍过的页表和异常向量的初始化过程,只需要同学们稍作了解,具体的细节会在下一阶段详细介绍。 + +在第三节,我们描述了从系统上电到操作系统开始运行的过程。我们将介绍几个重要的引导加载程序,即 OpenSBI 和 U-Boot,它们在系统启动过程中扮演着桥梁的角色,负责初始化硬件、加载操作系统镜像等任务。此外,我们还将探讨 DeviceTree 和 UEFI 在 Risc-V 系统初始化中的作用,以及它们之间的对比和特点。 + +## 本章目录 + +- [启动流程和链接脚本](./ch5-01.md) +- [初始化](./ch5-02.md) +- [Risc-V 设备从上电到运行](./ch5-03.md) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch5-01.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch5-01.md new file mode 100644 index 0000000000000000000000000000000000000000..338300f0f6d40f0675eb328b665aac59b3d3de23 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch5-01.md @@ -0,0 +1,42 @@ +# 启动流程和链接脚本 + +## 本节目录 + +- [启动流程和链接脚本](#启动流程和链接脚本) + - [本节目录](#本节目录) + - [一般启动流程](#一般启动流程) + - [链接脚本](#链接脚本) + - [内核代码](#内核代码) + +## 一般启动流程 + +对于 Risc-V 开发板,我们仍然以 Linux 系统为例,其一般的启动流程如下: + +- 板子上电后,CPU 从固定地址运行 ROM 中的代码 +- ROM 包含简单的设备驱动,从 fash 或者 SD 卡中加载 bootloader +- 再由 bootloader 加载内核、initramfs 等到内存,跳转到 Linux 内核启动 + +在一个基于全志 D1 硬件平台的启动流程内,主要涉及到 SPL(Secondary Program Loader)、U-Boot 和 Linux 操作系统。在这个流程中,它们的角色和各自的加载地址如下: + +SPL,二级程序加载器,通常位于设备的 ROM(只读存储器)中。它是最先被执行的代码,负责执行一些基本的硬件初始化和准备工作。SPL 的任务主要就是将下一级的引导加载器(如 U-Boot)从存储设备加载到 RAM 中。 + +U-Boot,一个开源的引导加载器,广泛用于嵌入式系统。在这个流程中,U-Boot 是由 SPL 从存储设备加载到 RAM 中,并从物理地址 `0x4a00_0000` 开始执行。U-Boot 会将 Linux 内核镜像从存储设备加载到 RAM 中的某个位置,并设置必要的启动参数,然后将控制权传递给 Linux 内核。 + +Linux 是最终要执行的操作系统。在这个流程中,Linux 内核从物理地址 `0x4200_0000` 开始执行。 + +感兴趣的同学们可以通过以下资料获取相关内容: +[参考资料](https://linux-sunxi.org/Allwinner_Nezha) + +## 链接脚本 + +链接脚本(Linker Script)是用于定义如何将程序的不同部分(如代码、数据、栈等)映射到内存中的文件。在嵌入式系统和操作系统开发中,链接脚本是一个非常重要的文件,因为它决定了程序在内存中的布局和大小。 + +在 Risc-V 系统中,链接脚本通常使用特定的语法来描述内存布局和符号地址。它定义了程序的各个段(如代码段、数据段、BSS 段等)的起始地址和大小,以及它们之间的相对位置。链接器(Linker)在编译过程中使用链接脚本来确定程序的最终内存布局,并生成可执行文件或镜像文件。 + +对于链接脚本来说,同学们需要了解程序的编译过程,具体要了解到代码段的更细的划分、数据段的更细的划分,需要同学们进行自学,可以在下面资料进行参考:[参考资料](https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/3first-instruction-in-kernel1.html#id6)。 + +## 内核代码 + +内核代码是操作系统的核心部分,负责管理硬件资源、提供系统服务以及运行用户程序。在 Risc-V 系统中,内核代码通常使用 C 语言(或汇编语言)编写,并遵循特定的编程规范和接口标准。 + +对于内核代码,其二进制从.head.text 开始,剩余内容,请同学们跟着老师的课程进行学习,主要会讲解到 Linux 下的地址映射、内核运行的代码顺序等知识点。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch5-02.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch5-02.md new file mode 100644 index 0000000000000000000000000000000000000000..584b4ee9a0e08aa26750396b6324ab74dc428281 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch5-02.md @@ -0,0 +1,58 @@ +# 初始化 + +## 本节目录 + +- [初始化](#初始化) + - [本节目录](#本节目录) + - [页表和 MMU](#页表和-mmu) + - [Risc-V 页表初始化](#risc-v-页表初始化) + - [启用 MMU 前后](#启用-mmu-前后) + - [Risc-V 异常向量初始化](#risc-v-异常向量初始化) + - [小结](#小结) + +## 页表和 MMU + +- Linux 内核运行需要 MMU 启用 +- 内核刚开始运行,MMU 未启用 +- 需要写好一个页表将内核映射到高地址,然后启用 MMU + +## Risc-V 页表初始化 + +`setup_vm` 初始化启动用到的两个页表: + +- `trampoline_pg_dir`:启用 MMU 前后所用,映射启用 MMU 的代码到高地址 +- `early_pg_dir`:内核最初启动的时候所用,映射整个内核到高地址 + +具体的实现逻辑,请自学或参照老师的课程进行学习。 + +## 启用 MMU 前后 + +`relocate_enable_mmu` 用于启用 MMU,并跳转到高地址继续执行: + +- 使用 `trampoline_pg_dir` 衔接 +- 启用 MMU +- 返回后 `pc` 在高地址 + +这一部分涉及到一个问题:内核启动时从 `0x4020_0000` 开始,而内核代码需要在 `0xFFFFFFFF80000000` 运行。如何配置 MMU 来完成这个切换? + +通过配置 MMU 来完成这个切换的方法是: + +直接设置返回地址加上偏移量的结果,将其变成高地址,这样在返回后就能正确运行了。 + +## Risc-V 异常向量初始化 + +通过 `setup_trap_vector` 初始化异常向量: + +- RISC-V Linux 使用一个统一的入口点 handle_exception 来处理异常和中断 +- scratch=0 表示异常从内核态发生 +- sscratch ≠ 0 时,它指向一个 task_struct 结构体,表示异常从用户态发生 +- RISC-V 的系统调用也被视为一种异常处理 + +## 小结 + +在 Risc-V 初始化过程中,主要完成以下任务: + +- 设置初始化时的简单页表 early_pg_dir,并开启虚拟内存机制(MMU) +- 设置异常向量 stvec 指向 handle_exception 函数 + - 在处理异常之前保存进程上下文 + - 在返回进程之前恢复其上下文 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch5-03.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch5-03.md new file mode 100644 index 0000000000000000000000000000000000000000..de077bb55a1f67d9d75d7f78e8e6965f41b85fa3 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch5-03.md @@ -0,0 +1,84 @@ +# Risc-V 设备从上电到运行 + +## 本节目录 + +- [Risc-V 设备从上电到运行](#risc-v-设备从上电到运行) + - [本节目录](#本节目录) + - [流程(OpenSbi 和 U-boot)](#流程opensbi-和-u-boot) + - [DeviceTree](#devicetree) + - [Risc-V UEFI](#risc-v-uefi) + - [对比](#对比) + - [Risc-V 嵌入式设备启动的特点](#risc-v-嵌入式设备启动的特点) + +## 流程(OpenSbi 和 U-boot) + +这一部分,我们以常用的基于 U-Boot 和 OpenSBl 的启动流程为例。 + +通过上一章的学习,我们知道,在上电之后,会从 ROM 中开始执行代码, +在 SoC(System on Chip)架构下, 芯片内部包含一块 ROM,保存了一些简单的驱动,同时会从 SD 卡或 fash 加载 U-Boot SPL 到一块小内存中(单独的 SRAM 或 L2cache-as-RAMM)。然后会又 U-Boot SPL 进行最早的初始化,主要是初始化 DDR 内存、时钟等最重要的设备,并且从 SD 卡加载 OpenSBI 和 U-Boot 本体到 DDR 内存,然后跳转到 OpenSBI 运行。 + +对于 OpenSBI 和 U-Boot 来说,它们执行下面工作: +OpenSBI: + +- 初始化 IPI 和时钟设备 +- 初始化自身准备好提供 SBI 服务 +- 切换到 Supervisor mode,跳转到 U-Boot + +U-Boot: + +- 从预先配置的启动设备加载内核到内存(来源可以是 SD 卡、NVMe、网络……) +- 跳转到内核入口点 + +之后,操作系统就能够开始运行了。 + +## DeviceTree + +那么,软件如何获知机器上有哪些功能和设备呢? + +对于 ROM 来说,其内部的代码比较固定,可以在直接代码里对照硬件资源编写。但对于通用的操作系统,例如 Linux 内核,如何在各种机器上都可以运行? + +对于嵌入式设备来说,解决获知设备问题的方法是 Devicetree。 + +- 文本格式(Devicetree source,DTS)和二进制格式(Flattened devicetree,FDT 或 DTB) +- 包含设备信息、MMIO 地址、中断连接方式、时钟复位电源连接方式等信息 +- 与内核同时加载到内存中,跳转到各软件入口点时传入地址 +- 惯例:a0 是当前核心的 hartid,a1 是内存中 FDT 的物理地址 +- (U-Boot 和 OpenSBl 为了方便及复用 devicetree 已经完成的工作,也用 FDT 获得硬件配置) + +## Risc-V UEFI + +RISC-V 机器固件也可以提供 UEFI 接口 + +- 提供 PCle 控制器的驱动,可以以通用方式访问 PCle 设备 +- 提供 PCle Option ROM 支持,在早期启动时就可以用上外部设备的功能(显卡的显示功能、网卡的网络启动功能.) + +操作系统通过 ACPI 获知硬件信息 + +- 比 devicetree 提供更多的信息,还提供更多接口,如通用的 PCle 设备访问,热插拔通知,电源管理接口 +- 当然也比 devicetree 复杂 +- (在比较简单的设备上 UEFI 也可以不提供 ACPI 而提供 devicetree) + +## 对比 + +定制化的主板(常见的 RISC-V 开发板,通常不再扩展其他设备) + +- 需要初始化具体主板相关硬件如 GPIO 和内存等 +- 从 devicetree 获知有哪些设备 +- 操作系统中需要很多设备相关的驱动 + +通用的主板(常见如 PC,通常需要再插入其他设备) + +- 系统配置情况在开机时候是不知道的 +- 需要探测(Probe)、Training(内存和 PCle)和枚举(PCle 等等即插即用设备) +- UEFI/ACPI 提供了整个主板、包括主板上外插的设备的软件抽象,通过定义的接口把这些信息传递给 OS,使 OS 不改而能够适配到所有机型和硬件 + +## Risc-V 嵌入式设备启动的特点 + +通常与设备强相关 + +- ROM 通常是闭源的 +- 早期启动流程通常是厂商提供的代码,例如 DDR 内存控制器的初始化代码,即使有 C 源码,一般也是无法理解其中很多魔法数字 + +缺点:对可插拔外设的兼容性 + +- 如 RISC-V 嵌入式设备支持 PCle 外设,需要操作系统内核包含具体硬件上的 PCle 控制器驱动 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-00.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-00.md new file mode 100644 index 0000000000000000000000000000000000000000..6e02bf2ddbd0993887817e12080e9dc1e2707b8b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-00.md @@ -0,0 +1,42 @@ +# 第六章 - 题目分析[1 - 9] + +## 本章概要 + +本章主要进行的习题知识点范围如下: + +- 实验代码框架讲解 +- MakeFile 脚本分析 +- ld 脚本分析 + +对于 MakeFile 文档,我们将会进行 5 道练习题,内容分别为: + +1. 编写第一份 MakeFile +2. 编写用于测试的 MakeFile +3. 使用 Makefile 静态链接程序 +4. 使用 Makefile 构建第一个静态链接库 +5. 使用 Makefile 构建第一个动态链接库 + +在 MakeFile 题目中,涉及到了 gcc 编译的一部分内容和一些 MakeFile 的基础知识点。 + +对于 ld 脚本,我们将会进行 4 道练习题,内容分别是: + +1. 编写一个简单的 ld 文件并指定内存区域。 +2. 编写一个简单的 ld 文件并指定 text 起始地址。 +3. 编写一个简单的 ld 文件并指定自定义 symbol。 +4. 编写一个简单的 ld 文件并指定自定义 section。 + +这部分内容要求同学们对于 ld 脚本的结构有一定的了解,并且要了解大致的编译原理。 + +## 本章目录 + +- [实验代码框架讲解](./ch6-01.md) +- [实验代码框架讲解](./ch6-01.md) +- [第一题 - MakeFile - 编写第一份 MakeFile](./ch6-02.md) +- [第二题 - MakeFile - 编写用于测试的 MakeFile](./ch6-03.md) +- [第三题 - MakeFile - 静态链接程序](./ch6-04.md) +- [第四题 - MakeFile - 构建第一个静态链接库](./ch6-05.md) +- [第五题 - MakeFile - 构建第一个动态链接库](./ch6-06.md) +- [第六题 - ld 脚本 - 指定内存区域](./ch6-07.md) +- [第七题 - ld 脚本 - 指定 text 起始地址](./ch6-08.md) +- [第八题 - ld 脚本 - 指定自定义 symbol](./ch6-09.md) +- [第九题 - ld 脚本 - 指定自定义 section](./ch6-10.md) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-01.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-01.md new file mode 100644 index 0000000000000000000000000000000000000000..2b1e6d5c81c74175dedab0d579a56b5691586b38 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-01.md @@ -0,0 +1,180 @@ +# 实验代码框架讲解 + +## 本节目录 + +- [实验代码框架讲解](#实验代码框架讲解) + - [本节目录](#本节目录) + - [项目结构](#项目结构) + - [Makefile 内容](#makefile-内容) + - [使用 makefile 进行实验](#使用-makefile-进行实验) + +## 项目结构 + +```bash +. +├── exercises //所有习题都在此文件夹下 +├── LICENSE +├── Makefile +├── README.en.md +├── README.md +└── test //所有测例都在此文件夹下 +``` + +- **exercises**:这个文件夹包含了所有的习题代码。每个习题可以作为一个子文件夹,其中包含习题的源代码、数据文件和任何相关的资源。 +- **LICENSE**:项目的许可证文件,定义了项目的使用、修改和分发的法律条款。 +- **Makefile**:一个自动化构建脚本,用于编译、测试和清理项目。通过运行 `make` 命令,可以执行 Makefile 中定义的任务。 +- **test**:这个文件夹包含了所有的测试用例。每个测试用例可以作为一个子文件夹或文件,用于验证习题代码的正确性。 + +## Makefile 内容 + +实验框架的 Makefile: + +```Makefile +# Makefile variables for directory structure and tools +AUX_DIR := aux # Auxiliary directory +BUILD_DIR := build # Build output directory +SRC_DIR := src # Source code directory +RESULT_DIR := result # Test result directory +TEST_DIR := test # Directory containing test scripts + +# Compiler settings +CC ?= gcc # Default compiler + +# Compiler flags +CFLAGS = -std=c11 -Wall -Wextra -Wpedantic -Werror -g # C compiler flags +LDFLAGS = -Wl,--as-needed -Wl,--no-undefined # Linker flags + +# Find all exercise directories under SRC_DIR +EXERCISES := $(shell find $(SRC_DIR) -type d -name 'exercise-*' -exec basename {} \;) + +# Default target: build all exercises +all: $(EXERCISES) + +# Build each exercise directory +$(EXERCISES): + @mkdir -p $(abspath $(BUILD_DIR))/$(notdir $@) + $(MAKE) -C $(SRC_DIR)/$@ OUTPUT_DIR=$(abspath $(BUILD_DIR))/$(notdir $@) || true + +# Test target: run tests for all exercises +test: $(RESULT_DIR) $(EXERCISES:%=test-%) report generate_json_report + +# Test each exercise +test-%: $(BUILD_DIR)/% + @mkdir -p $(RESULT_DIR)/$* + @echo "Running tests for $*" + @$(TEST_DIR)/$*/test.sh $(BUILD_DIR)/$*/$* && touch $(RESULT_DIR)/$*/$*.pass || touch $(RESULT_DIR)/$*/$*.fail + +# Create build directory if it doesn't exist +$(BUILD_DIR): + @mkdir -p $(BUILD_DIR) + +# Create result directory if it doesn't exist +$(RESULT_DIR): + @mkdir -p $(RESULT_DIR) + +# Generate a human-readable test report +report: + @echo "Generating test report..." + @total=$$(find $(RESULT_DIR) -type f -name '*.pass' -o -name '*.fail' | wc -l); \ + success=$$(find $(RESULT_DIR) -type f -name '*.pass' | wc -l); \ + failed=$$(find $(RESULT_DIR) -type f -name '*.fail' | wc -l); \ + echo "Total tests: $$total"; \ + echo "Success: $$success"; \ + echo "Failed: $$failed"; \ + echo "Success tests:"; \ + find $(RESULT_DIR) -type f -name '*.pass' -exec basename {} .pass \; | sed 's/^/ /'; \ + echo "Failed tests:"; \ + find $(RESULT_DIR) -type f -name '*.fail' -exec basename {} .fail \; | sed 's/^/ /' + +# Generate a JSON test report +generate_json_report: + @echo "Generating JSON report..." + @success=$$(find $(RESULT_DIR) -type f -name '*.pass' | wc -l); \ + score=$$((success * 5)); \ + total_score=100; \ + channel="gitee"; \ + course_id=1546; \ + ext="aaa"; \ + name=""; \ + echo '{' > $(RESULT_DIR)/report.json; \ + echo ' "channel": "'$$channel'"'',' >> $(RESULT_DIR)/report.json; \ + echo ' "courseId": ' $$course_id',' >> $(RESULT_DIR)/report.json; \ + echo ' "ext": "'$$ext'"'',' >> $(RESULT_DIR)/report.json; \ + echo ' "name": "'$$name'"'',' >> $(RESULT_DIR)/report.json; \ + echo ' "score": ' $$score',' >> $(RESULT_DIR)/report.json; \ + echo ' "totalScore": ' $$total_score >> $(RESULT_DIR)/report.json; \ + echo '}' >> $(RESULT_DIR)/report.json; + +# Clean target: remove all generated files and directories +clean: + for dir in $(EXERCISES); do \ + $(MAKE) -C $$dir clean; \ + done + rm -rf $(BUILD_DIR) $(RESULT_DIR) + +# Declare phony targets to prevent conflicts with file names +.PHONY: all clean $(EXERCISES) report generate_json_report +``` + +- **目录定义**: + - `EXERCISE_DIR`:存放练习文件的目录。 + - `TEST_DIR`:存放测试文件的目录。 + - `BUILD_DIR`:存放构建产物的目录。 +- **文件和目录操作**: + - `mkdir -p $(TEST_DIR) $(BUILD_DIR)`:确保测试和构建目录存在。 +- **编译和链接**: + - `CC = gcc`:定义编译器为 gcc。 + - `CFLAGS = -Wall -Wextra -std=c99`:编译选项,包括警告和 C99 标准。 + - `LDFLAGS = -lm`:链接选项,链接数学库。 +- **构建规则**: + - `$(BUILD_DIR)/%: $(EXERCISE_DIR)/%.c`:为每个练习文件生成对应的执行文件。 +- **清理规则**: + - `clean`:删除所有执行文件和构建目录下的对象文件。 +- **测试规则**: + - `generate-test-cases`:生成测试用例。 + - `test-output`:运行测试并比较输出结果。 + - `save-test-results`:保存测试结果和通过率到 JSON 文件。 + +## 使用 makefile 进行实验 + +在实验过程中,Makefile 提供了自动化构建和测试的便利。以下是如何使用这个 Makefile 进行实验的步骤: + +1. **编译所有练习**: + + ```bash + make + ``` + + 这个命令会编译所有练习文件,并在`BUILD_DIR`目录下生成对应的执行文件。 + +2. **生成测试用例**: + + ```bash + make generate-test-cases + ``` + + 这个命令会运行所有执行文件,并将输出保存为测试用例。 + +3. **运行测试**: + + ```bash + make test-output + ``` + + 这个命令会比较每个执行文件的输出与预期结果,并显示测试结果。 + +4. **保存测试结果**: + + ```bash + make save-test-results + ``` + + 这个命令会计算测试的通过率,并将结果保存到`test_results.json`文件中。 + +5. **清理**: + + ```bash + make clean + ``` + + 这个命令会删除所有构建产物和对象文件,保持环境整洁。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-02.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-02.md new file mode 100644 index 0000000000000000000000000000000000000000..548f7079887bac5b3fdf18b94e64247ae4e8c4a1 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-02.md @@ -0,0 +1,83 @@ +# 第一题 - MakeFile - 编写第一份 MakeFile + +## 本节目录 + +- [第一题 - MakeFile - 编写第一份 MakeFile](#第一题---makefile---编写第一份-makefile) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [源代码文件](#源代码文件) + - [MakeFile 文件](#makefile-文件) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写你的第一个 Makefile 并运行,使结果能够通过测试。结果应该是,生成一个名为 `exercise-01` 的可执行文件。 + +## 输入 + +```bash +# 将会在测试脚本中尝试: +make all +``` + +## 输出 + +应该产生一个名为 exercise-01 的可执行文件。 + +## 已有代码介绍 + +已有三个源代码文件和一个 MakeFile 文件,你只需要修改 MakeFile 文件即可。 + +### 源代码文件 + +- `main.c`: 程序的入口点 +- `functions.c`: 提供各种函数,其中包括 hello 函数 +- `functions.h`: 提供函数声明 + +### MakeFile 文件 + +```makefile +CC ?= gcc + +CFLAGS ?= -std=c11 -Wall -Wextra -Wpedantic -Werror -g +LDFLAGS ?= -Wl,--as-needed -Wl,--no-undefined + +OUTPUT_DIR ?= . + +SRCS = main.c functions.c +OBJS = $(SRCS:.c=.o) +TARGET = $(OUTPUT_DIR)/exercise-01 + +all: $(TARGET) + +$(OUTPUT_DIR): + mkdir -p $(OUTPUT_DIR) + +$(TARGET): $(OBJS) + # 在这里指定 TARGET 的构建命令 + + +%.o: %.c + # 在这里指定 C 文件的编译命令 + +clean: + rm -f $(TARGET) $(OBJS) +``` + +## 提示 + +你需要在 MakeFile 文件中的中文注释附近,完成所需要执行的逻辑。如果遇到困难,那么大概率是因为三种情况引起: + +1. 对 MakeFile 不熟,读不懂框架代码 +2. 只熟悉写死的 MakeFile,不知道使用 MakeFile 里的变量,读不懂框架代码 +3. 对 gcc 编译不熟,知道怎么写 MakeFile 但是不知道怎么写编译命令 + +如果遇到上面问题,可以对症下药,通过视频、AI、问老师等多种方式进行解决。 + +## 注意事项 + +**不要修改源代码文件!** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-03.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-03.md new file mode 100644 index 0000000000000000000000000000000000000000..db3f48df77ac0f2e7cebfcfcd1dca60d7c73cdea --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-03.md @@ -0,0 +1,94 @@ +# 第二题 - MakeFile - 编写用于测试的 MakeFile + +## 本节目录 + +- [第二题 - MakeFile - 编写用于测试的 MakeFile](#第二题---makefile---编写用于测试的-makefile) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [源代码文件](#源代码文件) + - [MakeFile 文件](#makefile-文件) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写你的第一个 Makefile 并运行,使结果能够通过测试。结果应该是,既生成可执行文件 `exercise-02`,又生成可执行文件 `exercise-02_test`。 + +## 输入 + +```bash +# 将会在测试脚本中尝试: +make all +``` + +## 输出 + +应该产生一个名为 `exercise-02` 的可执行文件和一个名为 `exercise-02_test` 的可执行文件。 + +## 已有代码介绍 + +已有四个源代码文件和一个 MakeFile 文件,你只需要修改 MakeFile 文件即可。 + +### 源代码文件 + +- `main.c`: 程序的入口点 +- `functions.c`: 提供各种函数,其中包括 hello 函数 +- `functions.h`: 提供函数声明 +- `test.c`: 用于提供测试的源代码文件 + +### MakeFile 文件 + +```makefile +CC ?= gcc + +CFLAGS ?= -std=c11 -Wall -Wextra -Wpedantic -Werror -g +LDFLAGS ?= -Wl,--as-needed -Wl,--no-undefined + +OUTPUT_DIR ?= . + +SRCS = main.c functions.c +OBJS = $(SRCS:.c=.o) +TARGET = $(OUTPUT_DIR)/exercise-02 + +TEST_SRCS = test.c functions.c +TEST_OBJS = $(TEST_SRCS:.c=.o) +TEST_TARGET = $(OUTPUT_DIR)/exercise-02_test + +all: $(TARGET) test + +$(OUTPUT_DIR): + mkdir -p $(OUTPUT_DIR) + +$(TARGET): $(OBJS) + # 在这里指定 TARGET 的构建命令 + +$(TEST_TARGET): $(TEST_OBJS) + # 在这里指定 TEST_TARGET 的构建命令 + +%.o: %.c + # 在这里指定所有 C 文件的编译命令 + +test: $(TEST_TARGET) + $(TEST_TARGET) + +clean: + rm -f $(TARGET) $(TEST_TARGET) $(OBJS) $(TEST_OBJS) + +``` + +## 提示 + +你需要在 MakeFile 文件中的中文注释附近,完成所需要执行的逻辑。有和上一题相似的地方的话,可以考虑复用。剩下的内容,如果遇到困难,那么大概率是因为三种情况引起: + +1. 对 MakeFile 不熟,读不懂框架代码 +2. 只熟悉写死的 MakeFile,不知道使用 MakeFile 里的变量,读不懂框架代码 +3. 对 gcc 编译不熟,知道怎么写 MakeFile 但是不知道怎么写编译命令 + +如果遇到上面问题,可以对症下药,通过视频、AI、问老师等多种方式进行解决。 + +## 注意事项 + +**不要修改源代码文件!** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-04.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-04.md new file mode 100644 index 0000000000000000000000000000000000000000..be96c8c34e8e19bd355ef8720f69025d7f5a0398 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-04.md @@ -0,0 +1,95 @@ +# 第三题 - MakeFile - 静态链接程序 + +## 本节目录 + +- [第三题 - MakeFile - 静态链接程序](#第三题---makefile---静态链接程序) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [源代码文件](#源代码文件) + - [MakeFile 文件](#makefile-文件) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +- 编译和链接两个源文件 `functions.c` 和 `main.c` 以生成一个可执行程序。 +- 修改 Makefile,能够将 `functions.c` 编译为中间产物 `functions.a`,最终使用该产物静态链接出可执行程序并运行它。 +- 此处的“它”指能够成功生成 Makefile 中的 TARGET 和 TEST_TARGET。 + +## 输入 + +```bash +# 将会在测试脚本中尝试: +make all +``` + +## 输出 + +应该产生一个名为 `exercise-03` 的可执行文件和一个名为 `exercise-03_test` 的可执行文件。 + +## 已有代码介绍 + +已有五个源代码文件和一个 MakeFile 文件,你只需要修改 MakeFile 文件即可。 + +### 源代码文件 + +- `main.c`: 程序的入口点 +- `functions.c`: 提供各种函数,其中包括 hello 函数 +- `functions.h`: 提供函数声明 +- `test.c`:用于提供对 hello 函数进行测试的源代码文件 +- `tests.c`:用于测试静态链接情况的源代码文件 + +### MakeFile 文件 + +```makefile +CC ?= gcc + +CFLAGS = -std=c11 -Wall -Wextra -Wpedantic -Werror -g +# 注意需要指定 -static +LDFLAGS = -static + +OUTPUT_DIR ?= . + +SRCS = main.c functions.c +OBJS = $(SRCS:.c=.o) +TARGET = $(OUTPUT_DIR)/exercise-03 + +TEST_SRCS = test.c functions.c +TEST_OBJS = $(TEST_SRCS:.c=.o) +TEST_TARGET = $(OUTPUT_DIR)/exercise-03_test + +all: $(TARGET) + +$(TARGET): $(OBJS) + # 在这里指定 TARGET 的构建命令 + +$(TEST_TARGET): $(TEST_OBJS) + # 在这里指定 TEST_TARGET 的构建命令 + +%.o: %.c + # 在这里指定所有 C 文件的编译命令 + +test: $(TEST_TARGET) + ./$(TEST_TARGET) + +clean: + rm -f $(TARGET) $(TEST_TARGET) $(OBJS) $(TEST_OBJS) + +``` + +## 提示 + +你需要在 MakeFile 文件中的中文注释附近,完成所需要执行的逻辑。有和上一题相似的地方的话,可以考虑复用。剩下的内容,如果遇到困难,那么大概率是因为三种情况引起: + +1. 对 MakeFile 不熟,读不懂框架代码 +2. 只熟悉写死的 MakeFile,不知道使用 MakeFile 里的变量,读不懂框架代码 +3. 对 gcc 编译不熟,知道怎么写 MakeFile 但是不知道怎么写编译命令 + +如果遇到上面问题,可以对症下药,通过视频、AI、问老师等多种方式进行解决。比如,可以搜索“gcc 静态链接”。 + +## 注意事项 + +**不要修改源代码文件!** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-05.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-05.md new file mode 100644 index 0000000000000000000000000000000000000000..1a9af4273af23cafaf9c1ff53e93d64c5eb7210e --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-05.md @@ -0,0 +1,107 @@ +# 第四题 - MakeFile - 构建第一个静态链接库 + +## 本节目录 + +- [第四题 - MakeFile - 构建第一个静态链接库](#第四题---makefile---构建第一个静态链接库) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [源代码文件](#源代码文件) + - [MakeFile 文件](#makefile-文件) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +- 修改 Makefile,能够将 `functions.c` 编译为静态库 `libfunctions.a` +- 使用该库编译出可执行程序并运行它。 +- 此处的“它”指能够成功生成 Makefile 中的 TARGET 和 TEST_TARGET。 + +## 输入 + +```bash +# 将会在测试脚本中尝试: +make all +``` + +## 输出 + +应该产生一个名为 `exercise-04` 的可执行文件和一个名为 `exercise-04_test` 的可执行文件。 + +## 已有代码介绍 + +已有四个源代码文件和一个 MakeFile 文件,你只需要修改 MakeFile 文件即可。 + +### 源代码文件 + +- `main.c`: 程序的入口点 +- `functions.c`: 提供各种函数,其中包括 hello 函数 +- `functions.h`: 提供函数声明 +- `test.c`:用于提供对 hello 函数进行测试的源代码文件 + +### MakeFile 文件 + +```makefile +CC ?= gcc + +CFLAGS = -std=c11 -Wall -Wextra -Wpedantic -Werror -g +LDFLAGS = -Wl,--as-needed -Wl,--no-undefined + +OUTPUT_DIR ?= . + +SRCS = main.c +OBJS = $(SRCS:.c=.o) +TARGET = $(OUTPUT_DIR)/exercise-04 + +LIB_NAME = libfunctions.a +LIB_TARGET = $(OUTPUT_DIR)/$(LIB_NAME) +LIB_SRC = functions.c +LIB_OBJ = $(LIB_SRC:.c=.o) + +TEST_SRCS = test.c functions.c +TEST_OBJS = $(TEST_SRCS:.c=.o) +TEST_TARGET = $(OUTPUT_DIR)/exercise-04_test + +all: $(TARGET) test + +$(OUTPUT_DIR): + mkdir -p $(OUTPUT_DIR) + +$(LIB_OBJ): $(LIB_SRC) + # 在这里指定如何编译 functions.o + +$(LIB_TARGET): $(LIB_OBJ) + # 在这里指定如何生成静态链接库 + +$(TARGET): $(OBJS) $(LIB_TARGET) + # 在这里指定如何链接 libfunctions.a 来生成 TARGET + +$(TEST_TARGET): $(TEST_OBJS) $(LIB_TARGET) + # 在这里指定如何链接 libfunctions.a 来生成 TEST_TARGET + +%.o: %.c + # 在这里指定所有 C 文件的编译命令 + +test: $(TEST_TARGET) + $(TEST_TARGET) + +clean: + rm -f $(LIB_TARGET) $(TARGET) $(TEST_TARGET) $(OBJS) $(LIB_OBJ) $(TEST_OBJS) + +``` + +## 提示 + +你需要在 MakeFile 文件中的中文注释附近,完成所需要执行的逻辑。有和上一题相似的地方的话,可以考虑复用。剩下的内容,如果遇到困难,那么大概率是因为三种情况引起: + +1. 对 MakeFile 不熟,读不懂框架代码 +2. 只熟悉写死的 MakeFile,不知道使用 MakeFile 里的变量,读不懂框架代码 +3. 对 gcc 编译不熟,知道怎么写 MakeFile 但是不知道怎么写编译命令 + +如果遇到上面问题,可以对症下药,通过视频、AI、问老师等多种方式进行解决。比如,可以搜索“gcc 生成静态库”。 + +## 注意事项 + +**不要修改源代码文件!** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-06.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-06.md new file mode 100644 index 0000000000000000000000000000000000000000..22393400c5c177bff275ee77272890505becde16 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-06.md @@ -0,0 +1,108 @@ +# 第五题 - MakeFile - 构建第一个动态链接库 + +## 本节目录 + +- [第五题 - MakeFile - 构建第一个动态链接库](#第五题---makefile---构建第一个动态链接库) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [源代码文件](#源代码文件) + - [MakeFile 文件](#makefile-文件) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +- 修改 Makefile,能够将 `functions.c` 编译为动态链接库 `libfunctions.so` +- 使用该库编译出可执行程序并运行它。 +- 此处的“它”指能够成功生成 Makefile 中的 TARGET 和 TEST_TARGET。 + +## 输入 + +```bash +# 将会在测试脚本中尝试: +make all +``` + +## 输出 + +应该产生一个名为 `exercise-05` 的可执行文件和一个名为 `exercise-05_test` 的可执行文件。 + +## 已有代码介绍 + +已有四个源代码文件和一个 MakeFile 文件,你只需要修改 MakeFile 文件即可。 + +### 源代码文件 + +- `main.c`: 程序的入口点 +- `functions.c`: 提供各种函数,其中包括 hello 函数 +- `functions.h`: 提供函数声明 +- `test.c`:用于提供对 hello 函数进行测试的源代码文件 + +### MakeFile 文件 + +```makefile +CC ?= gcc + +CFLAGS = -std=c11 -Wall -Wextra -Wpedantic -Werror -g +LDFLAGS = -Wl,--as-needed -Wl,--no-undefined + +OUTPUT_DIR ?= . + +SRCS = main.c +OBJS = $(SRCS:.c=.o) +TARGET = $(OUTPUT_DIR)/exercise-05 + +LIB_NAME = libfunctions.so +LIB_TARGET = $(OUTPUT_DIR)/$(LIB_NAME) +LIB_SRC = functions.c +LIB_OBJ = $(LIB_SRC:.c=.o) +RPATH = -Wl,-rpath,'$$ORIGIN' + +TEST_SRCS = test.c functions.c +TEST_OBJS = $(TEST_SRCS:.c=.o) +TEST_TARGET = $(OUTPUT_DIR)/exercise-05_test + +all: $(TARGET) test + +$(OUTPUT_DIR): + mkdir -p $(OUTPUT_DIR) + +$(LIB_OBJ): $(LIB_SRC) + # 在这里指定如何编译 libfunctions.o + +$(LIB_TARGET): $(LIB_OBJ) + # 在这里指定如何生成 libfunctions.so + +$(TARGET): $(OBJS) $(LIB_TARGET) + # 在这里指定如何链接 libfunctions.so 来生成 TARGET + +$(TEST_TARGET): $(TEST_OBJS) $(LIB_TARGET) + # 在这里指定如何链接 libfunctions.so 来生成 TEST_TARGET + +%.o: %.c + # 在这里指定如何编译所有 C 文件 + +test: $(TEST_TARGET) + $(TEST_TARGET) + +clean: + rm -f $(LIB_TARGET) $(TARGET) $(TEST_TARGET) $(OBJS) $(LIB_OBJ) $(TEST_OBJS) + +``` + +## 提示 + +你需要在 MakeFile 文件中的中文注释附近,完成所需要执行的逻辑。有和上一题相似的地方的话,可以考虑复用。剩下的内容,如果遇到困难,那么大概率是因为三种情况引起: + +1. 对 MakeFile 不熟,读不懂框架代码 +2. 只熟悉写死的 MakeFile,不知道使用 MakeFile 里的变量,读不懂框架代码 +3. 对 gcc 编译不熟,知道怎么写 MakeFile 但是不知道怎么写编译命令 + +如果遇到上面问题,可以对症下药,通过视频、AI、问老师等多种方式进行解决。比如,可以搜索“gcc 生成动态库”。 + +## 注意事项 + +**不要修改源代码文件!** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-07.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-07.md new file mode 100644 index 0000000000000000000000000000000000000000..3bff2fdddc9a0192e2bec43476696703252caf46 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-07.md @@ -0,0 +1,82 @@ +# 第六题 - ld 脚本 - 指定内存区域 + +## 本节目录 + +- [第六题 - ld 脚本 - 指定内存区域](#第六题---ld-脚本---指定内存区域) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个 ld 文件(命名为 `memory_region.ld`),设置内存区域起始地址为 0x8000000,长度为 0x2000。 + +## 输入 + +```shell +# 将会在测试脚本中尝试: +# 打印程序入口地址,匹配是否为 0x8000000 +$(readelf -l "$EXECUTABLE" | grep "Entry point" | head -n 1 | awk '{print $3}') +``` + +## 输出 + +运行测试时,检测到"Test passed."字样。 + +## 已有代码介绍 + +```c +MEMORY { + /* >>> 在这里定义内存区域,设置内存区域起始地址为 `0x8000000`,长度为 `0x2000`。 */ + + /* <<< */ +} + + +SECTIONS { + /* 定义代码段,包含可执行程序的指令 */ + .text : { + *(.text) + /* 其他段的定义 */ + } + + /* 定义只读数据段,包含常量数据 */ + .rodata : { + *(.rodata) + } > RAM + + /* 定义BSS段,包含未初始化的全局变量和静态变量 */ + .bss : { + *(.bss) + } > RAM + + /* 定义程序头表 */ + . = ALIGN(4); + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .dynamic : { *(.dynamic) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame : { *(.eh_frame) } + . = ALIGN(8); + . = . + SIZEOF_HEADERS; +} + +``` + +## 提示 + +你需要了解 ld 脚本的结构,之后,在 Memory 内,按照中文注释完成对应要求。 + +## 注意事项 + +**无特殊注意事项** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-08.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-08.md new file mode 100644 index 0000000000000000000000000000000000000000..60e8cb160589f54bf3686144fb6545ea9efbb342 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-08.md @@ -0,0 +1,83 @@ +# 第七题 - ld 脚本 - 指定 text 起始地址 + +## 本节目录 + +- [第七题 - ld 脚本 - 指定 text 起始地址](#第七题---ld-脚本---指定-text-起始地址) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个 ld 文件(命名为 `text_adress_.ld`),设置内存区域起始地址为 0x8000000,长度为 0x2000,指定 .text 的起始地址为 0x1000。 + +## 输入 + +```shell +# 将会在测试脚本中尝试: +# 打印 `text` 地址,匹配是否为 0x1000 +$(readelf -l "$EXECUTABLE" | grep "Entry point" | head -n 1 | awk '{print $3}') +``` + +## 输出 + +运行测试时,检测到"Test passed."字样。 + +## 已有代码介绍 + +```c +MEMORY { + /* >>> 在这里定义内存区域,设置内存区域起始地址为 `0x8000000`,长度为 `0x2000`。 */ + + /* <<< */ +} + + +SECTIONS { + /* >>> 在这里指定 `.text` 的起始地址为 `0x1000` */ + .text : { + *(.text) + /* 其他段的定义 */ + } + /* <<< */ + + /* 定义只读数据段,包含常量数据 */ + .rodata : { + *(.rodata) + } > RAM + + /* 定义BSS段,包含未初始化的全局变量和静态变量 */ + .bss : { + *(.bss) + } > RAM + + /* 定义程序头表 */ + . = ALIGN(4); + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .dynamic : { *(.dynamic) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame : { *(.eh_frame) } + . = ALIGN(8); + . = . + SIZEOF_HEADERS; +} + +``` + +## 提示 + +你需要了解 ld 脚本的结构,之后,在 Memory 和 SECTIONS 内,按照中文注释完成对应要求。有和上一题相似的地方的话,可以考虑复用。 + +## 注意事项 + +**虽然测试脚本内没有对 Memory 内容进行测试,但仍然要进行实现!** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-09.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-09.md new file mode 100644 index 0000000000000000000000000000000000000000..803c8c29f20a1d3f6ca5a0d53d4cea1b1abd3adc --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-09.md @@ -0,0 +1,88 @@ +# 第八题 - ld 脚本 - 指定自定义 symbol + +## 本节目录 + +- [第八题 - ld 脚本 - 指定自定义 symbol](#第八题---ld-脚本---指定自定义-symbol) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个 ld 文件(命名为 new_symbol.ld),设置内存区域起始地址为 0x8000000,长度为 0x2000,指定 .text 包含一个自定义的 symbol 名为 my_custom_symbol,其地址为 0x1111。 + +## 输入 + +```shell +# 将会在测试脚本中尝试: +# 打印 `my_custom_symbol` 的地址,匹配是否为 0000000008001111 +# 起始地址 + symbol 偏移地址 应该是 0000000008001111 +$(nm "$EXECUTABLE" | grep ' my_custom_symbol$' | awk '{print $1}') +``` + +## 输出 + +运行测试时,检测到"Test passed."字样。 + +## 已有代码介绍 + +```c +MEMORY { + /* >>> 在这里定义内存区域,设置内存区域起始地址为 `0x8000000`,长度为 `0x2000`。 */ + + /* <<< */ +} + + +SECTIONS { + .text : { + *(.text) + /* 其他段的定义 */ + + /* >>> 在这里添加 my_custom_symbol,地址设置 0x1111 。*/ + + /* <<< */ + } + /* <<< */ + + + /* 定义只读数据段,包含常量数据 */ + .rodata : { + *(.rodata) + } > RAM + + /* 定义BSS段,包含未初始化的全局变量和静态变量 */ + .bss : { + *(.bss) + } > RAM + + /* 定义程序头表 */ + . = ALIGN(4); + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .dynamic : { *(.dynamic) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame : { *(.eh_frame) } + . = ALIGN(8); + . = . + SIZEOF_HEADERS; +} + +``` + +## 提示 + +你需要了解 ld 脚本的结构,之后,在 Memory 和 SECTIONS 内,按照中文注释完成对应要求。有和上一题相似的地方的话,可以考虑复用。 + +## 注意事项 + +**无特殊注意事项** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch6-10.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-10.md new file mode 100644 index 0000000000000000000000000000000000000000..01eda51939c2899c92026f221cc9017311b221b2 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch6-10.md @@ -0,0 +1,87 @@ +# 第九题 - ld 脚本 - 指定自定义 section + +## 本节目录 + +- [第九题 - ld 脚本 - 指定自定义 section](#第九题---ld-脚本---指定自定义-section) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个 ld 文件(命名为 `custom_section_.ld`),设置内存区域起始地址为 0x8000000,长度为 0x2000,指定 setions 包含一个自定义的 section 名为 `my_custom_section`,并在 `my_custom_section` 内放置 `my_custom_data`,其地址为 0x1234。 + +## 输入 + +```shell +# 将会在测试脚本中尝试: +# 打印 `my_custom_data` 的地址,匹配是否为 0000000000001234 +$(readelf -s "$EXECUTABLE" | grep ' my_custom_data$' | awk '{print $2}') +``` + +## 输出 + +运行测试时,检测到"Test passed."字样。 + +## 已有代码介绍 + +```c +MEMORY { + /* >>> 在这里定义内存区域,设置内存区域起始地址为 `0x8000000`,长度为 `0x2000`。 */ + + /* <<< */ +} + + +SECTIONS { + .text : { + *(.text) + /* 其他段的定义 */ + } + + + /* 定义只读数据段,包含常量数据 */ + .rodata : { + *(.rodata) + } > RAM + + /* 定义BSS段,包含未初始化的全局变量和静态变量 */ + .bss : { + *(.bss) + } > RAM + + /* >>> 在这里添加 my_custom_sectoin,并在其内放置 `my_custom_data`,地址设置 0x1234 。*/ + + /* <<< */ + + + /* 定义程序头表 */ + . = ALIGN(4); + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .dynamic : { *(.dynamic) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .eh_frame : { *(.eh_frame) } + . = ALIGN(8); + . = . + SIZEOF_HEADERS; +} + +``` + +## 提示 + +你需要了解 ld 脚本的结构,之后,在 Memory 和 SECTIONS 内,按照中文注释完成对应要求。有和上一题相似的地方的话,可以考虑复用。 + +## 注意事项 + +**无特殊注意事项** diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-00.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-00.md new file mode 100644 index 0000000000000000000000000000000000000000..8b5106376880eb0c2975860facb8ec2ebfa549a9 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-00.md @@ -0,0 +1,49 @@ +# 第七章 - 题目分析[10 - 20] + +## 本章概要 + +本章主要进行的习题知识点范围如下: + +- 操作系统里常用的数据结构 - 队列 +- 内核模块 +- Risc-V 汇编 + +对于数据结构,我们会做两道题,分别为: + +1. 合并两个队列 +2. 按组反转一个队列 + +在数据结构部分,需要大家能够按照指定思想,复现出对应的算法。 + +对于内核模块,我们会做六道题,分别为: + +1. 编写一个内核模块打印 hello world +2. 编写一个内核模块实现阶乘计算 +3. 编写一个内核模块实现字符串反转 +4. 编写一个内核模块实现平均数计算 +5. 编写一个内核模块实现线性查找 +6. 编写一个内核模块延时打印字符串 + +在内核模块,主要需要大家掌握内核模块的编程方法,并实现一些基础算法。 + +对于 Risc-V 汇编,我们会做三道题,分别为: + +1. 使用 RISC-V 内联汇编实现条件返回 +2. 使用 RISC-V 内联汇编实现最大公因数求解 +3. 使用 RISC-V 内联汇编实现数组元素查找 + +在 Risc-V 汇编部分,主要需要大家掌握 RISC-V 内联汇编的基本用法,推荐大家参考 Risc-V 汇编手册。 + +## 本章目录 + +- [第十题 - 数据结构(队列) - 合并两个队列](./ch7-01.md) +- [第十一题 - 数据结构(队列) - 按组反转一个队列](./ch7-02.md) +- [第十二题 - 内核模块 - 打印 hello world](./ch7-03.md) +- [第十三题 - 内核模块 - 阶乘计算](./ch7-04.md) +- [第十四题 - 内核模块 - 字符串反转](./ch7-05.md) +- [第十五题 - 内核模块 - 平均数计算](./ch7-06.md) +- [第十六题 - 内核模块 - 线性查找](./ch7-07.md) +- [第十七题 - 内核模块 - 延时打印字符串](./ch7-08.md) +- [第十八题 - Risc-V 汇编 - 条件返回](./ch7-09.md) +- [第十九题 - Risc-V 汇编 - 最大公因数求解](./ch7-10.md) +- [第二十题 - Risc-V 汇编 - 数组元素查找](./ch7-11.md) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-01.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-01.md new file mode 100644 index 0000000000000000000000000000000000000000..8d0c5a91328549b2fe55fe5339a4e85ef0d123a1 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-01.md @@ -0,0 +1,61 @@ +# 第十题 - 数据结构(队列) - 合并两个队列 + +## 本节目录 + +- [第十题 - 数据结构(队列) - 合并两个队列](#第十题---数据结构队列---合并两个队列) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +将两个任务队列(例如运行队列)合并在一起,这在多核处理器中尤为常见,当某个处理器的任务队列需要与另一个处理器的任务队列合并时,可以使用这一操作。 + +你需要实现一个函数 `merge_task_queues`,该函数用于合并两个已经排序的任务队列。每个任务队列由 TaskNode 结构体表示,任务队列按任务 ID (task_id) 从小到大排序。合并后的任务队列也需要保持有序。 + +## 示例 + +假设我们有两个任务队列: + +队列 A:1 -> 3 -> 5 +队列 B:2 -> 4 -> 6 +调用 `merge_task_queues(A, B)` 后,返回的新队列应该是: + +合并后的队列:1 -> 2 -> 3 -> 4 -> 5 -> 6 + +## 输入 + +一组任务 id 组成的的队列,由 `,` 分割,比如 1,3,5。 +另一组任务 id 组成的的队列,由 `,` 分割,比如 2,4,6。 + +## 输出 + +打印合并后的任务队列: + +Merged Queue: 1 2 3 4 5 6 + +## 已有代码介绍 + +任务队列的节点由以下结构体表示: + +```c +typedef struct TaskNode { + int task_id; + struct TaskNode *next; +} TaskNode; +``` + +## 提示 + +- 初始的任务队列已经安装升序排序完毕,合并后的队列也要以升序排列。 +- 在原地操作即可,不需要申请新的内存空间。 +- 同时遍历两个队列,不断选出最小的任务 id,然后将其插入到新队列的末尾。 + +## 注意事项 + +记得判断退出了 while 循环后,是否仍然有非空队列,如果有,则应该进一步合并。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-02.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-02.md new file mode 100644 index 0000000000000000000000000000000000000000..aba79c550cdcf681d97121873d111da25989b402 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-02.md @@ -0,0 +1,64 @@ +# 第十一题 - 数据结构(队列) - 按组反转一个队列 + +## 本节目录 + +- [第十一题 - 数据结构(队列) - 按组反转一个队列](#第十一题---数据结构队列---按组反转一个队列) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +在操作系统的任务调度优先级处理过程中,当某些任务队列需要按相反的顺序处理时,可以使用任务队列的反转操作来实现。 + +给定一个任务队列,按每 𝑘 个节点一组进行翻转,返回修改后的队列。这里的 k 是一个正整数,并且小于或等于队列的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。不能只是单纯地改变节点内部的值,而是需要实际进行节点交换。 + +你需要实现一个函数 `reverse_task_queue_by_group`,该函数用于按组(k)反转一个任务队列。 + +## 示例 + +假设我们有一个任务队列: + +原始队列:1 -> 2 -> 3 -> 4 -> 5 + +调用 `reverse_task_queue_by_group(head,2)` 后,返回的新队列应该是: + +反转后的队列:2 -> 1 -> 4 -> 3 -> 5 + +## 输入 + +一组任务 id 组成的队列,由 `,` 分割,比如 1,2,3,4,5 + +## 输出 + +打印反转后的任务队列 + +Reversed queue: 2 1 4 3 5 + +## 已有代码介绍 + +任务队列的节点由以下结构体表示: + +```c +typedef struct TaskNode { + int task_id; + struct TaskNode *next; +} TaskNode; +``` + +## 提示 + +- 类似于“滑动窗口”,多维护一个指针,用于记录窗口的开始和末尾。 +- 在原地操作即可,不需要申请新的内存空间。 +- 先读懂框架,再做题! + +## 注意事项 + +- 确保在原地进行操作 +- 确保对最后一部分的不足 k 的子队列的处理 +- 确保不是值操作而是对指针操作 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-03.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-03.md new file mode 100644 index 0000000000000000000000000000000000000000..548ccab34cede25b0d65df1ba08b337e93b1f503 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-03.md @@ -0,0 +1,40 @@ +# 第十二题 - 内核模块 - 打印 hello world + +## 本节目录 + +- [第十二题 - 内核模块 - 打印 hello world](#第十二题---内核模块---打印-hello-world) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个内核模块,打印 `hello world!`。 + +## 输入 + +无 + +## 输出 + +打印 `hello world!` + +## 已有代码介绍 + +模块初始化函数 hello_init 在模块加载时执行,需要正确输出`hello world!`。 + +模块退出函数 hello_exit 在模块卸载时执行,打印卸载信息。 + +## 提示 + +- 按照代码内的提示即可完成任务 + +## 注意事项 + +- 注意“输出语句”的使用! +- 注意要同时输出“KERNEL INFO”! +- (可以直接复制题目目录内的`README.md`最后处提供的输出代码) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-04.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-04.md new file mode 100644 index 0000000000000000000000000000000000000000..b6edd451cf8ad46313c50189092593ce89588f8b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-04.md @@ -0,0 +1,50 @@ +# 第十三题 - 内核模块 - 阶乘计算 + +## 本节目录 + +- [第十三题 - 内核模块 - 阶乘计算](#第十三题---内核模块---阶乘计算) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +写一个内核模块,实现计算给定整数的阶乘。 + +给定整数应通过一个名为 `factorial_input` 的 kernel parameter 传入。 + +## 示例 + +输入:5 输出:120 + +$5 \times 4 \times 3 \times 2 \times 1 = 120$ + +## 输入 + +一个整数 n(n >= 0) + +## 输出 + +整数 n 的阶乘值 + +## 已有代码介绍 + +模块初始化函数 factorial_init 在模块加载时执行,需要正确计算并输出阶乘。 + +模块退出函数 factorial_exit 在模块卸载时执行,打印卸载信息。 + +## 提示 + +- 可以使用递归 +- 也可以使用 for 循环 + +## 注意事项 + +- 注意“输出语句”的使用! +- 注意要同时输出“KERNEL INFO”! +- (可以直接复制题目目录内的`README.md`最后处提供的输出代码) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-05.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-05.md new file mode 100644 index 0000000000000000000000000000000000000000..467f273c7683c011eabf3e71003b2961a6b96403 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-05.md @@ -0,0 +1,50 @@ +# 第十四题 - 内核模块 - 字符串反转 + +## 本节目录 + +- [第十四题 - 内核模块 - 字符串反转](#第十四题---内核模块---字符串反转) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个内核模块,接收一个字符串作为输入,将其反转后输出。 + +给定字符串应通过一个名为 `reverse_string_input` 的 kernel parameter 传入。 + +## 示例 + +输入:hello 输出:olleh + +## 输入 + +一个字符串(长度 >0)。 + +## 输出 + +反转后的字符串。 + +## 已有代码介绍 + +模块初始化函数 reverse_init 在模块加载时执行,需要正确翻转给定的字符串并返回。 + +模块退出函数 reverse_exit 在模块卸载时执行,打印卸载信息。 + +## 提示 + +- 在原地执行字符串翻转操作 +- 注意字符串的长度大于 0 + +## 注意事项 + +- 注意字符串的长度要大于 0! +- 要在原地操作字符串 +- 注意“输出语句”的使用! +- 注意要同时输出“KERNEL INFO”! +- (可以直接复制题目目录内的`README.md`最后处提供的输出代码) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-06.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-06.md new file mode 100644 index 0000000000000000000000000000000000000000..9000dc679b0e58802ddfdf103ed5ddfce61b30b2 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-06.md @@ -0,0 +1,49 @@ +# 第十五题 - 内核模块 - 平均数计算 + +## 本节目录 + +- [第十五题 - 内核模块 - 平均数计算](#第十五题---内核模块---平均数计算) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个内核模块, + +给定的数据应通过一个名为 `average_input` 的 kernel parameter 传入,使用 `,` 分割。 + +## 示例 + +输入:1,2,3,4,5 输出:3 + +## 输入 + +一组数字,使用 `,` 分割。 + +## 输出 + +一个平均数。 + +## 已有代码介绍 + +模块初始化函数 average_init 在模块加载时执行,需要正确计算给定数组的平均数。 + +模块退出函数 average_exit 在模块卸载时执行,打印卸载信息。 + +## 提示 + +- 要理解 `module_param_array` 是如何工作的 +- 在中文注释处完善逻辑 + +## 注意事项 + +- 注意处理,使得`average_input_size < MAX_ARRAY_SIZE` +- 注意“输出语句”的使用! +- 注意要同时输出“KERNEL INFO”! +- (可以直接复制题目目录内的`README.md`最后处提供的输出代码) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-07.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-07.md new file mode 100644 index 0000000000000000000000000000000000000000..90b2ba21f19d70827fe3b82819208ab412c774cf --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-07.md @@ -0,0 +1,53 @@ +# 第十六题 - 内核模块 - 线性查找 + +## 本节目录 + +- [第十六题 - 内核模块 - 线性查找](#第十六题---内核模块---线性查找) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个内核模块, + +给定的数据应通过一个名为 `ls_array_input` 的 kernel parameter 传入,使用 `,` 分割。 + +查找的目标应通过一个名为 `ls_target` 的 kernel parameter 传入。 + +## 示例 + +输入: 1. 3,6,9,12,15 2. 12 输出:3 + +## 输入 + +两个参数: + +- 一组数字,使用 `,` 分割。 +- 一个数字,必须包含在上述组中。 + +## 输出 + +目标在给定数组中的下标。 + +## 已有代码介绍 + +模块初始化函数 search_init 在模块加载时执行,需要线性查找对应元素的下标,如果找不到,为了程序的鲁棒性,可以返回`-1`。 + +模块退出函数 search_exit 在模块卸载时执行,打印卸载信息。 + +## 提示 + +- 可以使用`-1`作为找不到下标时的返回值 +- 线性查找,遍历即可 + +## 注意事项 + +- 注意“输出语句”的使用! +- 注意要同时输出“KERNEL INFO”! +- (可以直接复制题目目录内的`README.md`最后处提供的输出代码) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-08.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-08.md new file mode 100644 index 0000000000000000000000000000000000000000..3a8b49acd86790e10b6a4806d62976634aec779e --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-08.md @@ -0,0 +1,46 @@ +# 第十七题 - 内核模块 - 延时打印字符串 + +## 本节目录 + +- [第十七题 - 内核模块 - 延时打印字符串](#第十七题---内核模块---延时打印字符串) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个内核模块,延时打印一串字符 Delay 2s! + +## 示例 + +2s 后输出 `hello world!` + +## 输入 + +无 + +## 输出 + +延时 2s 后打印 Delay 2s!。 + +## 已有代码介绍 + +模块初始化函数 delay_init 在模块加载时执行,之后要先延时,再打印。 + +模块退出函数 delay_exit 在模块卸载时执行,打印卸载信息。 + +## 提示 + +- 查找资料时,可以关注“Linux 系统模块” +- 使用`msleep (2000);`进行延时 + +## 注意事项 + +- 注意“输出语句”的使用! +- 注意要同时输出“KERNEL INFO”! +- (可以直接复制题目目录内的`README.md`最后处提供的输出代码) diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-09.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-09.md new file mode 100644 index 0000000000000000000000000000000000000000..f175b7fe83de8f696f96f9d1401fbc9d1253d265 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-09.md @@ -0,0 +1,45 @@ +# 第十八题 - Risc-V 汇编 - 条件返回 + +## 本节目录 + +- [第十八题 - Risc-V 汇编 - 条件返回](#第十八题---risc-v-汇编---条件返回) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个 C 函数,名为 conditional_jump。该函数接受两个整数参数,如果第一个参数大于等于第二个参数,则返回第一个参数的值,否则返回第二个参数的值。 + +## 示例 + +输入:a = 5, b = 3,输出:Result: 5 + +## 输入 + +两个整数 a,b,以空格分隔,表示函数的两个参数。输入保证在 int 类型范围内。 + +## 输出 + +根据条件跳转逻辑,返回格式 Result: num,num 就是要返回的整数值 + +## 已有代码介绍 + +通过 main 调用,由内联汇编代码写的函数。 + +## 提示 + +- 使用 RISC-V 汇编指令替换 find_max 函数中的 PLACEHOLDER。 +- "bge" 指令用于在比较操作中,当大于或等于时跳转。 +- 可以使用“%1, %2”取得函数传入的第一个和第二个参数 + +## 注意事项 + +- 保持输出格式为 Result: num。 +- 使用内联汇编! +- 注意汇编的寄存器参数和直接参数的差别! diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-10.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-10.md new file mode 100644 index 0000000000000000000000000000000000000000..2742db00937d6d5352b4cae7693507ee485aa60b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-10.md @@ -0,0 +1,45 @@ +# 第十九题 - Risc-V 汇编 - 最大公因数求解 + +## 本节目录 + +- [第十九题 - Risc-V 汇编 - 最大公因数求解](#第十九题---risc-v-汇编---最大公因数求解) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +编写一个 C 语言函数 gcd,接受两个整数 a 和 b,利用辗转相减法计算它们的最大公因数,并返回结果。然后编写一个程序调用该函数,并输出结果 + +## 示例 + +输入 12 18,输出 6 + +## 输入 + +两个整数 a 和 b 作为程序参数,以空格分隔 + +## 输出 + +输出样式 GCD of a and b is num, num 是一个整数,表示输入整数 a 和 b 的最大公因数。 + +## 已有代码介绍 + +通过 main 调用,由内联汇编代码写的函数。 + +## 提示 + +- 使用 RISC-V 汇编指令替换 gcd 函数中的 PLACEHOLDER。 +- "beq" 指令用于在比较操作中,当等于时跳转。 +- "blt" 指令用于在比较操作中,当小于时跳转。 + +## 注意事项 + +- 保持输出格式为 GCD of a and b is num。 +- 使用内联汇编! +- 注意汇编的寄存器参数和直接参数的差别! diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/ch7-11.md b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-11.md new file mode 100644 index 0000000000000000000000000000000000000000..26c583274c300695562f9c58c14b1f293377cc02 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/ch7-11.md @@ -0,0 +1,64 @@ +# 第二十题 - Risc-V 汇编 - 数组元素查找 + +## 本节目录 + +- [第二十题 - Risc-V 汇编 - 数组元素查找](#第二十题---risc-v-汇编---数组元素查找) + - [本节目录](#本节目录) + - [题目要求](#题目要求) + - [示例](#示例) + - [输入](#输入) + - [输出](#输出) + - [已有代码介绍](#已有代码介绍) + - [提示](#提示) + - [注意事项](#注意事项) + +## 题目要求 + +设计一个 C 语言函数 find_value,接受一个整数数组 arr、数组的长度 n、一个目标值 target,以及一个起始索引 start_index,返回目标值在数组中从指定起始索引开始的第一次出现的索引。如果目标值不在数组中或起始索引超出数组范围,则返回 -1。然后编写一个程序调用该函数,并输出结果。 + +## 示例 + +输入: + +``` +1,2,3,4,5 +5 +3 +1 +``` + +输出: + +``` +Index of target from start index: 2 +``` + +## 输入 + +共需要 4 个参数: + +1. 一个整数数组 arr,以逗号分割,比如 1,2,3,4,5; +2. 一个整数 n; +3. 一个目标 target; +4. 一个索引 start_index; + 输入保证在 int 类型范围内。 + +## 输出 + +返回字符串格式 Index of target from start index: INDEX,INDEX 表示 target 在指定数组范围内的索引,若没有该元素,返回字符串 Target not found or start index out of range.。` + +## 已有代码介绍 + +通过 main 调用,由内联汇编代码写的函数。 + +## 提示 + +- 使用 RISC-V 汇编指令替换 gcd 函数中的 PLACEHOLDER。 +- a \* 4 <=> a << 2 +- "slli" 指令用于将某个数左移指定位后,存入指定的寄存器,具体参数请自行查阅手册 + +## 注意事项 + +- 保持输出格式为 Index of target from start index: INDEX。 +- 使用内联汇编! +- 注意汇编的寄存器参数和直接参数的差别! diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/foreword.md b/mdbook/eulixos-camp-book-stage1-docs/src/foreword.md new file mode 100644 index 0000000000000000000000000000000000000000..cf34b1bff2a416625fc5b8b94ed76b3f29959638 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/foreword.md @@ -0,0 +1,24 @@ +# 前言 + +**本文档旨在为读者提供一个关于操作系统深入理解的指南,并在基础阶段提供必要的知识和实践任务。** + +## 本书大纲 + +本书从操作系统的基本概念开始,逐步深入到其内部结构和实现机制。全书分为多个章节,每个章节都围绕一个核心主题进行展开,旨在帮助读者系统地掌握操作系统的相关知识。 + +- **第一章**:介绍了操作系统的基本概念,包括什么是操作系统、为什么要有操作系统,以及 RISC-V 作为操作系统未来发展方向的展望。 +- **第二章**:详细讨论了操作系统的结构,包括宏内核、微内核、外核+libOS 以及多内核/复内核等不同的架构模式,并分析了它们各自的优缺点。 +- **第三章**:讲解了中断、异常和系统调用的概念及其在操作系统中的作用,为后续章节中讨论系统资源管理打下基础。 +- **第四章**和**第五章**:关注于系统初始化过程,从计算机的启动、BIOS、EFI/UEFI,到 Grub、Init 与 RunLevel,再到 Risc-V 的初始化流程,为读者展示了从硬件上电到操作系统运行的整个过程。 +- **第六章**和**第七章**:提供了丰富的实践任务,包括 Makefile 的编写、链接脚本(ld script)的定制、数据结构的操作、内核模块的编程以及 Risc-V 汇编的实践等,旨在通过实践加深读者对操作系统理论知识的理解。 +- **附录 A**:汇总了可供参考的各种资料,为读者提供了进一步学习和研究的方向。 + +## 读者的任务 + +在基础阶段,读者需要了解操作系统的基本概念、结构以及初始化过程,掌握中断、异常和系统调用的基本概念和作用。同时,读者还需要完成一些实践任务,如编写 Makefile、定制链接脚本、编写内核模块和 Risc-V 汇编代码等,通过实践加深对操作系统理论知识的理解和应用。 + +我们鼓励读者在阅读本书的同时,积极思考并尝试解决遇到的问题,通过不断的学习和实践,逐步提高自己的能力。希望本书能够成为读者在傲来操作系统基础阶段学习道路上的良师益友。 + +## 反馈与贡献 + +如果您有任何意见、建议或疑问,欢迎随时通过 GitHub 上的 [issues](https://github.com/kunyuanxu-star/eulixos-camp-book-stage1/issues) 页面进行反馈。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/introduce.md b/mdbook/eulixos-camp-book-stage1-docs/src/introduce.md new file mode 100644 index 0000000000000000000000000000000000000000..cfa5b8601069d3ce78158df27d726209d628af64 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage1-docs/src/introduce.md @@ -0,0 +1,9 @@ +# 介绍 + +## 傲来操作系统 + +傲来操作系统(EulixOS)是由中科院软件所 / 中科南京软件技术研究院团队基于 openEuler 打造的操作系统发行版,其开发目标是集成软件所的最新科研成果,面向开源爱好者、科研人员和学生,为在线服务、高性能计算、AI 计算等使用场景提供一款安全、易用的操作系统。 + +## 文档概述 + +本文档基于傲来操作系统训练营第一期的基础阶段课程内容与作业内容,为您提供一份较为简单易用的学习指南。但请注意,您可以将本文档看作您在傲来操作系统训练营基础阶段的一份**知识地图**,而不是一份完整详细的教程。我们会简要介绍上课讲的知识点和完成作业所需要掌握的知识点,但不会过于详细深入。如果有兴趣,可以自行去深入了解对应方向。 diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI.png b/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI.png new file mode 100644 index 0000000000000000000000000000000000000000..72e3b7ee509cf61f90b699bea381cefa21aa6319 Binary files /dev/null and b/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI.png differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI\345\217\221\345\261\225.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI\345\217\221\345\261\225.png" new file mode 100644 index 0000000000000000000000000000000000000000..2e9c538068faf8b277495dcaac64f9b16908e875 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI\345\217\221\345\261\225.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI\347\273\223\346\236\204.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI\347\273\223\346\236\204.png" new file mode 100644 index 0000000000000000000000000000000000000000..d163d9a45831314d0d09567bc35647be2a25d546 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/EFI\347\273\223\346\236\204.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/Linux\345\220\257\345\212\250.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/Linux\345\220\257\345\212\250.png" new file mode 100644 index 0000000000000000000000000000000000000000..38ead41fee8cab1378c0cdac3e4be4150a776703 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/Linux\345\220\257\345\212\250.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/OS\344\275\234\347\224\2501.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/OS\344\275\234\347\224\2501.png" new file mode 100644 index 0000000000000000000000000000000000000000..4a4c0f4ef262c3b46d2e07aec0c74c4295c2015d Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/OS\344\275\234\347\224\2501.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/OS\344\275\234\347\224\2502.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/OS\344\275\234\347\224\2502.png" new file mode 100644 index 0000000000000000000000000000000000000000..eb3f8da1e51c188893e1610a2dae26b83eafbd32 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/OS\344\275\234\347\224\2502.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\345\217\202\350\200\203\346\226\207\347\214\256.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\345\217\202\350\200\203\346\226\207\347\214\256.png" new file mode 100644 index 0000000000000000000000000000000000000000..6e2ce35a091e8fd520f7da1a8bfaa21a84bdf4c4 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\345\217\202\350\200\203\346\226\207\347\214\256.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\345\272\224\347\224\250.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\345\272\224\347\224\250.png" new file mode 100644 index 0000000000000000000000000000000000000000..e33da0ccfa1ef9be63ead86117dfc1639c529e92 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\345\272\224\347\224\250.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\347\224\237\346\200\201\346\240\210.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\347\224\237\346\200\201\346\240\210.png" new file mode 100644 index 0000000000000000000000000000000000000000..89af389bec73ac30f37c66e4060423593410e81c Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/RV\347\224\237\346\200\201\346\240\210.png" differ diff --git a/mdbook/eulixos-camp-book-stage1-docs/src/resource/SoC.png b/mdbook/eulixos-camp-book-stage1-docs/src/resource/SoC.png new file mode 100644 index 0000000000000000000000000000000000000000..acb8e73997a7b4751645ad890e2d4213671d313d Binary files /dev/null and b/mdbook/eulixos-camp-book-stage1-docs/src/resource/SoC.png differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/trap\345\257\204\345\255\230\345\231\250.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/trap\345\257\204\345\255\230\345\231\250.png" new file mode 100644 index 0000000000000000000000000000000000000000..9f4e17a41a031366b23cdb55039b11d89507b176 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/trap\345\257\204\345\255\230\345\231\250.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/trap\346\265\201\347\250\213.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/trap\346\265\201\347\250\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..73821dda44faa2da1dd3da52e9cf5ebeb6608bb9 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/trap\346\265\201\347\250\213.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\344\270\255\346\226\255\345\274\202\345\270\270.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\344\270\255\346\226\255\345\274\202\345\270\270.png" new file mode 100644 index 0000000000000000000000000000000000000000..83746cb848c03b1faacf69192dded11af883bed9 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\344\270\255\346\226\255\345\274\202\345\270\270.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\220\257\345\212\250\346\265\201\347\250\213.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\220\257\345\212\250\346\265\201\347\250\213.png" new file mode 100644 index 0000000000000000000000000000000000000000..e4b9be5710296860338f2668c49f7f2a517893a9 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\220\257\345\212\250\346\265\201\347\250\213.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\226\346\240\270.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\226\346\240\270.png" new file mode 100644 index 0000000000000000000000000000000000000000..03d62f5d5f4e175499795e1afe54c613fbfe2937 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\226\346\240\270.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\226\346\240\270\350\231\232\346\213\237\346\234\272.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\226\346\240\270\350\231\232\346\213\237\346\234\272.png" new file mode 100644 index 0000000000000000000000000000000000000000..bb6e80b56cfc562262369f4699b42da95a2b335e Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\226\346\240\270\350\231\232\346\213\237\346\234\272.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\232\346\240\270\344\276\213\345\255\220.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\232\346\240\270\344\276\213\345\255\220.png" new file mode 100644 index 0000000000000000000000000000000000000000..ce3c787287722e666a9d79f8eaec561bf7f52abe Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\244\232\346\240\270\344\276\213\345\255\220.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270.png" new file mode 100644 index 0000000000000000000000000000000000000000..922ecdeb4ea266f6dc0a9410ec135fe0365d93bd Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270\345\276\256\345\206\205\346\240\270\345\257\271\346\257\224.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270\345\276\256\345\206\205\346\240\270\345\257\271\346\257\224.png" new file mode 100644 index 0000000000000000000000000000000000000000..a8f0827d6c62c234267651e5ad3ac248e82c1b9c Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270\345\276\256\345\206\205\346\240\270\345\257\271\346\257\224.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270\345\276\256\345\206\205\346\240\270\346\226\207\344\273\266.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270\345\276\256\345\206\205\346\240\270\346\226\207\344\273\266.png" new file mode 100644 index 0000000000000000000000000000000000000000..c7ed9a21014d10b4a917ee6205072f33cdde28bf Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\256\217\345\206\205\346\240\270\345\276\256\345\206\205\346\240\270\346\226\207\344\273\266.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\276\256\345\206\205\346\240\270.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\276\256\345\206\205\346\240\270.png" new file mode 100644 index 0000000000000000000000000000000000000000..ca6f2f5ba1fda76e8c50601437b979c630ee0d62 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\276\256\345\206\205\346\240\270.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\276\256\345\206\205\346\240\270\346\200\247\350\203\275.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\276\256\345\206\205\346\240\270\346\200\247\350\203\275.png" new file mode 100644 index 0000000000000000000000000000000000000000..c4f9aae3c80ebaeab520b6e57d204517e5c1c5a7 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\345\276\256\345\206\205\346\240\270\346\200\247\350\203\275.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\214\207\344\273\244\351\233\206.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\214\207\344\273\244\351\233\206.png" new file mode 100644 index 0000000000000000000000000000000000000000..563d0bd991f966054577c58500c4fadf74ea47d2 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\214\207\344\273\244\351\233\206.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\223\215\344\275\234\347\263\273\347\273\237\346\230\257\344\273\200\344\271\210.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\223\215\344\275\234\347\263\273\347\273\237\346\230\257\344\273\200\344\271\210.png" new file mode 100644 index 0000000000000000000000000000000000000000..e7fec23891b98a4e3e980be4b15c0416eeef7e19 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\223\215\344\275\234\347\263\273\347\273\237\346\230\257\344\273\200\344\271\210.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\223\215\344\275\234\347\263\273\347\273\237\347\232\204\345\210\206\345\214\226.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\223\215\344\275\234\347\263\273\347\273\237\347\232\204\345\210\206\345\214\226.png" new file mode 100644 index 0000000000000000000000000000000000000000..7feab75aaa6274935d1a0f613b5391b17370cb07 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\223\215\344\275\234\347\263\273\347\273\237\347\232\204\345\210\206\345\214\226.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\236\266\346\236\204\345\257\271\346\257\224.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\236\266\346\236\204\345\257\271\346\257\224.png" new file mode 100644 index 0000000000000000000000000000000000000000..031f4a074d593ca849b775d93b3c45dc3ccc24c5 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\236\266\346\236\204\345\257\271\346\257\224.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\236\266\346\236\204\346\274\224\350\277\233.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\236\266\346\236\204\346\274\224\350\277\233.png" new file mode 100644 index 0000000000000000000000000000000000000000..bc4f628a33508d4e45b7c34b722630c265bbb5d3 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\236\266\346\236\204\346\274\224\350\277\233.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\267\267\345\220\210\345\206\205\346\240\270.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\267\267\345\220\210\345\206\205\346\240\270.png" new file mode 100644 index 0000000000000000000000000000000000000000..468d4b485c45127267d5431d2677f18e8c3f6280 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\346\267\267\345\220\210\345\206\205\346\240\270.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\347\211\271\346\235\203\347\272\247.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\347\211\271\346\235\203\347\272\247.png" new file mode 100644 index 0000000000000000000000000000000000000000..8924f96ed9de3a3f366d30de14bebac0960b35ac Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\347\211\271\346\235\203\347\272\247.png" differ diff --git "a/mdbook/eulixos-camp-book-stage1-docs/src/resource/\350\275\256\350\257\242.png" "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\350\275\256\350\257\242.png" new file mode 100644 index 0000000000000000000000000000000000000000..5b1446f2d8555b5cf941f27445e1f26b77eed246 Binary files /dev/null and "b/mdbook/eulixos-camp-book-stage1-docs/src/resource/\350\275\256\350\257\242.png" differ diff --git a/mdbook/eulixos-camp-book-stage2-docs/.gitignore b/mdbook/eulixos-camp-book-stage2-docs/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7585238efedfc33acdd9494b0269951aaf3909ec --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/mdbook/eulixos-camp-book-stage2-docs/book.toml b/mdbook/eulixos-camp-book-stage2-docs/book.toml new file mode 100644 index 0000000000000000000000000000000000000000..00730bae641adf298d0c871781b4a4db1ed8e364 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/book.toml @@ -0,0 +1,6 @@ +[book] +authors = ["XUKUNYUAN"] +language = "Chinese" +multilingual = false +src = "src" +title = "第一期傲来操作系统训练营进阶阶段学习引导" diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/SUMMARY.md b/mdbook/eulixos-camp-book-stage2-docs/src/SUMMARY.md new file mode 100644 index 0000000000000000000000000000000000000000..20ec27f8b081c271ea3002e3a3ecd3da48168940 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/SUMMARY.md @@ -0,0 +1,53 @@ +# Summary + +- [前言](./preface.md) +- [第一章 虚拟内存与物理内存管理](./chapter-1.md) + - [虚拟内存与物理内存管理概念](./chapter-1_1.md) + - [RISC-V 虚拟内存管理机制](./chapter-1_2.md) +- [第二章 系统虚拟化与容器](./chapter-2.md) + - [虚拟化技术](./chapter-2_1.md) + - [容器技术](./chapter-2_2.md) +- [第三章 RISC-V向量拓展基础与优化基础C库实践](./chapter-3.md) + - [RISC-V 向量拓展 (V Extension) 基础](./chapter-3_1.md) + - [RISC-V SIMD 和 Unroll 优化方案](./chapter-3_2.md) +- [第四章 矩阵与卷积算法](./chapter-4.md) + - [矩阵与线性代数](./chapter-4_1.md) + - [卷积算法](./chapter-4_2.md) + - [矩阵转置](./chapter-4_3.md) +- [第五章 Linux内核模块](./chapter-5.md) + - [编写一个简单的内核模块](./chapter-5_1.md) + - [内核模块传参](./chapter-5_2.md) + - [创建字符设备](./chapter-5_3.md) + - [实现计时器](./chapter-5_4.md) + - [实现简单文件操作](./chapter-5_5.md) + - [Linux系统编程基础](./chapter-5_6.md) +- [第六章 RISC-V架构内联汇编](./chapter-6.md) + - [RISC-V架构简介](./chapter-6_1.md) + - [RISC-V基础指令集](./chapter-6_2.md) + - [控制结构的汇编表示方法](./chapter-6_3.md) +- [第七章 操作系统常用算法](./chapter-7.md) + - [FIFO 页面置换算法](./chapter-7_1.md) + - [LRU 页面置换算法](./chapter-7_2.md) + - [模拟时间片轮转调度算法](./chapter-7_3.md) +- [第八章 习题引导](./chapter-8.md) + - [矩阵相乘 - 矩阵](./exercises/22.md) + - [2D卷积操作 - 矩阵](./exercises/23.md) + - [矩阵原地转置 - 矩阵](./exercises/24.md) + - [包含0的行列进行矩阵置零 - 矩阵](./exercises/25.md) + - [查找矩阵中第K个最小的元素 - 矩阵](./exercises/26.md) + - [编写一个内核模块求最大值 - 内核模块](./exercises/36.md) + - [编写一个内核模块启动一个定时器 - 内核模块](./exercises/37.md) + - [编写一个内核模块创建一个虚拟字符设备 - 内核模块](./exercises/38.md) + - [编写一个内核模块实现一个简单的文件操作函数 - 内核模块](./exercises/39.md) + - [内联汇编实现斐波那契数列 - RISC-V基础指令](./exercises/43.md) + - [内联汇编实现整数数组求和 - RISC-V基础指令](./exercises/44.md) + - [内联汇编实现查找数组最大值 - RISC-V基础指令](./exercises/45.md) + - [内联汇编实现判断数组是否有序 - RISC-V基础指令](./exercises/46.md) + - [内联汇编实现数组目标元素个数 - RISC-V基础指令](./exercises/47.md) + - [模拟FIFO页面置换算法 - 操作系统](./exercises/50.md) + - [模拟LRU页面置换算法 - 操作系统](./exercises/51.md) + - [获取容器主机名 - 操作系统](./exercises/52.md) + - [简单虚拟地址到物理地址的转换 - 操作系统](./exercises/53.md) + - [模拟时间片轮转调度算法 - 操作系统](./exercises/54.md) + - [模拟虚拟内存限制 - 操作系统](./exercises/55.md) + diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1.md new file mode 100644 index 0000000000000000000000000000000000000000..08f5742dab9749b4657d4965985a9b11e63097db --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1.md @@ -0,0 +1,25 @@ +# 虚拟内存与物理内存管理 + +操作系统在 RISC-V 架构上的虚拟内存和物理内存管理涉及多个方面,包括地址转换、页表、内存保护等。 + +- [虚拟内存与物理内存管理概念](./chapter-1_1.md) +- [RISC-V 虚拟内存管理机制](./chapter-1_2.md) + +## 教程与学习资源 + +1. **RISC-V 规范**: + - [RISC-V Privileged Architecture Specification](https://riscv.org/specifications/privileged-isa/):详细描述了 RISC-V 的特权架构,包括虚拟内存管理。 + +2. **RISC-V 模拟器**: + - [QEMU](https://www.qemu.org/):一个开源的模拟器,可以模拟 RISC-V 平台,便于调试和学习操作系统的内存管理机制。 + - [Spike](https://github.com/riscv/riscv-isa-sim):RISC-V 官方提供的指令集模拟器。 + +3. **RISC-V 操作系统教程**: + - [xv6-riscv](https://github.com/mit-pdos/xv6-riscv):MIT 开源的一个教育用操作系统,移植到 RISC-V 架构上,可以帮助理解操作系统的内存管理。 + - [OSDI 教程](https://osdi.org/):提供了详细的操作系统设计与实现的课程资料,涵盖虚拟内存管理等内容。 + +4. **书籍与文档**: + - 《操作系统原理》:经典操作系统教材,涵盖虚拟内存管理的基础知识。 + - 《Computer Organization and Design RISC-V Edition》:详细介绍了 RISC-V 架构及其内存管理。 + +通过这些资源,你可以深入学习 RISC-V 架构下的虚拟内存和物理内存管理机制,并实践相关知识。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1_1.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1_1.md new file mode 100644 index 0000000000000000000000000000000000000000..398363a4e9f4dfbdce8a2f85c06ea0c94e85f6fb --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1_1.md @@ -0,0 +1,44 @@ +# 虚拟内存与物理内存管理概念 + +## 虚拟地址和物理地址 + +### 虚拟地址 +虚拟地址是指进程在运行时使用的地址。每个进程都认为自己独占整个内存空间,实际上,这些地址是通过操作系统的内存管理单元(MMU)映射到实际的物理内存地址。虚拟地址的引入使得程序的内存访问更加灵活和安全,避免了不同进程间的直接内存冲突。 + +### 物理地址 +物理地址是指实际的硬件内存地址,是计算机内存的真正位置。物理地址是由硬件直接访问的,不同进程的虚拟地址会通过页表映射到不同的物理地址上,从而实现内存的隔离和保护。 + +## 页表 (Page Table) + +### 页表的作用 +页表用于将虚拟地址转换为物理地址。每个进程都有一个或多个页表,用于维护其虚拟地址到物理地址的映射关系。操作系统通过页表来管理进程的内存空间,确保每个进程的虚拟地址空间正确映射到物理内存上。 + +### 页表条目 (PTE, Page Table Entry) +页表条目包含了虚拟页号 (VPN, Virtual Page Number) 和物理页号 (PPN, Physical Page Number) 的映射关系。每个页表条目还可能包含一些控制信息,如页的有效位、读/写权限、缓存策略等。 + +![](https://rcore-os.cn/rCore-Tutorial-Book-v3/_images/address-general.png) + +## 多级页表 (Multilevel Page Table) + +### 多级页表的必要性 +多级页表用于减少内存开销和页表大小。在大地址空间中,如果使用单级页表,页表会非常大且占用大量内存。多级页表通过将页表分为多个层次,每层次管理一部分地址空间,从而减少单个页表的大小。 + +### RISC-V 的多级页表 +RISC-V 通常使用两级或三级页表来管理大地址空间。每一级页表指向下一级页表或最终的物理页号。通过多级页表结构,RISC-V 可以高效地管理和映射大地址空间,同时减少页表占用的内存。 + +## 页大小 (Page Size) + +### 页大小的多样性 +RISC-V 支持多种页大小,如 4KB、2MB 和 1GB,以适应不同应用需求。小页适合细粒度的内存管理,大页适合大块数据的快速访问。不同页大小的灵活性使得系统可以根据具体应用的需求选择合适的页大小,提高内存管理效率。 + +## 地址转换缓冲区 (TLB, Translation Lookaside Buffer) + +### TLB 的作用 +TLB 是一种高速缓存,用于快速查找和转换虚拟地址到物理地址。由于页表存储在内存中,频繁的地址转换会带来显著的性能开销。TLB 缓存常用的页表条目,大大加快地址转换速度,减少访问内存的延迟。 + +### TLB 的工作原理 +TLB 存储着最近使用的虚拟地址到物理地址的映射。当CPU需要访问某个虚拟地址时,会首先查询TLB。如果找到对应的物理地址,则直接使用;如果没有找到,则需要查找页表,并将结果存入TLB,以备下次使用。TLB 的高命中率可以显著提高系统性能。 + +## 总结 + +通过虚拟地址、物理地址、页表、多级页表、页大小和TLB的协同工作,现代计算机系统能够高效地管理内存,为每个进程提供独立的虚拟地址空间,同时提高内存访问速度和系统整体性能。这些概念和机制共同构成了操作系统内存管理的核心,确保系统的稳定性、安全性和高效性。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1_2.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1_2.md new file mode 100644 index 0000000000000000000000000000000000000000..177b00467b20f88262204bfd85cd5c4e96f4db11 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-1_2.md @@ -0,0 +1,96 @@ +# RISC-V 虚拟内存管理机制 + +## 虚拟内存模式:Sv32、Sv39 和 Sv48 + +RISC-V 支持多种虚拟内存模式,以适应不同的地址空间需求: + +1. **Sv32 (32-bit 虚拟地址)** + - 适用于简单的系统和嵌入式应用。 + - 支持 32 位虚拟地址空间(4 GB)。 + - 使用两级页表,每级页表有 1024 个条目。 + - 每个页表条目大小为 4 字节。 + +2. **Sv39 (39-bit 虚拟地址)** + - 常用于中等规模的系统。 + - 支持 39 位虚拟地址空间(512 GB)。 + - 使用三级页表,每级页表有 512 个条目。 + - 每个页表条目大小为 8 字节。 + +3. **Sv48 (48-bit 虚拟地址)** + - 适用于大型系统和服务器应用。 + - 支持 48 位虚拟地址空间(256 TB)。 + - 使用四级页表,每级页表有 512 个条目。 + - 每个页表条目大小为 8 字节。 + +## 页表基地址寄存器 (SATP, Supervisor Address Translation and Protection) + +SATP 寄存器是 RISC-V 中用于管理页表的关键寄存器。它存储当前使用的页表基地址和地址转换模式。SATP 寄存器的结构如下: + +- **Mode**:地址转换模式,指示当前使用的虚拟内存模式(如 Sv32、Sv39 或 Sv48)。 +- **ASID**:地址空间标识符,用于多进程环境中区分不同进程的地址空间。 +- **PPN**:页表根页的物理页号,指示页表在物理内存中的位置。 + +操作系统通过设置 SATP 寄存器来控制虚拟地址到物理地址的映射。 + +## 页表结构与多级页表 + +不同虚拟内存模式的页表结构各不相同,但其核心思想是相似的。以 Sv39 为例,解释多级页表的工作原理: + +- **VPN[2]**:最高 9 位,索引第一级页表。 +- **VPN[1]**:中间 9 位,索引第二级页表。 +- **VPN[0]**:最低 9 位,索引第三级页表。 +- **Offset**:页内偏移,12 位。 + +虚拟地址在转换为物理地址时,按照上述三级页表逐级查找,最终得到物理页号 (PPN) 和页内偏移组合成物理地址。 + +## 页表条目 (PTE, Page Table Entry) + +每个页表条目包含以下信息: + +- **V**:有效位,指示该条目是否有效。 +- **R**:读权限位。 +- **W**:写权限位。 +- **X**:执行权限位。 +- **U**:用户态权限位。 +- **G**:全局位。 +- **A**:访问位,指示页是否被访问。 +- **D**:脏位,指示页是否被修改。 +- **PPN**:物理页号,指示虚拟页映射到的物理页。 + +这些位的组合决定了该页的属性和权限。 + +## 地址转换缓冲区 (TLB, Translation Lookaside Buffer) + +TLB 是一种高速缓存,用于快速查找和转换虚拟地址到物理地址。由于页表存储在内存中,频繁的地址转换会带来显著的性能开销。TLB 缓存常用的页表条目,大大加快地址转换速度,减少访问内存的延迟。 + +## 异常处理 + +当发生页错误 (Page Fault) 时,硬件会触发异常,操作系统捕获并处理这些异常。页错误通常由以下原因引起: + +- **缺页异常**:访问的虚拟页没有映射到物理内存。 +- **权限异常**:访问权限不符合要求(如读写权限不足)。 +- **非法地址**:访问的虚拟地址不在进程的地址空间范围内。 + +操作系统在处理页错误时,通常会执行以下步骤: + +1. **缺页异常处理**:分配新的物理页并更新页表条目,使虚拟页正确映射到物理页。 +2. **权限异常处理**:调整页表条目的权限位,确保进程的内存访问符合权限要求。 +3. **非法地址处理**:终止进程或采取其他安全措施,防止非法访问。 + +## 页表管理过程 + +1. **页表初始化**:操作系统在初始化时为每个进程创建页表,并设置 SATP 寄存器指向根页表。 +2. **页表查找**:当 CPU 需要转换虚拟地址时,首先查找 TLB。如果命中,直接得到物理地址;如果未命中,按照多级页表逐级查找,最终得到物理地址。 +3. **页表更新**:当进程需要分配或释放内存时,操作系统会更新相应的页表条目,确保虚拟地址正确映射到物理地址。 +4. **页表回收**:当进程结束时,操作系统会回收其页表,释放内存资源。 + +## 页大小和大页支持 + +不同大小的页可以在页表中混合使用,以提高内存管理的灵活性和效率。SV39 支持三种页大小: + +- **4KB**:最小页,用于细粒度内存管理。 +- **2MB**:中等大小页,用于中等大小数据的管理。 +- **1GB**:大页,用于大块数据的快速访问。 + +不同大小的页使系统能够根据具体应用需求选择合适的页大小,从而优化内存管理效率。 +- RISC-V 特权态下的页表管理机制通过虚拟内存模式、SATP 寄存器、多级页表结构、页表条目、TLB 和异常处理的协同工作,实现了高效的内存管理。操作系统通过这些机制为每个进程提供独立的虚拟地址空间,提高系统的安全性、稳定性和性能。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2.md new file mode 100644 index 0000000000000000000000000000000000000000..4189467fdb00d41388c0793a8045644674cbc482 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2.md @@ -0,0 +1,12 @@ +# RISC-V 操作系统的虚拟化与容器技术详解 + +虚拟化和容器技术是现代操作系统中至关重要的两种技术,它们为应用提供了隔离、安全性和资源管理。RISC-V 作为一种开放的指令集架构,支持这些技术的实现。以下是对 RISC-V 操作系统中虚拟化与容器技术的详细讲解。 + +## 本章内容 + +- [虚拟化技术](./chapter-2_1.md) +- [容器技术](./chapter-2_2.md) + +## 本章介绍 + +RISC-V 操作系统中的虚拟化与容器技术通过硬件和软件的协同工作,实现了高效的资源管理和应用隔离。虚拟化技术提供了运行多个操作系统的能力,而容器技术提供了轻量级的应用隔离和快速部署能力。这些技术的结合,使得 RISC-V 平台能够支持复杂的应用场景,满足多样化的计算需求。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2_1.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2_1.md new file mode 100644 index 0000000000000000000000000000000000000000..3d16a4e202cf1d777a7599b6d94e0304e04d2028 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2_1.md @@ -0,0 +1,23 @@ +# 虚拟化技术 + +虚拟化技术允许在一台物理机器上运行多个虚拟机 (VM),每个虚拟机都可以运行一个操作系统。虚拟化技术包括硬件虚拟化、全虚拟化、半虚拟化和硬件辅助虚拟化。 + +1. **硬件虚拟化 (Hardware Virtualization)** + - **定义**:直接在硬件上运行虚拟机管理程序 (Hypervisor)。 + - **RISC-V 支持**:RISC-V 架构提供了特权指令集 (Privilege Architecture),包括机器模式 (M-mode)、监督模式 (S-mode) 和用户模式 (U-mode),这为硬件虚拟化提供了基础。 + - **实现**:Hypervisor 在 M-mode 运行,管理多个在 S-mode 运行的虚拟机。每个虚拟机有自己的页表和虚拟内存空间。 + +2. **全虚拟化 (Full Virtualization)** + - **定义**:虚拟机运行未经修改的操作系统,Hypervisor 完全模拟底层硬件。 + - **RISC-V 支持**:通过硬件辅助虚拟化扩展(如 hypervisor extension),RISC-V 可以实现全虚拟化。 + - **实现**:Hypervisor 拦截和处理所有的特权指令和硬件访问请求,提供完整的虚拟硬件环境。 + +3. **半虚拟化 (Paravirtualization)** + - **定义**:虚拟机运行经过修改的操作系统,优化虚拟化性能。 + - **RISC-V 支持**:操作系统需要修改以适应 RISC-V 虚拟化接口。 + - **实现**:虚拟机中的操作系统直接调用 Hypervisor 提供的接口,减少特权指令的开销,提高性能。 + +4. **硬件辅助虚拟化 (Hardware-assisted Virtualization)** + - **定义**:通过硬件支持来简化和加速虚拟化操作。 + - **RISC-V 支持**:RISC-V 的 hypervisor extension 提供了硬件支持,如虚拟机内存管理单元 (VMMU) 和虚拟化特权指令。 + - **实现**:Hypervisor 可以高效地管理虚拟机的内存和设备访问,减少上下文切换的开销。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2_2.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2_2.md new file mode 100644 index 0000000000000000000000000000000000000000..b4394c035b7142c2f5d6c1b38795bf6dd0b7d6f6 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-2_2.md @@ -0,0 +1,31 @@ +# 容器技术 + +容器技术允许在同一操作系统内核上运行多个隔离的用户空间实例,每个实例被称为容器。容器技术包括操作系统级虚拟化、命名空间和控制组 (cgroups)。 + +1. **操作系统级虚拟化 (Operating System-level Virtualization)** + - **定义**:容器共享宿主操作系统内核,但运行在隔离的用户空间。 + - **RISC-V 支持**:RISC-V 操作系统(如 Linux)提供了支持容器的内核特性。 + - **实现**:通过容器引擎(如 Docker 或 Podman)在 RISC-V 上运行容器,每个容器有独立的文件系统、网络和进程空间。 + +2. **命名空间 (Namespaces)** + - **定义**:命名空间是 Linux 内核提供的隔离机制,用于隔离系统资源。 + - **RISC-V 支持**:RISC-V 架构上的 Linux 内核支持命名空间。 + - **实现**:不同的命名空间类型包括: + - **PID 命名空间**:隔离进程 ID。 + - **NET 命名空间**:隔离网络接口和路由表。 + - **MNT 命名空间**:隔离挂载点。 + - **UTS 命名空间**:隔离主机名和域名。 + - **IPC 命名空间**:隔离进程间通信。 + - **USER 命名空间**:隔离用户和用户组 ID。 + +3. **控制组 (cgroups)** + - **定义**:cgroups 是 Linux 内核提供的资源管理机制,用于限制和隔离资源使用。 + - **RISC-V 支持**:RISC-V 架构上的 Linux 内核支持 cgroups。 + - **实现**:cgroups 允许为容器设置资源限制,如 CPU、内存、I/O 和网络带宽,确保容器间的资源分配和使用互不干扰。 + +## 容器与虚拟机的对比 + +- **性能**:容器由于共享宿主内核,启动和运行效率高;虚拟机由于模拟硬件和运行独立内核,性能稍低。 +- **隔离**:虚拟机提供更强的隔离,因为每个虚拟机都有独立的内核;容器隔离相对较弱,但足以满足大多数应用场景。 +- **资源使用**:容器轻量级,占用更少的资源;虚拟机重量级,占用更多资源。 +- **兼容性**:虚拟机可以运行不同操作系统;容器通常运行与宿主相同的操作系统。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3.md new file mode 100644 index 0000000000000000000000000000000000000000..f49b22abf9e9d2a2c29c583c0e648e522225bacf --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3.md @@ -0,0 +1,47 @@ +# RISC-V向量拓展基础与优化基础C库实践 + +## 本章要求 + +1. 掌握RISC-V向量拓展基础知识 +2. 掌握RISC-V向量拓展优化基础知识 +3. 掌握C语言中向量化编程的基本方法 +4. 掌握C语言中向量化编程的优化方法 + +## 主要内容 + +- [RISC-V 向量拓展 (V Extension) 基础](./chapter-3_1.md) +- [RISC-V SIMD 和 Unroll 优化方案](./chapter-3_2.md) + +## 参考资料 + +1. **RISC-V指令集架构手册** - 这是理解RISC-V架构的基础,包括其向量扩展和SIMD能力。 + - 链接:[RISC-V ISA Manual](https://riscv.org/technical/specifications/) + +2. **RISC-V Vector Extension Specification** - 详细介绍了RISC-V的向量扩展,包括指令集和编程模型。 + - 链接:[RISC-V Vector Extension](https://github.com/riscv/riscv-v-spec) + +3. **GCC编译器文档** - 包括如何使用GCC编译器进行RISC-V程序的编译和优化。 + - 链接:[GCC RISC-V Options](https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/RISC-V-Options.html) + +4. **LLVM编译器文档** - LLVM项目也支持RISC-V,文档中包含了编译和优化RISC-V程序的相关信息。 + - 链接:[LLVM RISC-V Documentation](https://llvm.org/docs/RISCV.html) + +5. **RISC-V Software Development Tools** - 介绍RISC-V软件开发工具,包括编译器、调试器和性能分析工具。 + - 链接:[RISC-V Software Tools](https://riscv.org/software-tools/) + +6. **RISC-V International Conferences** - RISC-V相关的国际会议论文集,包含最新的研究成果和开发实践。 + - 链接:[RISC-V International Conferences](https://riscv.org/conference/) + +7. **RISC-V社区论坛和邮件列表** - 社区讨论和技术支持,可以获取关于RISC-V编程和优化的实用建议。 + - 链接:[RISC-V Community](https://riscv.org/community/) + +8. **性能分析工具文档** - 如gprof和perf,这些工具可以帮助你分析程序的性能并识别优化点。 + - gprof文档:通常包含在GCC的文档中。 + - perf文档:[perf Documentation](https://www.kernel.org/doc/html/latest/dev-tools/perf.html) + +9. **并行编程和优化书籍** - 这些书籍通常包含关于如何优化并行程序的通用策略,可以应用于RISC-V SIMD优化。 + - 例如:"Parallel Programming for Multicore and Cluster Systems" by Mikel L. Z. Luque + +10. **学术期刊和会议论文** - 搜索与RISC-V SIMD和Unroll优化相关的学术论文,可以提供深入的技术分析和案例研究。 + +请注意,上述链接可能需要根据实际可用性和访问权限进行调整。在使用这些资源时,请确保遵循适当的引用规范和版权法规。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3_1.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3_1.md new file mode 100644 index 0000000000000000000000000000000000000000..9dac2f2daa575c78dd8ab87ecd0419fa6c8590b2 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3_1.md @@ -0,0 +1,68 @@ +# RISC-V 向量拓展 (V Extension) 基础 + +RISC-V 向量拓展 (Vector Extension, V) 是 RISC-V 指令集架构的一部分,旨在为高性能计算、机器学习、图像处理等领域提供高效的向量处理能力。向量拓展指令集支持单指令多数据 (SIMD) 操作,能够显著提高并行计算性能。 + +## 向量寄存器与指令集 + +1. **向量寄存器** + - RISC-V V 扩展提供了一组向量寄存器 (Vector Registers),每个寄存器可以存储多个数据元素。 + - 向量寄存器的数量通常为 32 个,标识为 v0 到 v31。 + - 每个向量寄存器的长度是可配置的,可以是 128 位、256 位、512 位等。 + +2. **向量指令集** + - RISC-V V 扩展定义了一组向量指令,用于对向量寄存器进行操作。这些指令包括但不限于: + - **加载/存储指令**:用于在内存和向量寄存器之间传输数据。 + - **算术运算指令**:用于执行加法、减法、乘法、除法等运算。 + - **逻辑运算指令**:用于执行按位与、或、异或等操作。 + - **比较指令**:用于比较向量寄存器中的数据。 + - **数据重排指令**:用于重排、合并、拆分向量数据。 + +## SIMD 和向量化 + +1. **SIMD (Single Instruction, Multiple Data)** + - SIMD 是一种并行计算模式,在 RISC-V 向量扩展中得到了体现。 + - SIMD 允许单条指令同时对多个数据元素进行操作,从而提高计算效率。 + +2. **向量化** + - 向量化是指将标量操作转换为向量操作,以利用 SIMD 指令的并行计算能力。 + - 向量化可以通过手动编写向量指令,或者使用编译器自动向量化实现。 + +## RISC-V C 语言计算库 + +为了在 C 语言中高效地利用 RISC-V 的向量拓展和 SIMD 指令,可以使用专门的计算库。这些库提供了高层次的 API,简化了向量指令的使用。 + +### RISC-V 向量计算库 + +1. **RVV Intrinsics** + - RVV Intrinsics 是一组内嵌函数,允许在 C 语言中直接使用 RISC-V 向量指令。 + - Intrinsics 函数通常以 `__riscv_v_` 开头,如 `__riscv_v_add_vv` 用于向量加法。 + +2. **使用示例** + ```c + #include + + void vector_add(const int *a, const int *b, int *c, int n) { + size_t vl; + for (size_t i = 0; i < n; i += vl) { + vl = vsetvl_e32m1(n - i); // 设置向量长度 + vint32m1_t va = vle32_v_i32m1(&a[i], vl); // 加载向量a + vint32m1_t vb = vle32_v_i32m1(&b[i], vl); // 加载向量b + vint32m1_t vc = vadd_vv_i32m1(va, vb, vl); // 执行向量加法 + vse32_v_i32m1(&c[i], vc, vl); // 存储结果向量c + } + } + ``` + +### RISC-V 向量库优化 + +1. **循环展开 (Loop Unrolling)** + - 循环展开是一种编译优化技术,通过减少循环控制开销提高性能。 + - 手动循环展开可以结合向量指令进一步提升性能。 + +2. **自动向量化** + - 编译器可以自动识别并向量化循环和数据操作,以生成高效的 SIMD 代码。 + - 需要确保代码中的数据访问和操作是连续且没有数据依赖的,以便编译器可以进行向量化。 + +3. **手动向量化** + - 在某些情况下,手动编写向量化代码可以获得更高的性能。 + - 使用 RVV Intrinsics 或者汇编语言直接编写向量操作代码。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3_2.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3_2.md new file mode 100644 index 0000000000000000000000000000000000000000..58cb6cd08df750bc4c48b9856b098184e98ce04b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-3_2.md @@ -0,0 +1,87 @@ +# RISC-V SIMD 和 Unroll 优化方案 + +为了优化C语言程序以利用RISC-V的SIMD和Unroll技术,下面是一个详细的步骤指南,包括代码示例。 + +### 步骤1: 识别热点代码 + +首先,使用性能分析工具来识别程序中的热点代码段。这些热点代码通常是性能瓶颈所在,优化它们可以获得最大的性能提升。 + +```bash +# 使用gprof进行性能分析 +gcc -O2 -pg your_program.c -o your_program +./your_program +gprof your_program gmon.out +``` + +### 步骤2: 重构循环 + +确保循环中的数据访问是连续的,这有助于提高缓存的利用率。消除循环体内的依赖,使得可以安全地应用循环展开。 + +#### 原始循环 +```c +int sum = 0; +for (int i = 0; i < N; ++i) { + sum += array[i]; +} +``` + +#### 重构后的循环 +```c +int sum = 0; +for (int i = 0; i < N; i+=4) { + sum += array[i]; + sum += array[i+1]; + sum += array[i+2]; + sum += array[i+3]; +} +``` + +### 步骤3: 使用向量指令 + +使用RISC-V的向量指令集(RVV)来替换标量操作。确保数据对齐,以利用SIMD指令的性能。 + +#### 使用RVV Intrinsics +```c +#include + +int main() { + size_t N = 1024; + int array[N], sum = 0; + vint32m1_t vec_sum = vsetvli(0, RVV_VLMAX, RVV_E32, RVV_M1); + + for (int i = 0; i < N; i+=RVV_VL) { + vint32m1_t data = vle32_v_i32m1(array + i, RVV_VL); + vec_sum = vadd_vv_i32m1(vec_sum, data, RVV_VL); + } + + sum = vredsum_vs_i32m1_i32(vec_sum, 0, RVV_VLMAX); + return sum; +} +``` + +### 步骤4: 编译器优化 + +启用编译器的自动向量化选项,并确保编译选项适用于RISC-V架构。 + +```bash +# 使用GCC编译器进行优化 +gcc -O3 -march=rv64imafdc -ftree-vectorize your_program.c -o your_program +``` + +### 步骤5: 性能测试和调优 + +使用基准测试和性能分析工具来验证优化效果,并根据测试结果进一步调整代码和编译选项。 + +```bash +# 使用perf进行性能测试 +perf stat -e task-clock,cache-misses,branches,branch-misses ./your_program +``` + +根据性能测试的结果,可能需要回到步骤2或步骤3,对循环展开的程度或向量化的使用进行调整。 + +### 注意事项 +- 确保在使用向量指令之前,数据已经适当对齐。 +- 循环展开的程度应该根据目标机器的寄存器数量和缓存大小来决定。 +- 过度优化可能会导致代码可读性降低,需要在性能和可维护性之间找到平衡。 + +通过遵循上述步骤,你可以有效地利用RISC-V的SIMD和Unroll技术来优化C语言程序的性能。记得在每一步中都进行充分的测试,以确保所做的优化是有效的。 \ No newline at end of file diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4.md new file mode 100644 index 0000000000000000000000000000000000000000..f5c5edbccdf60c71730c31cbfaa8ef7028b82dfd --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4.md @@ -0,0 +1,29 @@ +# 矩阵与卷积算法 + +## 本章要求 + +1. 了解矩阵乘法和卷积算法。 +2. 掌握矩阵转置的概念。 +3. 学会通过编程实现矩阵乘法和卷积算法。 + +## 主要内容 + +- [矩阵与线性代数](./chapter-4_1.md) +- [卷积算法](./chapter-4_2.md) +- [矩阵转置](./chapter-4_3.md) + +## 注意 + +本章节为习题前置知识,而非正式课程内容,但建议先学习本章节内容再进行相关习题。 + +## 参考资料 + +对于矩阵乘法、卷积算法和矩阵转置算法的参考资料,以下是一些推荐的算法资料: + +### 矩阵乘法 +- **Strassen算法**是一种经典的矩阵乘法算法,它通过分治法将矩阵分为更小的子矩阵并递归地计算它们;矩阵乘法的朴素算法具有Θ(n^3)的时间复杂度,而Strassen算法通过递归地分割和组合子矩阵来降低时间复杂度。 + +### 卷积算法 +- 实际采用的卷积算法包括但不限于VanillaConv、GroupConv、Depthwise Separable Convolution、DeformableConv、CondConv、Ghost Convolution和Involution等,它们在精度、参数量和计算量之间进行不同的取舍。 + + diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_1.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_1.md new file mode 100644 index 0000000000000000000000000000000000000000..1b9e76b2dccf73a893f98ae2c02163a4fee3fac8 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_1.md @@ -0,0 +1,34 @@ +# 矩阵与线性代数 + +- 矩阵乘法是线性代数的基本运算,它可以用来表示和操作向量、矩阵、张量等数学对象。矩阵的加法、减法、乘法、除法、转置、行列式、特征值、特征向量等运算都是线性代数的基本概念。 + +## 算法流程 + +1. 确定输入矩阵的维度。 +2. 确定输出矩阵的维度。 +3. 按照输出矩阵的维度,将输入矩阵的行向量依次与输出矩阵的列向量进行矩阵乘法运算,得到输出矩阵的相应元素。 +4. 输出矩阵的元素即为输入矩阵与输出矩阵的乘积。 + +## 伪代码 + +1. 输入矩阵 A 的维度为 m * n,输出矩阵 B 的维度为 p * q。 +2. 对于 i = 1 to p,j = 1 to q,执行以下操作: + - 对于 k = 1 to n,执行以下操作: + - 计算 A(i, k) * B(k, j) 并将结果累加到 C(i, j) 中。 + - 输出矩阵 C 的元素即为输入矩阵 A 和输出矩阵 B 的卷积。 + +## 示例代码(C语言) + +```c +// 假设我们有两个矩阵A和B,它们的尺寸分别为 m x n 和 n x p,矩阵C是结果矩阵,尺寸为 m x p。 +void matrix_multiply(int m, int n, int p, double A[m][n], double B[n][p], double C[m][p]) { + for (int i = 0; i < m; i++) { + for (int j = 0; j < p; j++) { + C[i][j] = 0; + for (int k = 0; k < n; k++) { + C[i][j] += A[i][k] * B[k][j]; + } + } + } +} +``` diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_2.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_2.md new file mode 100644 index 0000000000000000000000000000000000000000..d5dff57d4a51c557b5de3d0fb6ceb2f173784c5b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_2.md @@ -0,0 +1,72 @@ +# 卷积算法 + +卷积算法是一种在信号处理、图像处理、机器学习等领域广泛使用的数学运算。它涉及到两个函数的卷积,通常用来分析一个信号或函数与另一个信号或函数的相似度。在深度学习中,卷积神经网络(CNN)就是基于卷积算法构建的。 + + + + +

+The convolution of two functions \( f(t) \) and \( g(t) \), denoted as \( (f * g)(t) \), is defined as: +

+

+\[ + (f * g)(t) = \int_{-\infty}^{\infty} f(\tau) g(t - \tau) \, d\tau + \] +

+ +其中,τ是积分变量,t是时间或空间的坐标。 + +在离散情况下,卷积定义为: +

+ \[ + (f * g)(n) = \sum_{m=-\infty}^{\infty} f(m) g(n - m) + \] +

+这里的f(m)和g(m)是离散信号的值。 + +## 卷积的应用 + +信号处理:用于滤波、平滑、特征提取等。 +图像处理:用于边缘检测、图像平滑、特征提取等。 +机器学习:在卷积神经网络中,卷积层通过卷积操作提取输入数据的特征。 + +## 卷积算法流程 + +1. 确定卷积核:选择或学习一个卷积核(滤波器)。 +2. 卷积核中心定位:将卷积核中心对齐到输入数据的第一个位置。 +3. 元素乘积:计算卷积核和输入数据对应位置的元素乘积。 +4. 求和:将上一步得到的乘积求和,得到输出特征图的一个元素。 +5. 滑动卷积核:将卷积核向右滑动一个元素宽度,重复步骤3和4。 + +## 资料 +1. [知乎 - 如何通俗易懂地解释卷积?](https://www.zhihu.com/question/22298352) +2. [CSDN - 卷积算法详解](https://blog.csdn.net/weixin_43702653/article/details/123776987) + +## 示例代码(C语言) + +```c +// 假设我们有一个输入矩阵 input 和一个卷积核 kernel,我们需要计算输出矩阵 output。 +void convolution2D(int inputRows, int inputCols, double input[inputRows][inputCols], + int kernelRows, int kernelCols, double kernel[kernelRows][kernelCols], + double output[inputRows][inputCols]) { + int kernelCenterX = kernelCols / 2; + int kernelCenterY = kernelRows / 2; + + for (int i = 0; i < inputRows; ++i) { + for (int j = 0; j < inputCols; ++j) { + output[i][j] = 0; // 初始化输出矩阵 + for (int m = 0; m < kernelRows; ++m) { + for (int n = 0; n < kernelCols; ++n) { + int ii = i + (m - kernelCenterY); + int jj = j + (n - kernelCenterX); + + // 检查是否越界 + if (ii >= 0 && ii < inputRows && jj >= 0 && jj < inputCols) { + output[i][j] += input[ii][jj] * kernel[m][n]; + } + } + } + } + } +} +``` diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_3.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_3.md new file mode 100644 index 0000000000000000000000000000000000000000..fad7bb2981e869e90227272b2fd30dbccfbf882d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-4_3.md @@ -0,0 +1,45 @@ +# 矩阵转置 + +矩阵转置是指矩阵的行列互换,记作$A^T$,定义为: +

+ \[ + A^T = [a_{ij}]_{m\times n} = \left[ + \begin{array}{cccc} + a_{11} & a_{12} & \cdots & a_{1n} \\ + a_{21} & a_{22} & \cdots & a_{2n} \\ + \vdots & \vdots & \ddots & \vdots \\ + a_{m1} & a_{m2} & \cdots & a_{mn} + \end{array} + \right] + \] +

+ +即为: +对每一个i,j,将矩阵A的第i行的元素与第j列的元素进行交换。 + +## 示例代码(C语言) + +```c +// 假设我们有一个矩阵 A,我们需要计算它的转置矩阵。 +void transpose(int m, int n, double A[m][n], double AT[n][m]) { + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + AT[j][i] = A[i][j]; + } + } +} +``` + +## 原地转置 + +```c +void transpose(int m, int n, double A[m][n]) { + for (int i = 0; i < m; i++) { + for (int j = i + 1; j < n; j++) { + double temp = A[i][j]; + A[i][j] = A[j][i]; + A[j][i] = temp; + } + } +} +``` \ No newline at end of file diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5.md new file mode 100644 index 0000000000000000000000000000000000000000..669b61486afe0d9e7e2ce7595e875b1684218a3a --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5.md @@ -0,0 +1,41 @@ +# Linux内核模块 + +## 本章要求 + +1. 了解Linux内核模块的概念和用法。 +2. 掌握编写一个简单内核模块的方法。 +3. 了解内核模块参数的传递方式。 +4. 学会利用内核模块创建一个虚拟字符设备。 +5. 了解如何通过数组参数传递给内核模块。 + +## 主要内容 + +- [编写一个简单的内核模块](./chapter-5_1.md) +- [内核模块传参](./chapter-5_2.md) +- [创建字符设备](./chapter-5_3.md) +- [实现计时器](./chapter-5_4.md) +- [实现简单文件操作](./chapter-5_5.md) +- [Linux系统编程基础](./chapter-5_6.md) + +## 注意 + +本章节为习题前置知识,而非正式课程内容,但建议先学习本章节内容再进行相关习题。 + + +## 前提条件 +要跟随本教程,您需要具备以下前提条件: +- 熟练掌握C编程 +- 基本的Linux命令行知识 +- 已设置好Linux开发环境(例如,运行Linux发行版并安装了`make`和`gcc`) + +## 本章简介 + +本教程为编写 Linux 内核模块提供了一个起点。它涵盖了模块参数传递、创建虚拟字符设备、启动定时器以及使用简单的文件操作。对于更高级的主题,请参考《Linux 内核模块编程指南》和其他内核开发资源。 + + +## 参考文献 + +- [Linux Kernel Module Programming Guide](https://tldp.org/LDP/lkmpg/2.6/html/) +- [Linux Device Drivers, Third Edition](https://lwn.net/Kernel/LDD3/) +- [Kernel API Documentation](https://www.kernel.org/doc/html/latest/) + diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_1.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_1.md new file mode 100644 index 0000000000000000000000000000000000000000..4c01d82dc3ee90b57ceba58022a95222455b3b89 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_1.md @@ -0,0 +1,62 @@ +# 编写一个简单的内核模块 + +从一个简单的“Hello, World!”内核模块开始,了解模块编程的基础。 + +## 示例 +```c +#include // Needed by all modules +#include // Needed for KERN_INFO +#include // Needed for macros + +static int __init hello_init(void) +{ + printk(KERN_INFO "Hello, World!\n"); + return 0; +} + +static void __exit hello_exit(void) +{ + printk(KERN_INFO "Goodbye, World!\n"); +} + +module_init(hello_init); +module_exit(hello_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Your Name"); +MODULE_DESCRIPTION("A simple Hello World module"); +``` + +## 编译与加载模块 +1. 在目录中编写`Makefile`: + ```makefile + obj-m += hello_module.o + + all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + + clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean + ``` + +> 注意:之后其他章节均有类似此示例的Makefile,故省略不写,Makefile中`obj-m`变量的值应与模块名一致。 + +2. 编译模块: + ```bash + make + ``` + +3. 加载模块: + ```bash + sudo insmod hello_module.ko + ``` + +4. 查看模块加载日志: + ```bash + dmesg | tail + ``` + +5. 卸载模块: + ```bash + sudo rmmod hello_module + ``` \ No newline at end of file diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_2.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_2.md new file mode 100644 index 0000000000000000000000000000000000000000..49bf768712667a4f9a9d151760f654885e7c75db --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_2.md @@ -0,0 +1,91 @@ +# 内核模块传参 + +## 示例 +```c +#include +#include +#include +#include + +static int param_var = 0; +module_param(param_var, int, S_IRUGO); +MODULE_PARM_DESC(param_var, "An integer"); + +static int __init param_init(void) +{ + printk(KERN_INFO "param_var: %d\n", param_var); + return 0; +} + +static void __exit param_exit(void) +{ + printk(KERN_INFO "Exiting param_module\n"); +} + +module_init(param_init); +module_exit(param_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Your Name"); +MODULE_DESCRIPTION("A module with parameter passing"); +``` + +加载模块并传入参数 +```bash +sudo insmod param_module.ko param_var=42 +dmesg | tail +``` + +# 数组传参 + +Linux 内核模块可以接受数组作为参数,这对于配置和自定义模块行为非常有用。下面我们展示如何实现这一点。 + +## 示例 +```c +#include +#include +#include +#include + +#define MAX_ARRAY_SIZE 5 + +// Define an array and a variable to hold the array size +static int array[MAX_ARRAY_SIZE]; +static int arr_argc = 0; + +// Define the module parameters +module_param_array(array, int, &arr_argc, 0000); +MODULE_PARM_DESC(array, "An integer array"); + +static int __init array_param_init(void) +{ + int i; + printk(KERN_INFO "Array parameter module initialized with %d elements\n", arr_argc); + for (i = 0; i < arr_argc; i++) + { + printk(KERN_INFO "array[%d] = %d\n", i, array[i]); + } + return 0; +} + +static void __exit array_param_exit(void) +{ + printk(KERN_INFO "Exiting array parameter module\n"); +} + +module_init(array_param_init); +module_exit(array_param_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Your Name"); +MODULE_DESCRIPTION("A module with array parameter passing"); +``` + +加载模块并传递数组参数: + + ```bash + sudo insmod array_param_module.ko array=1,2,3,4,5 + ``` + +## 总结 +通过这种方式,可以向内核模块传递数组参数,从而使模块更加灵活和可配置。这对于需要动态传递多个参数的情况非常有用。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_3.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_3.md new file mode 100644 index 0000000000000000000000000000000000000000..1855a0e991c5981c7392945969d8d592552dd568 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_3.md @@ -0,0 +1,173 @@ +## 创建虚拟字符设备 + +虚拟字符设备通过文件操作与用户空间应用程序交互。 + + +## 示例代码 +```c +#include +#include +#include // copy_to_user, copy_from_user +#include + +#define DEVICE_NAME "vchardev" +#define BUF_LEN 80 + +static int major; +static char message[BUF_LEN]; +static struct class *vchardev_class = NULL; +static struct device *vchardev_device = NULL; + +static int dev_open(struct inode *inode, struct file *file) +{ + printk(KERN_INFO "vchardev: device opened\n"); + return 0; +} + +static int dev_release(struct inode *inode, struct file *file) +{ + printk(KERN_INFO "vchardev: device closed\n"); + return 0; +} + +static ssize_t dev_read(struct file *file, char __user *buffer, size_t len, loff_t *offset) +{ + int bytes_read = 0; + if (*message == 0) + return 0; + + while (len && *message) + { + put_user(*(message++), buffer++); + len--; + bytes_read++; + } + printk(KERN_INFO "vchardev: read %d bytes\n", bytes_read); + return bytes_read; +} + +static ssize_t dev_write(struct file *file, const char __user *buffer, size_t len, loff_t *offset) +{ + int i; + for (i = 0; i < len && i < BUF_LEN - 1; i++) + get_user(message[i], buffer + i); + + message[i] = '\0'; + printk(KERN_INFO "vchardev: received %zu characters from user\n", len); + return len; +} + +static struct file_operations fops = { + .open = dev_open, + .read = dev_read, + .write = dev_write, + .release = dev_release, +}; + +static int __init vchardev_init(void) +{ + major = register_chrdev(0, DEVICE_NAME, &fops); + if (major < 0) + { + printk(KERN_ALERT "vchardev failed to register a major number\n"); + return major; + } + printk(KERN_INFO "vchardev: registered with major number %d\n", major); + + vchardev_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(vchardev_class)) + { + unregister_chrdev(major, DEVICE_NAME); + printk(KERN_ALERT "Failed to register device class\n"); + return PTR_ERR(vchardev_class); + } + printk(KERN_INFO "vchardev: device class registered\n"); + + vchardev_device = device_create(vchardev_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME); + if (IS_ERR(vchardev_device)) + { + class_destroy(vchardev_class); + unregister_chrdev(major, DEVICE_NAME); + printk(KERN_ALERT "Failed to create the device\n"); + return PTR_ERR(vchardev_device); + } + printk(KERN_INFO "vchardev: device created\n"); + + return 0; +} + +static void __exit vchardev_exit(void) +{ + device_destroy(vchardev_class, MKDEV(major, 0)); + class_unregister(vchardev_class); + class_destroy(vchardev_class); + unregister_chrdev(major, DEVICE_NAME); + printk(KERN_INFO "vchardev: module unloaded\n"); +} + +module_init(vchardev_init); +module_exit(vchardev_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Your Name"); +MODULE_DESCRIPTION("A simple virtual character device"); +``` + +## 编译运行调试 + +编译代码: + +```bash +$ make +``` + +加载模块: + +```bash +$ insmod vchardev.ko +``` + +查看模块信息: + +```bash +$ lsmod | grep vchardev +vchardev 16384 0 +``` + +创建设备文件: + +```bash +$ mknod /dev/vchardev c 254 0 +``` + +查看设备文件信息: + +```bash +$ ls -l /dev/vchardev +crw-rw-rw- 1 root root 254, 0 May 20 10:20 /dev/vchardev +``` + +打开设备文件: + +```bash +$ cat /dev/vchardev +``` + +向设备文件写入数据: + +```bash +$ echo "Hello, world!" > /dev/vchardev +``` + +从设备文件读取数据: + +```bash +$ cat /dev/vchardev +Hello, world! +``` + +卸载模块: + +```bash +$ rmmod vchardev +``` diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_4.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_4.md new file mode 100644 index 0000000000000000000000000000000000000000..60cf1baabb46ee05ee84e5741fe23dfabaa030cc --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_4.md @@ -0,0 +1,64 @@ +# 实现计时器 + +计时器对于在内核空间中处理定时操作至关重要。 + + +## 示例 +```c +#include +#include +#include +#include + +static struct timer_list my_timer; + +void timer_callback(struct timer_list *t) +{ + printk(KERN_INFO "Timer expired and callback executed\n"); +} + +static int __init timer_init(void) +{ + printk(KERN_INFO "Initializing Timer Module\n"); + + timer_setup(&my_timer, timer_callback, 0); + mod_timer(&my_timer, jiffies + msecs_to_jiffies(2000)); // 2 seconds + + return 0; +} + +static void __exit timer_exit(void) +{ + del_timer(&my_timer); + printk(KERN_INFO "Timer Module Unloaded\n"); +} + +module_init(timer_init); +module_exit(timer_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Your Name"); +MODULE_DESCRIPTION("A simple timer module"); +``` + +## 编译运行调试 + +加载模块: + +```bash +sudo insmod timer.ko +``` + +卸载模块: + +```bash +sudo rmmod timer +``` + +查看日志: + +```bash +dmesg +``` + +在日志中可以看到打印的时间信息。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_5.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_5.md new file mode 100644 index 0000000000000000000000000000000000000000..7be5c81aee9ad64c674118ec6334d405c0f46671 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_5.md @@ -0,0 +1,70 @@ +# 实现简单的文件操作函数 + +## filp_open函数及示例 +`filp_open` 用于在内核空间打开一个文件。 + +```c +#include +#include +#include +#include +#include + +static int __init filp_open_example_init(void) +{ + struct file *f; + char buf[128]; + mm_segment_t fs; + loff_t pos = 0; + + printk(KERN_INFO "Opening file from kernel space\n"); + + f = filp_open("/etc/hostname", O_RDONLY, 0); + if (IS_ERR(f)) + { + printk(KERN_ALERT "Failed to open file\n"); + return PTR_ERR(f); + } + + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_read(f, buf, sizeof(buf), &pos); + set_fs(fs); + + printk(KERN_INFO "File content: %s\n", buf); + + filp_close(f, NULL); + + return 0; +} + +static void __exit filp_open_example_exit(void) +{ + printk(KERN_INFO "Unloading filp_open_example module\n"); +} + +module_init(filp_open_example_init); +module_exit(filp_open_example_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Your Name"); +MODULE_DESCRIPTION("A module demonstrating filp_open"); +``` + +## 加载与调试 + +编译并加载模块: + +``` +$ make +$ sudo insmod filp_open_example.ko +``` + + +运行结果: + +``` +[ 26.313563] Opening file from kernel space +[ 26.313602] File content: localhost +[ 26.313605] Unloading filp_open_example module +``` diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_6.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_6.md new file mode 100644 index 0000000000000000000000000000000000000000..32c96e5de8c21c2fa6564cf557743c72480695b3 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-5_6.md @@ -0,0 +1,209 @@ +# Linux系统编程基础 + +## 本章要求 + +1. 了解Linux系统编程的基本概念,包括进程、线程、内存管理、文件系统接口等。 +2. 掌握Linux系统编程的基本技术,包括获取主机名、设置程序内存上限、使用`fork`创建子进程、通过管道进行进程间通讯等。 +3. 能够编写简单的Linux系统编程程序。 + +## 注意 + +本章节为习题前置知识,而非正式课程内容,但建议先学习本章节内容再进行相关习题。 + +## 介绍 + +Linux系统编程涉及使用C语言与操作系统进行直接交互。本文将详细介绍一些基本的Linux系统编程技术,并通过示例代码展示如何获取主机名、设置程序内存上限、使用`fork`创建子进程,以及通过管道进行进程间通讯。 + +## 获取主机名 + +### `gethostname`函数 + +`gethostname`函数用于获取当前主机的名称。它的原型如下: + +```c +#include +int gethostname(char *name, size_t len); +``` + +### 示例代码 + +```c +#include +#include +#include + +int main() { + char hostname[HOST_NAME_MAX]; + + if (gethostname(hostname, sizeof(hostname)) == -1) { + perror("gethostname"); + return 1; + } + + printf("Hostname: %s\n", hostname); + return 0; +} +``` + +## 使用`rlimit`设置程序内存上限 + +### `setrlimit`函数 + +`setrlimit`函数用于设置进程的资源限制。它的原型如下: + +```c +#include +int setrlimit(int resource, const struct rlimit *rlim); +``` + +### 示例代码 + +```c +#include +#include + +int main() { + struct rlimit rl; + + // 设置程序的内存上限为128MB + rl.rlim_cur = 128 * 1024 * 1024; + rl.rlim_max = 128 * 1024 * 1024; + + if (setrlimit(RLIMIT_AS, &rl) == -1) { + perror("setrlimit"); + return 1; + } + + printf("Memory limit set to 128MB\n"); + return 0; +} +``` + +## `fork`函数创建子进程 + +### `fork`函数 + +`fork`函数用于创建一个子进程。它的原型如下: + +```c +#include +pid_t fork(void); +``` + +### 示例代码 + +```c +#include +#include + +int main() { + pid_t pid = fork(); + + if (pid == -1) { + perror("fork"); + return 1; + } else if (pid == 0) { + // 子进程 + printf("Hello from the child process!\n"); + } else { + // 父进程 + printf("Hello from the parent process!\n"); + } + + return 0; +} +``` + +## 管道与进程间通讯 + +### `pipe`函数 + +`pipe`函数用于创建一个管道,管道是进程间通讯的一种方式。它的原型如下: + +```c +#include +int pipe(int pipefd[2]); +``` + +### 示例代码 + +```c +#include +#include +#include + +int main() { + int pipefd[2]; + char buffer[128]; + + if (pipe(pipefd) == -1) { + perror("pipe"); + return 1; + } + + pid_t pid = fork(); + + if (pid == -1) { + perror("fork"); + return 1; + } else if (pid == 0) { + // 子进程 + close(pipefd[0]); // 关闭读端 + char message[] = "Hello from the child process!"; + write(pipefd[1], message, strlen(message)); + close(pipefd[1]); // 关闭写端 + } else { + // 父进程 + close(pipefd[1]); // 关闭写端 + read(pipefd[0], buffer, sizeof(buffer)); + printf("Parent received: %s\n", buffer); + close(pipefd[0]); // 关闭读端 + } + + return 0; +} +``` + +## 学习资料 + +学习Linux系统编程的相关资料和教程非常丰富,以下是一些推荐的资源: + +### 书籍 +1. **《UNIX环境高级编程》**(Advanced Programming in the UNIX Environment) - 作者:W. Richard Stevens + - 这本书被誉为UNIX编程的经典之作,详细介绍了UNIX系统编程的各个方面,是深入学习Linux编程的必备书籍。 + +2. **《Linux系统编程》**(Linux System Programming) - 作者:Robert Love + - 本书专门介绍了Linux系统编程的基础和高级主题,涵盖了文件I/O、进程控制、线程和同步、网络编程等内容。 + +3. **《深入理解Linux内核》**(Understanding the Linux Kernel) - 作者:Daniel P. Bovet, Marco Cesati + - 这本书深入解析了Linux内核的各个子系统和模块,对理解Linux系统的工作原理非常有帮助。 + +### 在线教程和文档 +1. **The Linux Programming Interface** - 作者:Michael Kerrisk + - 该书的官方网站提供了部分章节的在线阅读和示例代码,详细讲解了Linux编程接口。 + +2. [**GNU C Library Documentation**](https://www.gnu.org/software/libc/manual/) + - 官方文档详细介绍了GNU C库中的各个函数和数据结构,是查阅具体函数用法的权威资料。 + +3. **Linux Manual Pages (man pages)** + - Linux系统自带的手册页提供了大量函数和命令的使用说明,使用`man`命令可以方便地查阅。 + - 例如,`man 2 fork` 可以查看`fork`系统调用的详细信息。 + +### 在线课程 +1. [**Coursera: "Operating Systems and You: Becoming a Power User"**]((https://www.coursera.org/learn/os-power-user)) + - 这门课程由谷歌提供,介绍了Linux操作系统的基本使用和系统编程的基本概念。 + +2. [**Udacity: "Linux System Programming"**](https://www.udacity.com/course/linux-system-programming--ud923) + - 这门课程提供了Linux系统编程的入门知识,适合初学者学习。 + +### 社区和论坛 +1. [**Stack Overflow**](https://stackoverflow.com/) + - Stack Overflow是一个大型编程问答社区,里面有大量关于Linux系统编程的问答,搜索具体问题时可以找到许多有用的答案。 + +2. [**Reddit: r/linux**](https://www.reddit.com/r/linux/) + - Reddit的Linux板块讨论Linux系统相关的各种话题,包括系统编程,里面有很多经验丰富的用户可以提供帮助。 + +### 实验和实践 +1. [**Linux From Scratch (LFS)**](http://www.linuxfromscratch.org/lfs/) + - 通过从零开始构建一个Linux系统,可以深入理解Linux的各个组件和编程接口。 + diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6.md new file mode 100644 index 0000000000000000000000000000000000000000..814c3d6088689b8b179559bb4ea1f8cc9c119bd9 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6.md @@ -0,0 +1,31 @@ +# RISC-V架构内联汇编 + +## 本章要求 + +1. 了解RISC-V指令集的基本知识。 +2. 了解RISC-V架构的特点,以及如何在C语言中使用内联汇编。 +3. 掌握C语言中常见的控制结构的汇编表示方法。 + +## 主要内容 + +- [RISC-V架构简介](./chapter-6_1.md) +- [RISC-V基础指令集](./chapter-6_2.md) +- [控制结构的汇编表示方法](./chapter-6_3.md) + + +## 注意 + +本章节为习题前置知识,而非正式课程内容,但建议先学习本章节内容再进行相关习题。 + +## 本章简介 + +通过本教程,你将了解如何在RISC-V架构上使用内联汇编,并掌握将C语言中的控制结构转换为汇编代码的方法。希望这些内容对你在实际编程中有所帮助。 + +## 参考资料 +- [RISC-V 官方文档](https://riscv.org/technical/specifications/) +- [GCC 内联汇编指南](https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html) +- [RISC-V 汇编教程](https://riscv.org/software-tools/risc-v-assembly-programming/) +- [Learn RISC-V Assembly](https://riscvasm.com) +- 《RISC-V汇编语言程序设计》 +- 《嵌入式系统与RISC-V架构》 + diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_1.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_1.md new file mode 100644 index 0000000000000000000000000000000000000000..24d664b3c5a032d1cd7f5571f2605d1c12d5e21a --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_1.md @@ -0,0 +1,38 @@ +# RISC-V架构简介 + +RISC-V(发音为“risk-five”)是一个开源的指令集架构(ISA),它由加利福尼亚大学伯克利分校开发。RISC-V 的设计原则是简洁、模块化和可扩展,使其在研究、教育和商业领域都得到了广泛应用。 + +### RISC-V 的主要特点 + +1. **开源与自由**:RISC-V 是一个开放标准的 ISA,任何人都可以免费使用、修改和发布。这种开源特性使得 RISC-V 在学术研究和商业应用中具有很大的吸引力。 + +2. **简洁性**:RISC-V 的设计强调简洁,采用了精简指令集计算(RISC)原则。这使得 RISC-V 指令集相比于复杂指令集计算(CISC)如 x86 更加简单和高效。 + +3. **模块化**:RISC-V 采用模块化设计,不同的应用可以选择不同的扩展。基础的 RISC-V ISA 被称为 RV32I(32位)或 RV64I(64位),并可以通过添加扩展模块(如浮点、原子操作、多线程等)来增强功能。 + +4. **可扩展性**:RISC-V 支持从嵌入式系统到高性能计算的各种应用场景。其指令集架构可以扩展以支持特定领域的需求,比如专门用于机器学习加速的扩展。 + +### RISC-V 基础指令集 + +RISC-V 的基础指令集包括以下几个部分: + +1. **整数计算指令**(RV32I/RV64I):包括基本的算术、逻辑、移位和比较指令。 +2. **控制流指令**:包括条件分支、无条件跳转和函数调用/返回指令。 +3. **内存访问指令**:包括加载和存储指令,用于数据在寄存器和内存之间的传输。 + +### RISC-V 的扩展 + +1. **M 扩展**(乘法和除法):增加了整数乘法和除法指令。 +2. **A 扩展**(原子操作):支持原子读-修改-写指令,用于多线程同步。 +3. **F 和 D 扩展**(浮点数):增加了单精度(F)和双精度(D)浮点运算指令。 +4. **C 扩展**(压缩指令):通过使用更短的指令编码来减少程序的代码大小。 +5. **V 扩展**(向量操作):支持向量化操作,以加速数据并行计算。 + +### RISC-V 的应用 + +RISC-V 的灵活性和开放性使得它在多种领域中得到了应用,包括: + +1. **嵌入式系统**:由于其简洁和高效,RISC-V 非常适合低功耗的嵌入式设备。 +2. **物联网(IoT)**:开源的特点使得 RISC-V 可以广泛应用于各种物联网设备中。 +3. **高性能计算**:通过添加特定的扩展模块,RISC-V 也可以应用于高性能计算领域。 +4. **教育和研究**:RISC-V 的开放性和模块化设计使其成为教学和研究的理想平台。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_2.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_2.md new file mode 100644 index 0000000000000000000000000000000000000000..6617c46847518f677386d90d0cd5156ce461dfe1 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_2.md @@ -0,0 +1,168 @@ +# RISC-V基础指令集 + +本集训营用的最多的指令集是RV64I基础,它包含了处理器指令集最核心的部分:整数计算、控制流和内存访问指令,指令简单明了,适合快速掌握。 + +下面是对 RV64I 基础指令集的详细介绍。 + +## 寄存器 +- `x0`-`x31`:32个整数寄存器。 +- `f0`-`f31`:32个浮点寄存器。 +- `pc`:程序计数器。 +- `sp`:堆栈指针。 +- `ra`:返回地址寄存器。 + +## 指令格式 + +指令格式如下: + +```assembly +opcode rd, rs1, rs2 [, imm] +``` + +- `opcode`:指令的操作码。 +- `rd`:目标寄存器。 +- `rs1`:源寄存器1。 +- `rs2`:源寄存器2。 +- `imm`:立即数。 + +## 指令集 + +### RV32I 基础指令集 + +RV32I 基础指令集包含了整数计算、控制流和内存访问指令。 + +- 整数计算指令:`ADD`, `SUB`, `ADDI`, `AND`, `OR`, `XOR`, `ANDI`, `SLL`, `SRL`, `SRA`, `SLT`, `SLTU`, `SLTI` +- 控制流指令:`BEQ`, `BNE`, `BLT`, `BGE`, `JAL`, `JALR` +- 内存访问指令:`LW`, `LH`, `LHU`, `LB`, `LBU`, `SW`, `SH`, `SB` + +## 整数计算指令 + +### 算术指令 +- **ADD**: 将两个寄存器的值相加,结果存储在目标寄存器中。 + ```assembly + ADD rd, rs1, rs2 # rd = rs1 + rs2 + ``` +- **SUB**: 将一个寄存器的值从另一个寄存器的值中减去,结果存储在目标寄存器中。 + ```assembly + SUB rd, rs1, rs2 # rd = rs1 - rs2 + ``` +- **ADDI**: 将一个立即数与寄存器的值相加,结果存储在目标寄存器中。 + ```assembly + ADDI rd, rs1, imm # rd = rs1 + imm + ``` + +### 逻辑指令 +- **AND**: 对两个寄存器的值执行按位与操作,结果存储在目标寄存器中。 + ```assembly + AND rd, rs1, rs2 # rd = rs1 & rs2 + ``` +- **OR**: 对两个寄存器的值执行按位或操作,结果存储在目标寄存器中。 + ```assembly + OR rd, rs1, rs2 # rd = rs1 | rs2 + ``` +- **XOR**: 对两个寄存器的值执行按位异或操作,结果存储在目标寄存器中。 + ```assembly + XOR rd, rs1, rs2 # rd = rs1 ^ rs2 + ``` +- **ANDI**: 将寄存器的值与一个立即数进行按位与操作,结果存储在目标寄存器中。 + ```assembly + ANDI rd, rs1, imm # rd = rs1 & imm + ``` + +### 移位指令 +- **SLL**: 将寄存器的值左移指定的位数,结果存储在目标寄存器中。 + ```assembly + SLL rd, rs1, rs2 # rd = rs1 << rs2 + ``` +- **SRL**: 将寄存器的值右移指定的位数,结果存储在目标寄存器中(逻辑右移)。 + ```assembly + SRL rd, rs1, rs2 # rd = rs1 >> rs2 + ``` +- **SRA**: 将寄存器的值右移指定的位数,结果存储在目标寄存器中(算术右移)。 + ```assembly + SRA rd, rs1, rs2 # rd = rs1 >> rs2 (arithmetic) + ``` + +### 比较指令 +- **SLT**: 如果第一个寄存器的值小于第二个寄存器的值,则目标寄存器设置为1,否则设置为0(有符号比较)。 + ```assembly + SLT rd, rs1, rs2 # rd = (rs1 < rs2) + ``` +- **SLTU**: 如果第一个寄存器的值小于第二个寄存器的值,则目标寄存器设置为1,否则设置为0(无符号比较)。 + ```assembly + SLTU rd, rs1, rs2 # rd = (rs1 < rs2) (unsigned) + ``` +- **SLTI**: 如果寄存器的值小于立即数,则目标寄存器设置为1,否则设置为0(有符号比较)。 + ```assembly + SLTI rd, rs1, imm # rd = (rs1 < imm) + ``` + +## 控制流指令 + +### 条件分支指令 +- **BEQ**: 如果两个寄存器的值相等,则跳转到目标地址。 + ```assembly + BEQ rs1, rs2, offset # if (rs1 == rs2) PC += offset + ``` +- **BNE**: 如果两个寄存器的值不相等,则跳转到目标地址。 + ```assembly + BNE rs1, rs2, offset # if (rs1 != rs2) PC += offset + ``` +- **BLT**: 如果第一个寄存器的值小于第二个寄存器的值,则跳转到目标地址(有符号比较)。 + ```assembly + BLT rs1, rs2, offset # if (rs1 < rs2) PC += offset + ``` +- **BGE**: 如果第一个寄存器的值大于或等于第二个寄存器的值,则跳转到目标地址(有符号比较)。 + ```assembly + BGE rs1, rs2, offset # if (rs1 >= rs2) PC += offset + ``` + +### 无条件跳转指令 +- **JAL**: 跳转到目标地址,并将返回地址存储在目标寄存器中。 + ```assembly + JAL rd, offset # rd = PC + 4; PC += offset + ``` +- **JALR**: 跳转到由寄存器和立即数计算出的地址,并将返回地址存储在目标寄存器中。 + ```assembly + JALR rd, rs1, offset # rd = PC + 4; PC = (rs1 + offset) & ~1 + ``` + +## 内存访问指令 + +### 加载指令 +- **LW**: 从内存中加载一个字(32位)到目标寄存器。 + ```assembly + LW rd, offset(rs1) # rd = *(rs1 + offset) + ``` +- **LH**: 从内存中加载一个半字(16位)到目标寄存器,并进行符号扩展。 + ```assembly + LH rd, offset(rs1) # rd = *(int16_t *)(rs1 + offset) + ``` +- **LHU**: 从内存中加载一个半字(16位)到目标寄存器,并进行零扩展。 + ```assembly + LHU rd, offset(rs1) # rd = *(uint16_t *)(rs1 + offset) + ``` +- **LB**: 从内存中加载一个字节(8位)到目标寄存器,并进行符号扩展。 + ```assembly + LB rd, offset(rs1) # rd = *(int8_t *)(rs1 + offset) + ``` +- **LBU**: 从内存中加载一个字节(8位)到目标寄存器,并进行零扩展。 + ```assembly + LBU rd, offset(rs1) # rd = *(uint8_t *)(rs1 + offset) + ``` + +### 存储指令 +- **SW**: 将一个字(32位)从源寄存器存储到内存中。 + ```assembly + SW rs2, offset(rs1) # *(rs1 + offset) = rs2 + ``` +- **SH**: 将一个半字(16位)从源寄存器存储到内存中。 + ```assembly + SH rs2, offset(rs1) # *(int16_t *)(rs1 + offset) = rs2 + ``` +- **SB**: 将一个字节(8位)从源寄存器存储到内存中。 + ```assembly + SB rs2, offset(rs1) # *(int8_t *)(rs1 + offset) = rs2 + ``` + +这些指令构成了 RISC-V 基础指令集的重要部分,为各种计算和控制流操作提供了必要的功能。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_3.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_3.md new file mode 100644 index 0000000000000000000000000000000000000000..8e7e3f66f290492f0444e94cb134a655d8b3358a --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-6_3.md @@ -0,0 +1,168 @@ +# 控制结构的汇编表示方法 + +C语言与汇编往往呈现一一对应关系,但控制结构的汇编表示方法却有所不同。本节将介绍C语言控制结构的汇编表示方法,以大家最为熟悉的C语言结构——循环、分支、跳转为例,介绍如何用汇编语言表示这些控制结构。 + +## 内联汇编语法 + +在GCC编译器中,内联汇编使用`asm`关键字。以下是一个基本的内联汇编语法示例: + +```c +int result; +asm("add %0, %1, %2" : "=r" (result) : "r" (a), "r" (b)); +``` + +- `"add %0, %1, %2"`:汇编指令模板。 +- `: "=r" (result)`:输出操作数,`=r`表示输出到寄存器。 +- `: "r" (a), "r" (b)`:输入操作数,`r`表示从寄存器读取。 + +在上面的示例中,`result`变量的值等于`a`和`b`的和。 + +## 控制结构的内联汇编表示 + +### while循环 +```c +int i = 0; +while (i < 10) { + // 循环体 + i++; +} +``` + +等价的内联汇编: +```c +int i = 0; +asm volatile ( + "loop_start: \n\t" + " blt %[i], %[max], loop_body \n\t" + " j loop_end \n\t" + "loop_body: \n\t" + " addi %[i], %[i], 1 \n\t" + " j loop_start \n\t" + "loop_end: \n\t" + : [i] "+r" (i) + : [max] "r" (10) + : "cc" +); +``` + +### if-else语句 +```c +int a = 5, b = 10; +if (a < b) { + // 真分支 +} else { + // 假分支 +} +``` + +等价的内联汇编: +```c +int a = 5, b = 10; +asm volatile ( + " blt %[a], %[b], if_true \n\t" + " j else_branch \n\t" + "if_true: \n\t" + // 真分支代码 + " j end_if \n\t" + "else_branch: \n\t" + // 假分支代码 + "end_if: \n\t" + : + : [a] "r" (a), [b] "r" (b) + : "cc" +); +``` + +### do-while循环 +```c +int i = 0; +do { + // 循环体 + i++; +} while (i < 10); +``` + +等价的内联汇编: +```c +int i = 0; +asm volatile ( + "do_start: \n\t" + " addi %[i], %[i], 1 \n\t" + " blt %[i], %[max], do_start \n\t" + : [i] "+r" (i) + : [max] "r" (10) + : "cc" +); +``` + +### switch语句 +```c +int val = 2; +switch (val) { + case 1: + // case 1代码 + break; + case 2: + // case 2代码 + break; + default: + // 默认代码 + break; +} +``` + +等价的内联汇编: +```c +int val = 2; +asm volatile ( + " li t0, 1 \n\t" + " beq %[val], t0, case1 \n\t" + " li t0, 2 \n\t" + " beq %[val], t0, case2 \n\t" + " j default_case \n\t" + "case1: \n\t" + // case 1代码 + " j end_switch \n\t" + "case2: \n\t" + // case 2代码 + " j end_switch \n\t" + "default_case: \n\t" + // 默认代码 + "end_switch: \n\t" + : + : [val] "r" (val) + : "t0", "cc" +); +``` + +### continue与break +在循环中使用`continue`和`break`: + +```c +for (int i = 0; i < 10; i++) { + if (i == 5) continue; + if (i == 8) break; + // 循环体 +} +``` + +等价的内联汇编: +```c +int i = 0; +asm volatile ( + "for_start: \n\t" + " bge %[i], %[max], for_end \n\t" + " li t0, 5 \n\t" + " beq %[i], t0, continue_loop \n\t" + " li t0, 8 \n\t" + " beq %[i], t0, for_end \n\t" + // 循环体代码 + "continue_loop: \n\t" + " addi %[i], %[i], 1 \n\t" + " j for_start \n\t" + "for_end: \n\t" + : [i] "+r" (i) + : [max] "r" (10) + : "t0", "cc" +); +``` diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7.md new file mode 100644 index 0000000000000000000000000000000000000000..60b195cf70c63d4f5908c420863ecdfadfa8b063 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7.md @@ -0,0 +1,41 @@ +# 操作系统常用算法 + +## 本章要求 + +1. 了解操作系统中常用的页面置换算法。 +2. 掌握FIFO页面置换算法的实现。 +3. 掌握LRU页面置换算法的实现。 +4. 理解并实现时间片轮转调度算法。 + +## 主要内容 + +- [FIFO 页面置换算法](./chapter-7_1.md) +- [LRU 页面置换算法](./chapter-7_2.md) +- [模拟时间片轮转调度算法](./chapter-7_3.md) + +## 注意 + +本章节为习题前置知识,而非正式课程内容,但建议先学习本章节内容再进行相关习题。 + +## 参考文献 + +### 页面置换算法 +1. **Tanenbaum, A. S., & Bos, H. (2014). Operating Systems: Design and Implementation.** - 这本书详细介绍了操作系统的设计与实现,包括页面置换算法的原理和实现。 +2. **Silberschatz, A., Galvin, P. B., & Gagne, G. (2012). Operating System Concepts.** - 作为操作系统的经典教材,涵盖了页面置换算法的基本概念和实现方法。 +3. **Stallings, W. (2014). Operating Systems: Internals and Design Principles.** - 这本书提供了操作系统的内部机制和设计原则的深入讨论,包括页面置换策略。 + +### FIFO页面置换算法 +1. **FIFO Page Replacement Algorithm.** - 通常作为操作系统内存管理章节的一部分,在上述推荐的书籍中有所介绍。 + +### LRU页面置换算法 +1. **Belady, L. A. (1966). A study of replacement algorithms for a virtual storage computer.** - 这是LRU算法的原始论文,详细介绍了算法的设计和评估。 +2. **LRU Cache Implementation.** - 可在多个算法和数据结构的在线资源中找到,例如GeeksforGeeks或LeetCode。 + +### 时间片轮转调度算法 +1. **Time Slice Scheduling.** - 通常包含在操作系统的调度章节中,上述推荐的书籍有详细的讨论和示例。 +2. **Kanetkar, Y. (2012). Unix System Programming.** - 虽然主要关注UNIX系统编程,但也提供了对时间片轮转调度算法的实用视角。 + +### 在线资源和教程 +1. **Operating System Tutorials on GeeksforGeeks.** - 提供了操作系统概念的详细教程,包括页面置换和调度算法。 +2. **MIT OpenCourseWare.** - 麻省理工学院的公开课资源,可能包含操作系统课程的讲义和实验,涵盖本章要求的算法。 + diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_1.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_1.md new file mode 100644 index 0000000000000000000000000000000000000000..ac79113799e73a9b4094fea3ce7b04a6ba48e924 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_1.md @@ -0,0 +1,72 @@ +# FIFO 页面置换算法 + +## 原理 +FIFO(First-In-First-Out)页面置换算法是一种最简单的页面置换算法。它将最早加载进内存的页面置换出去。换句话说,当需要一个新的页面时,最先进入内存的页面将被移除。 + +## 函数实现 +以下是FIFO页面置换算法的实现: + +```c +#include +#include + +#define MAX_FRAMES 10 + +void fifo_page_replacement(char *queue_frames, int num_frames) { + int frames[MAX_FRAMES]; + int front = 0; + int rear = 0; + int size = 0; + int page_faults = 0; + + for (int i = 0; i < num_frames; i++) { + frames[i] = -1; // 初始化页框为空 + } + + for (int i = 0; i < strlen(queue_frames); i++) { + int page = queue_frames[i] - '0'; + int found = 0; + + // 检查页面是否已经在页框中 + for (int j = 0; j < size; j++) { + if (frames[j] == page) { + found = 1; + break; + } + } + + // 页面不在页框中,需要置换 + if (!found) { + page_faults++; + frames[rear] = page; + rear = (rear + 1) % num_frames; + + // 如果页框未满,增加当前大小 + if (size < num_frames) { + size++; + } + } + + // 打印当前页框状态 + printf("Page %d: [", page); + for (int j = 0; j < size; j++) { + printf("%d", frames[j]); + if (j < size - 1) { + printf(" "); + } + } + printf("]\n"); + } + + printf("Total page faults: %d\n", page_faults); +} + +int main() { + char queue_frames[] = "70120304230321201701"; + int num_frames = 3; + + fifo_page_replacement(queue_frames, num_frames); + + return 0; +} +``` \ No newline at end of file diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_2.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_2.md new file mode 100644 index 0000000000000000000000000000000000000000..a853e5c687a9099b8222ed2fc8610430a34814a2 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_2.md @@ -0,0 +1,89 @@ +# LRU 页面置换算法 + +## 原理 +LRU(Least Recently Used)页面置换算法根据页面最近使用的情况来决定置换哪一个页面。具体来说,它将最久未被使用的页面置换出去。 + +## 函数实现 +以下是LRU页面置换算法的实现: + +```c +#include +#include +#include + +#define MAX_FRAMES 100 + +/** + * 函数:模拟LRU页面置换算法。 + * + * @param queue_frames 一个字符串,表示页面访问序列。 + * 字符串中的每个字符是一个数字,表示一个页面号。 + * @param num_frames 页框的数量,表示物理内存中可用的页框数。 + */ +void lru_page_replacement(char *queue_frames, int num_frames) { + int frames[MAX_FRAMES]; + int counter[MAX_FRAMES]; + int time = 0; + int page_faults = 0; + + for (int i = 0; i < num_frames; i++) { + frames[i] = -1; // 初始化页框为空 + counter[i] = 0; // 初始化计数器 + } + + for (int i = 0; i < strlen(queue_frames); i++) { + int page = queue_frames[i] - '0'; + int found = 0; + + // 检查页面是否已经在页框中 + for (int j = 0; j < num_frames; j++) { + if (frames[j] == page) { + found = 1; + counter[j] = ++time; // 更新页面使用时间 + break; + } + } + + // 页面不在页框中,需要置换 + if (!found) { + page_faults++; + int lru_index = 0; + int lru_time = counter[0]; + + // 找到最久未使用的页面 + for (int j = 1; j < num_frames; j++) { + if (counter[j] < lru_time) { + lru_time = counter[j]; + lru_index = j; + } + } + + frames[lru_index] = page; + counter[lru_index] = ++time; + + // 打印当前页框状态 + printf("Page %d: [", page); + for (int j = 0; j < num_frames; j++) { + printf("%d", frames[j]); + if (j < num_frames - 1) { + printf(" "); + } + } + printf("]\n"); + } + } + + printf("Total page faults: %d\n", page_faults); +} + +int main() { + char queue_frames[] = "70120304230321201701"; + int num_frames = 3; + + lru_page_replacement(queue_frames, num_frames); + + return 0; +} +``` + +这两个函数分别实现了FIFO和LRU页面置换算法。在每个函数中,`queue_frames`是页面访问序列,`num_frames`是可用的页框数。每次访问一个页面时,函数会检查该页面是否在页框中。如果不在,则需要置换一个页面。FIFO算法总是置换最早进入内存的页面,而LRU算法则置换最久未使用的页面。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_3.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_3.md new file mode 100644 index 0000000000000000000000000000000000000000..18cfd759da6120de97acb053308097f364ebb9fa --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-7_3.md @@ -0,0 +1,109 @@ +# 模拟时间片轮转调度算法 + +## 原理 +时间片轮转(Round Robin, RR)是一种广泛使用的进程调度算法。其核心思想是将CPU时间划分成固定长度的时间片(time slices),并按顺序将这些时间片分配给就绪队列中的每个进程。每个进程在其时间片内运行,当时间片用完时,如果进程尚未完成,则将其置于就绪队列的末尾,等待下一轮调度。这样可以确保所有进程都能公平地获得CPU资源。 + +## 进程结构体 + +进程结构体包含以下字段: + +- `pid`:进程ID,用于标识进程。 +- `arrival_time`:到达时间,进程进入系统的时间。 +- `burst_time`:运行时间,进程需要的总CPU时间。 +- `remaining_time`:剩余运行时间,用于记录进程还需要多少时间完成。 +- `completion_time`:完成时间,进程完成执行的时间。 +- `turnaround_time`:周转时间,进程从到达至完成所经历的总时间(完成时间 - 到达时间)。 +- `waiting_time`:等待时间,进程在就绪队列中等待的时间(周转时间 - 运行时间)。 + +## 输入数据 + +时间片长度:4 个时间单位。 +进程数量:4 个进程。 +每个进程的到达时间和运行时间如下表所示: + +| 进程ID | 到达时间 | 运行时间 | +|--------|----------|----------| +| P1 | 0 | 10 | +| P2 | 1 | 6 | +| P3 | 2 | 8 | +| P4 | 3 | 4 | + +## 计算方法 + +模拟时间片轮转调度算法,并计算每个进程的完成时间、周转时间和等待时间。 + +```c +#include + +typedef struct { + int pid; // 进程ID + int arrival_time; // 到达时间 + int burst_time; // 运行时间 + int remaining_time; // 剩余运行时间 + int completion_time; // 完成时间 + int turnaround_time; // 周转时间 + int waiting_time; // 等待时间 +} Process; + +#define TIME_SLICE 4 +#define NUM_PROCESSES 4 + +void calculateTime(Process processes[], int n) { + int time = 0; + int completed = 0; + + while (completed < n) { + for (int i = 0; i < n; i++) { + if (processes[i].arrival_time <= time && processes[i].remaining_time > 0) { + if (processes[i].remaining_time <= TIME_SLICE) { + time += processes[i].remaining_time; + processes[i].remaining_time = 0; + processes[i].completion_time = time; + processes[i].turnaround_time = processes[i].completion_time - processes[i].arrival_time; + processes[i].waiting_time = processes[i].turnaround_time - processes[i].burst_time; + completed++; + } else { + time += TIME_SLICE; + processes[i].remaining_time -= TIME_SLICE; + } + } + } + } +} + +int main() { + Process processes[NUM_PROCESSES] = { + {1, 0, 10, 10, 0, 0, 0}, + {2, 1, 6, 6, 0, 0, 0}, + {3, 2, 8, 8, 0, 0, 0}, + {4, 3, 4, 4, 0, 0, 0} + }; + + calculateTime(processes, NUM_PROCESSES); + + for (int i = 0; i < NUM_PROCESSES; i++) { + printf("P%d 完成时间: %d, 周转时间: %d, 等待时间: %d\n", + processes[i].pid, + processes[i].completion_time, + processes[i].turnaround_time, + processes[i].waiting_time); + } + + return 0; +} +``` + +## 输出结果 +根据输入数据和时间片轮转调度算法的规则,输出结果如下: + +``` +P1 完成时间: 28, 周转时间: 28, 等待时间: 18 +P2 完成时间: 22, 周转时间: 21, 等待时间: 15 +P3 完成时间: 26, 周转时间: 24, 等待时间: 16 +P4 完成时间: 16, 周转时间: 13, 等待时间: 9 +``` + +### 5.1.6结论 +通过时间片轮转调度算法,每个进程都能公平地获得CPU资源,并且算法能够计算出每个进程的完成时间、周转时间和等待时间。这对于实现操作系统中的进程调度是非常重要的。 + + diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/chapter-8.md b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-8.md new file mode 100644 index 0000000000000000000000000000000000000000..6fd5b00e0f2a48c4c3d95a456eaa17fc12d2137b --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/chapter-8.md @@ -0,0 +1,26 @@ +# 习题引导 + +本章节给大家带来各个题目的详细介绍与解释,并给予大家提示以帮助大家更好地理解题目。 + +## 主要内容 + +- [矩阵相乘 - 矩阵](./exercises/22.md) +- [2D卷积操作 - 矩阵](./exercises/23.md) +- [矩阵原地转置 - 矩阵](./exercises/24.md) +- [包含0的行列进行矩阵置零 - 矩阵](./exercises/25.md) +- [查找矩阵中第K个最小的元素 - 矩阵](./exercises/26.md) +- [编写一个内核模块求最大值 - 内核模块](./exercises/36.md) +- [编写一个内核模块启动一个定时器 - 内核模块](./exercises/37.md) +- [编写一个内核模块创建一个虚拟字符设备 - 内核模块](./exercises/38.md) +- [编写一个内核模块实现一个简单的文件操作函数 - 内核模块](./exercises/39.md) +- [内联汇编实现斐波那契数列 - RISC-V基础指令](./exercises/43.md) +- [内联汇编实现整数数组求和 - RISC-V基础指令](./exercises/44.md) +- [内联汇编实现查找数组最大值 - RISC-V基础指令](./exercises/45.md) +- [内联汇编实现判断数组是否有序 - RISC-V基础指令](./exercises/46.md) +- [内联汇编实现数组目标元素个数 - RISC-V基础指令](./exercises/47.md) +- [模拟FIFO页面置换算法 - 操作系统](./exercises/50.md) +- [模拟LRU页面置换算法 - 操作系统](./exercises/51.md) +- [获取容器主机名 - 操作系统](./exercises/52.md) +- [简单虚拟地址到物理地址的转换 - 操作系统](./exercises/53.md) +- [模拟时间片轮转调度算法 - 操作系统](./exercises/54.md) +- [模拟虚拟内存限制 - 操作系统](./exercises/55.md) diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/22.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/22.md new file mode 100644 index 0000000000000000000000000000000000000000..a1a1144a8d0fe19fcdcbe63f6638a2e326266345 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/22.md @@ -0,0 +1,56 @@ +# 矩阵相乘 + +矩阵相乘是线性代数中的一项基本操作,它在图像处理、机器学习、物理模拟等多个领域都有广泛的应用。在本题目中,参与者需要实现一个矩阵相乘的功能,并将结果矩阵输出。 + +此题可参照[第四章 矩阵与卷积算法](../chapter-4.md)中的[矩阵与线性代数](../chapter-4_1.md) + +## 题目要求 + +- 实现一个名为 `multiply` 的函数,用于计算两个矩阵的乘积。 +- 矩阵乘函数由以下结构表示: + ```c + int **multiply(int **A, int ASize, int *AColSize, int **B, int BSize, int *BColSize, int *returnSize, int **returnColumnSizes); + ``` + +## 示例 + +假设有两个矩阵 A 和 B,调用 `multiply` 函数后,返回的新矩阵应该是: +``` +30 24 18 +84 69 54 +138 114 90 +``` + +## 输入 + +- 两个矩阵 A 和 B,每个矩阵由逗号分隔的数值表示,例如: + ``` + 矩阵 A:1,2,3,4,5,6,7,8,9 + 矩阵 B:9,8,7,6,5,4,3,2,1 + ``` + +## 输出 + +- 打印相乘后的矩阵,例如: + ``` + Result: 30 24 18 84 69 54 138 114 90 + ``` + +## 代码介绍 + +1. **main.c** 文件包含了主函数,用于解析命令行参数,分配矩阵内存,调用 `multiply` 函数,并打印结果矩阵。 + +2. **matrix_mul.c** 文件包含了 `multiply` 函数的实现,以及一个辅助函数 `printMatrix` 用于打印矩阵。 + +## 代码提示 + +- 在 `main.c` 中,使用 `parseMatrix` 函数解析字符串形式的矩阵到整数指针数组。 +- `multiply` 函数在 `matrix_mul.c` 中实现,但似乎存在问题,因为它没有实现矩阵相乘的逻辑,只是进行了内存分配和返回了结果矩阵的列数。 +- `printMatrix` 函数用于打印矩阵,它接受矩阵的指针、行数和列数作为参数。 + +## 注意事项 + +- 确保 `multiply` 函数正确实现了矩阵相乘的逻辑。 +- 检查内存分配是否正确,避免内存泄漏。 +- 确保输入的矩阵符合题目要求的维度,以便正确进行矩阵乘法。 +- 参与者需要根据题目要求,完成 `multiply` 函数的实现,并确保代码的正确性和效率。同时,需要注意内存管理,避免在程序中引入内存泄漏。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/23.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/23.md new file mode 100644 index 0000000000000000000000000000000000000000..0a313a63c02542a0078a0840543b852ddb49d045 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/23.md @@ -0,0 +1,84 @@ +# 2D 卷积操作 + +2D 卷积操作是图像处理和计算机视觉中的一项关键技术,广泛应用于图像滤波、特征提取等场景。本题目要求参与者实现一个2D卷积函数,用于计算输入矩阵与卷积核的卷积结果。 + +此题可参照[第四章 矩阵与卷积算法](../chapter-4.md)中的[卷积算法](../chapter-4_2.md) + +## 题目要求 + +- 实现 `convolution2D` 函数,计算输入矩阵与卷积核的卷积。 +- 函数定义: + ```c + void convolution2D(int input[5][5], int kernel[3][3], int output[3][3], int inputSize, int kernelSize); + ``` + +## 示例 + +- 输入矩阵: + ``` + 1 2 3 4 5 + 6 7 8 9 10 + 11 12 13 14 15 + 16 17 18 19 20 + 21 22 23 24 25 + ``` +- 卷积核: + ``` + 1 0 -1 + 0 -1 0 + -1 0 1 + ``` +- 调用 `convolution2D(input, kernel, output, 5, 3)` 后,输出矩阵应为: + ``` + -6 -6 -6 + -6 -6 -6 + -6 -6 -6 + ``` + +## 输入 + +- 输入矩阵和卷积核矩阵,以逗号分隔的数值表示,例如: + ``` + 输入矩阵:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 + 卷积核矩阵:1,0,-1,1,0,-1,1,0,-1 + ``` + +## 输出 + +- 打印卷积后的矩阵,例如: + ``` + Result: + -6 -6 -6 + -6 -6 -6 + -6 -6 -6 + ``` + +## 代码介绍 + +1. **convolution.c** 文件包含 `convolution2D` 函数的实现,但当前代码仅完成了初始化输出矩阵和卷积核半径的计算,卷积运算尚未实现。 + +2. **main.c** 文件包含主函数,用于解析命令行参数,调用 `convolution2D` 函数,并打印卷积结果。 + +## 详细提示信息 + +- **convolution2D 函数实现**: + - 计算卷积核半径 `kernelRadius`。 + - 初始化输出矩阵 `output` 为全零。 + - 通过嵌套循环遍历输入矩阵的每个元素,应用卷积核进行卷积运算。 + +- **main 函数解析**: + - 检查命令行参数数量。 + - 使用 `parseMatrix` 函数解析输入矩阵和卷积核。 + - 调用 `convolution2D` 函数执行卷积运算。 + - 打印卷积结果。 + +- **parseMatrix 函数**: + - 使用 `strtok` 函数解析字符串,将数值转换为整数并填充到矩阵中。 + +## 注意事项 + +- 确保 `convolution2D` 函数正确实现卷积运算逻辑。 +- 注意卷积核中心与输入矩阵对应位置的对齐方式。 +- 考虑边界条件,确保卷积核不会超出输入矩阵边界。 +- 避免内存泄漏,合理分配和释放内存。 +- 参与者需要根据题目要求,完成 `convolution2D` 函数的实现,确保卷积运算的正确性。同时,注意代码的健壮性和内存管理。通过解析命令行参数和打印结果,展示卷积运算的实际效果 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/24.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/24.md new file mode 100644 index 0000000000000000000000000000000000000000..5a3da6161aac47de1667fbd1bd564e7b03a5bbad --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/24.md @@ -0,0 +1,63 @@ +# 矩阵原地转置 + +矩阵的原地转置是指在不使用额外存储空间的情况下,直接将一个矩阵转置。对于一个 \( N \times N \) 的矩阵,原地转置意味着交换对角线元素以及对称位置的元素,空间复杂度为 \( O(1) \)。 + +此题可参考[第四章 矩阵与卷积算法](../chapter-4.md)中的[矩阵转置](../chapter-4_3.md)。 + +## 题目要求 + +- 实现 `transposeInPlace` 函数,对一个 \( N \times N \) 的矩阵进行原地转置。 + +## 示例 + +假设输入矩阵为: +``` +1 2 3 +4 5 6 +7 8 9 +``` + +执行原地转置后,输出矩阵应为: +``` +Transposed Matrix: +1 4 7 +2 5 8 +3 6 9 +``` + +## 输入 + +- 一个 \( N \times N \) 的矩阵,以字符串形式输入,矩阵中的元素由逗号分隔。 + +## 输出 + +- 转置后的矩阵,每行元素由空格分隔。 + +## 代码介绍 + +1. **matrix_trans.c** 文件包含 `transposeInPlace` 函数的框架,但当前尚未实现转置逻辑。 + +2. **main.c** 文件包含主函数,用于解析命令行参数,调用 `transposeInPlace` 函数,并打印转置后的矩阵。 + +## 详细提示信息 + +- **transposeInPlace 函数实现**: + - 需要交换矩阵对角线元素以及对称位置的元素。 + - 使用两个嵌套循环遍历矩阵,对于每个元素,如果其行索引和列索引之和不等于 \( N - 1 \),则与其对称元素交换。 + +- **main 函数解析**: + - 检查命令行参数数量。 + - 使用 `parseMatrix` 函数解析输入的矩阵字符串。 + - 调用 `transposeInPlace` 函数执行原地转置。 + - 打印转置后的矩阵。 + +- **parseMatrix 函数**: + - 解析输入的矩阵字符串,将字符串中的数值填充到矩阵中。 + - 注意处理字符串中的逗号和空格。 + +## 注意事项 + +- 确保 `transposeInPlace` 函数正确实现原地转置逻辑,不使用额外存储空间。 +- 考虑矩阵的对称性质,避免重复交换元素。 +- 确保 `parseMatrix` 函数能够正确解析各种格式的输入字符串。 +- 参与者需要根据题目要求,完成 `transposeInPlace` 函数的实现,确保矩阵能够正确原地转置。同时,注意代码的健壮性和正确性。通过解析命令行参数和打印结果,展示原地转置的实际效果。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/25.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/25.md new file mode 100644 index 0000000000000000000000000000000000000000..5d394809a578ffbf459c9e6583519194cbf5378d --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/25.md @@ -0,0 +1,69 @@ +# 包含 0 的行列进行矩阵置零 + +## 题目介绍 + +本题目要求实现一个算法,用于将矩阵中所有值为0的元素所在的行和列都置为0。这是一个常见的问题,通常出现在面试和算法竞赛中,考察对矩阵操作和空间复杂度的理解。 + +## 题目要求 + +- 实现 `setZeroes` 函数,该函数接收一个矩阵的指针、矩阵的大小以及每行的列数数组,将矩阵中值为0的元素所在的行和列都置为0。 + +## 输入 + +- 矩阵的大小 `matrixSize`。 +- 每行的列数数组 `matrixColSize`。 +- 矩阵的值,以一维数组的形式提供。 + +## 输出 + +- 修改后的矩阵,其中所有值为0的元素所在的行和列都被置为0。 + +## 示例 + +假设输入矩阵为: +``` +1 0 3 +4 5 0 +7 8 9 +``` + +执行 `setZeroes` 函数后,输出矩阵应为: +``` +1 0 0 +0 5 0 +7 0 9 +``` + +## 代码介绍 + +1. **main.c** 文件包含主函数,用于处理命令行输入,分配矩阵内存,调用 `setZeroes` 函数,并打印原始和修改后的矩阵。 + +2. **matrix_zero.c** 文件包含 `setZeroes` 函数的框架,以及辅助函数 `printMatrix` 和 `freeMatrix`。 + +## 详细提示信息 + +- **setZeroes 函数实现**: + - 使用额外的数组 `row` 和 `col` 来记录哪些行和列需要被置0。 + - 遍历矩阵,标记 `row` 和 `col` 中相应的元素。 + - 根据 `row` 和 `col` 的标记,将矩阵中相应的行和列置0。 + +- **main 函数解析**: + - 检查命令行参数数量是否正确。 + - 分配内存给 `matrixColSize` 和 `matrix`。 + - 解析命令行参数,填充矩阵值。 + - 调用 `setZeroes` 函数。 + - 打印原始和修改后的矩阵。 + - 释放分配的内存。 + +- **printMatrix 函数**: + - 打印矩阵的当前状态。 + +- **freeMatrix 函数**: + - 释放矩阵占用的内存。 + +## 注意事项 + +- 确保 `setZeroes` 函数在修改矩阵前正确记录了需要置0的行和列。 +- 注意内存分配和释放,避免内存泄漏。 +- 考虑空间复杂度,尽量减少额外空间的使用。 +- 参与者需要根据题目要求,完成 `setZeroes` 函数的实现,确保能够正确地将矩阵中为0的元素所在的行和列都置为0。同时,注意代码的健壮性和内存管理。通过命令行参数和打印结果,展示算法的实际效果。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/26.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/26.md new file mode 100644 index 0000000000000000000000000000000000000000..f90aeb4eae663409f84c5fac99089acb6c1af793 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/26.md @@ -0,0 +1,66 @@ +# 查找矩阵中第 K 个最小的元素 + +## 题目介绍 + +本题目要求实现一个算法,用于找出给定矩阵中的第 \( k \) 小的元素。矩阵是一个由不同长度的行组成的二维数组,这种类型的矩阵问题在编程和算法设计中很常见,特别是在处理大数据集时。 + +## 题目要求 + +- 实现 `kthSmallest` 函数,该函数接收矩阵的指针、矩阵的大小、每行的列数数组以及整数 \( k \),返回矩阵中的第 \( k \) 小的元素。 + +## 输入 + +- 矩阵的大小 `matrixSize`。 +- 整数 \( k \),表示需要找的第 \( k \) 小元素的索引(从1开始)。 +- 每行的列数数组 `matrixColSize`。 +- 矩阵的值,以一维数组的形式提供。 + +## 输出 + +- 第 \( k \) 小的元素值。 + +## 示例 + +假设输入矩阵为: +``` +5 3 +1 4 2 +6 7 8 +``` + +对于 \( k = 5 \)(即我们要找的第5小的元素),输出应为: +``` +The 5th smallest element in the matrix is: 4 +``` + +## 代码介绍 + +1. **main.c** 文件包含主函数,用于处理命令行输入,分配矩阵内存,调用 `kthSmallest` 函数,并打印原始矩阵和第 \( k \) 小的元素。 + +2. **matrix_kmin.c** 文件包含 `kthSmallest` 函数的框架,以及辅助数据结构最小堆(MinHeap)的实现。 + +## 详细提示信息 + +- **kthSmallest 函数实现**: + - 使用最小堆来存储矩阵中的元素,以找到第 \( k \) 小的元素。 + - 遍历矩阵,将每个元素插入到最小堆中。 + - 从最小堆中提取前 \( k - 1 \) 个元素,堆顶剩下的元素即为所求。 + +- **main 函数解析**: + - 检查命令行参数数量是否正确。 + - 分配内存给 `matrixColSize` 和 `matrix`。 + - 解析命令行参数,填充矩阵值。 + - 调用 `kthSmallest` 函数。 + - 打印原始矩阵和第 \( k \) 小的元素。 + - 释放分配的内存。 + +- **MinHeap 数据结构**: + - 实现了一个最小堆,用于存储和操作矩阵中的元素。 + - 提供了 `createMinHeap`、`minHeapify`、`extractMin` 和 `insertMinHeap` 等函数。 + +## 注意事项 + +- 确保 `kthSmallest` 函数正确实现,使用最小堆来找到第 \( k \) 小的元素。 +- 注意内存分配和释放,避免内存泄漏。 +- 考虑时间复杂度和空间复杂度,尽量减少不必要的操作。 +- 参与者需要根据题目要求,完成 `kthSmallest` 函数的实现,确保能够正确地找出矩阵中的第 \( k \) 小的元素。同时,注意代码的健壮性和内存管理。通过命令行参数和打印结果,展示算法的实际效果。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/36.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/36.md new file mode 100644 index 0000000000000000000000000000000000000000..645d511b4a9951dc59aae5d7ca410fed7dddd8bb --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/36.md @@ -0,0 +1,65 @@ +# 编写一个内核模块求最大值 + +## 题目介绍 + +本题目要求编写一个Linux内核模块,该模块能够接收一个整数数组作为输入,计算并输出数组中所有元素的最大值。这是一个很好的练习,可以帮助理解Linux内核模块的编写和基本的数组操作。 + +本题可参考[第五章 Linux内核模块](../chapter-5.md)中的[编写一个简单的内核模块](../chapter-5_1.md)与[内核模块传参](../chapter-5_2.md) + +## 题目要求 + +- 编写一个内核模块。 +- 接收一个以逗号分隔的整数字符串作为输入。 +- 计算输入数组中的最大值。 +- 输出结果。 + +## 输入 + +- 一个以逗号分隔的整数字符串,例如:"8,3,12,6,20" + +## 输出 + +- 数组中的最大值,对于上述输入,输出应为:20 + +## 示例 + +- 输入:[8, 3, 12, 6, 20] +- 输出:20 + +## 代码介绍 + +1. **max_value.c** 文件包含了内核模块的实现。 + - 定义了模块参数 `input_str`,用于接收输入的字符串。 + - 实现了 `parse_input` 函数,用于解析字符串并填充到整数数组 `arr` 中。 + - 实现了 `max_value` 函数,用于找出数组中的最大值,但当前实现尚未完成。 + +2. 模块初始化函数 `max_init` 在模块加载时执行,调用 `parse_input` 和 `max_value` 函数,并打印最大值。 + +3. 模块退出函数 `max_exit` 在模块卸载时执行,打印卸载信息。 + +## 详细提示信息 + +- **max_value 函数实现**: + - 需要遍历整个数组,使用一个变量记录最大值。 + - 与数组中的每个元素比较,并更新最大值。 + +- **parse_input 函数**: + - 使用 `strsep` 和 `kstrtoint` 函数解析字符串。 + - 将解析后的整数填充到数组 `arr` 中,并记录数组大小 `arr_size`。 + +- **模块参数**: + - 使用 `module_param` 定义了 `input_str` 参数。 + - 使用 `MODULE_PARM_DESC` 提供了参数的描述。 + +- **内存管理**: + - 使用 `kstrdup` 和 `kfree` 管理内存,避免内存泄漏。 + +- **错误处理**: + - 对于输入字符串为空或解析失败的情况,模块初始化函数返回 `-EINVAL`。 + +## 注意事项 + +- 确保 `max_value` 函数正确实现最大值查找逻辑。 +- 注意模块参数的使用和内存管理。 +- 考虑使用内核日志函数 `printk` 进行适当的错误和信息输出。 +- 参与者需要根据题目要求,完成 `max_value` 函数的实现,确保内核模块能够正确地找出并输出数组中的最大值。同时,注意代码的健壮性和内存管理。通过内核模块的编写和测试,加深对Linux内核模块开发的理解。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/37.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/37.md new file mode 100644 index 0000000000000000000000000000000000000000..98bd43397315159609bdfa5e9db6186c24e54784 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/37.md @@ -0,0 +1,63 @@ +# 编写一个内核模块启动一个定时器 + +## 题目介绍 + +本题目要求参与者编写一个Linux内核模块,该模块使用内核定时器(timer)来实现周期性的任务执行。内核定时器是Linux内核提供的一种机制,允许在指定的时间后执行某些操作,或者按照一定的时间间隔重复执行操作。 + +本题可参考[第五章 Linux内核模块](../chapter-5.md)中的[实现计时器](../chapter-5_4.md) + +## 题目要求 + +- 编写一个内核模块,使用内核定时器实现周期性执行。 +- 定时器应该在模块加载后5秒触发,并在每次触发时打印当前的jiffies(系统启动以来的时钟节拍数)。 +- 每次触发后,定时器应重新设置为再次5秒后触发。 + +## 输入 + +无特定输入要求。 + +## 输出 + +- 定时器每次触发时,打印一条包含当前jiffies的消息。 + +## 示例 + +- 触发示例消息: + ``` + Timer callback function called [jiffies值]. + ``` + +## 代码介绍 + +1. **timer.c** 文件包含了内核模块的实现。 + - 定义了一个 `struct timer_list` 名为 `my_timer`,用于管理定时器。 + - 实现了 `my_timer_callback` 函数,该函数在定时器触发时被调用,并打印当前的jiffies值。 + - 设置了 `my_timer_callback` 函数为 `my_timer` 的回调函数。 + +2. 模块初始化函数 `timer_init` 在模块加载时执行,需要在这里设置定时器。 + +3. 模块退出函数 `timer_exit` 在模块卸载时执行,删除定时器并打印卸载信息。 + +## 详细提示信息 + +- **定时器设置**: + - 使用 `mod_timer` 函数设置定时器的触发时间。 + - 定时器首次设置需要在 `timer_init` 函数中完成,并在5秒后触发。 + - 在 `my_timer_callback` 函数中重新设置定时器,以实现周期性触发。 + +- **模块初始化和退出**: + - 在 `timer_init` 中设置定时器并打印加载信息。 + - 在 `timer_exit` 中删除定时器并打印卸载信息。 + +- **内核日志**: + - 使用 `printk` 函数打印内核日志信息。 + +- **错误处理**: + - 目前代码中没有错误处理,但实际编写时应注意可能的错误情况。 + +## 注意事项 + +- 确保定时器正确设置并能够周期性触发。 +- 注意模块的初始化和退出函数中定时器的设置和删除。 +- 使用内核日志函数 `printk` 进行适当的信息输出。 +- 参与者需要根据题目要求,完成定时器的设置和周期性任务的实现。通过编写内核模块,加深对Linux内核定时器机制的理解,并掌握内核模块的编写和测试流程。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/38.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/38.md new file mode 100644 index 0000000000000000000000000000000000000000..cde269ec3a3928d28d1fb58bdae9a796050cf099 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/38.md @@ -0,0 +1,69 @@ +# 编写一个内核模块创建一个虚拟字符设备 + +## 题目介绍 + +本题目要求编写一个Linux内核模块,用于创建一个虚拟字符设备。当用户读取这个设备时,它会返回一个固定的字符串。这个练习有助于理解Linux内核模块的编写,特别是字符设备驱动的创建和使用。 + +本题可参考[第五章 Linux内核模块](../chapter-5.md)中的[创建字符设备](../chapter-5_3.md) + +## 题目要求 + +- 编写一个内核模块,创建一个虚拟字符设备。 +- 当用户读取该设备时,返回预设的字符串。 + +## 输入 + +无特定输入要求。 + +## 输出 + +- 当用户读取设备时,输出字符串 "hello"。 + +## 示例 + +- 设备读取示例输出: + ``` + hello + ``` + +## 代码介绍 + +1. **chardev.c** 文件包含了内核模块的实现。 + - 定义了设备名称 `DEVICE_NAME` 和默认消息 `DEFAULT_MESSAGE`。 + - 使用 `module_param` 定义了模块参数 `message`,允许在加载模块时传入自定义消息。 + +2. 实现了以下函数: + - `dev_open`:设备打开时调用,重置消息指针。 + - `dev_release`:设备关闭时调用,打印关闭信息。 + - `dev_read`:从设备读取数据时调用,需要实现读取逻辑。 + +3. 定义了 `file_operations` 结构体 `fops`,包含设备的操作方法。 + +4. 模块初始化函数 `chardev_init` 用于注册字符设备和打印相关信息。 + +5. 模块退出函数 `chardev_exit` 用于注销字符设备。 + +## 详细提示信息 + +- **字符设备注册**: + - 使用 `register_chrdev` 函数注册字符设备,获取主设备号 `major`。 + +- **模块参数**: + - 使用 `module_param` 定义了 `message` 参数,允许用户在加载模块时通过 `insmod` 命令传入自定义消息。 + +- **文件操作**: + - 实现 `dev_open` 函数,设备打开时重置消息指针。 + - 实现 `dev_read` 函数,从设备读取消息。需要使用 `vsscanf` 或类似方法将 `message_ptr` 指向的字符串复制到用户空间的 `buffer` 中,并返回读取的字节数。 + +- **内存管理**: + - 注意不要直接将用户空间的指针与内核空间的指针相互赋值,应使用适当的内存访问函数。 + +- **错误处理**: + - 在 `chardev_init` 中检查 `register_chrdev` 的返回值,如果注册失败,打印错误信息并返回错误代码。 + +## 注意事项 + +- 确保 `dev_read` 函数正确实现,能够返回预期的字符串。 +- 注意模块参数的使用和字符设备的注册与注销。 +- 使用内核日志函数 `printk` 进行适当的信息输出。 +- 参与者需要根据题目要求,完成虚拟字符设备的实现,确保能够通过模块参数传入消息,并在用户读取设备时返回该消息。通过编写内核模块,加深对Linux内核字符设备驱动的理解,并掌握内核模块的编写和测试流程。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/39.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/39.md new file mode 100644 index 0000000000000000000000000000000000000000..0c717a2a5b44ccb3360794a35ea187db0c62e7a0 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/39.md @@ -0,0 +1,57 @@ +# 编写一个内核模块实现一个简单的文件操作函数 + +## 题目介绍 + +本题目要求编写一个Linux内核模块,实现对文件的基本操作,包括打开、读取、写入和关闭文件。这是一个基础练习,有助于理解Linux内核模块中文件操作的实现方式。 + +本题可参考[第五章 Linux内核模块](../chapter-5.md)中的[实现简单文件操作](../chapter-5_5.md) + +## 题目要求 + +- 编写一个内核模块。 +- 实现文件的打开、读取、写入、关闭操作。 +- 在控制台输出操作结果。 + +## 输入 + +无特定输入要求。 + +## 输出 + +- 成功打开文件时输出:`File operation successful` +- 打开失败时输出:`Failed to open file` + +## 代码介绍 + +1. **fileop.c** 文件包含了内核模块的实现。 + - 定义了文件名 `FILE_NAME` 和用于操作的文件指针 `file_ptr`。 + +2. 模块初始化函数 `fileop_init` 在模块加载时执行,需要在这里实现文件操作逻辑。 + +3. 模块退出函数 `fileop_exit` 在模块卸载时执行,关闭文件并打印信息。 + +## 详细提示信息 + +- **文件操作**: + - 使用 `filp_open` 函数尝试打开文件,该函数需要文件名、访问模式和其他标志。 + - 检查 `filp_open` 的返回值,如果成功,将返回的 `struct file *` 指针赋值给 `file_ptr`。 + +- **读取和写入文件**: + - 如果需要,使用 `vfs_read` 或 `vfs_write` 函数实现文件的读取和写入操作。 + +- **错误处理**: + - 如果文件打开失败,打印错误信息。 + +- **模块初始化和退出**: + - 在 `fileop_init` 中实现文件操作,并根据操作结果打印相应信息。 + - 在 `fileop_exit` 中检查 `file_ptr` 是否非空,如果是,则调用 `filp_close` 关闭文件,并打印关闭信息。 + +- **内核日志**: + - 使用 `printk` 函数根据不同情况打印内核日志信息。 + +## 注意事项 + +- 确保正确处理文件打开操作的成功与失败。 +- 注意模块的初始化和退出函数中文件操作的完整性。 +- 使用内核日志函数 `printk` 进行适当的信息输出。 +- 参与者需要根据题目要求,完成文件操作的实现,确保能够正确打开文件并在适当的时候关闭文件。通过编写内核模块,加深对Linux内核文件操作的理解,并掌握内核模块的编写和测试流程。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/43.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/43.md new file mode 100644 index 0000000000000000000000000000000000000000..03da9f9c8c2ac96941c75169fc21e92b71d53d66 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/43.md @@ -0,0 +1,65 @@ +# 使用内联 RISCV 汇编实现计算斐波那契数列的第 n 个数 + +## 题目介绍 + +本题目要求参与者使用RISC-V汇编语言内联在C程序中,实现计算斐波那契数列第n项的函数。斐波那契数列是一个每一项都是前两项和的序列,通常定义为F(0) = 0, F(1) = 1, 且对于n > 1, 有 F(n) = F(n-1) + F(n-2)。 + +本题可参考[第六章 RISC-V架构内联汇编](../chapter-6.md)全部小节内容。 + +## 题目要求 + +- 使用RISC-V汇编语言内联在C函数中。 +- 实现计算斐波那契数列第n项的值。 +- 编写的汇编代码需要替换C函数中的PLACEHOLDER。 + +## 输入 + +- 命令行参数,一个整数n,表示斐波那契数列的位置。 + +## 输出 + +- 斐波那契数列第n项的值。 + +## 示例 + +- 如果输入是 `5`,则输出应该是 `5`,因为斐波那契数列的第5项是5。 + +## 代码介绍 + +1. **main.c** 文件包含了主函数和 `fibonacci` 函数的C包装器。 + - `main` 函数处理命令行参数,并调用 `fibonacci` 函数打印结果。 + - `fibonacci` 函数使用内联汇编实现,其中包含了多个PLACEHOLDER,需要用正确的RISC-V汇编指令替换。 + +2. 内联汇编使用GCC的扩展语法,其中: + - `"li"` 指令用于将立即数加载到寄存器。 + - `"addi"` 指令用于将一个立即数加到寄存器。 + - `"add"` 指令用于将两个寄存器的值相加。 + - `"mv"` 指令用于将一个寄存器的值移动到另一个寄存器。 + - `"bne"` 指令用于比较两个寄存器,如果不相等则跳转到指定标签。 + - `"j"` 指令用于无条件跳转。 + +## 详细提示信息 + +- **内联汇编替换**: + - 使用RISC-V汇编指令替换PLACEHOLDER,完成斐波那契数列的计算。 + - 对于 `n == 0`,应返回0,使用 `"li %0, 0"` 并跳转至结束标签。 + - 对于 `n == 1`,应返回1,使用 `"li %0, 1"` 并跳转至结束标签。 + - 在循环中,使用 `"add"` 更新斐波那契数列的值,并使用 `"mv"` 或 `"addi"` 更新寄存器。 + +- **控制流**: + - 使用 `"bne"` 指令控制循环,直到 `n` 减至0。 + +- **寄存器使用**: + - `%0` 用于存储结果。 + - `%1` 用于存储参数n,并在循环中递减。 + - `t0`, `t1`, `t2` 为临时寄存器,用于计算。 + +- **编译与测试**: + - 使用 `gcc` 编译器编译程序,并确保RISC-V汇编语法正确。 + +## 注意事项 + +- 确保替换的汇编指令逻辑正确,能够正确计算斐波那契数列的值。 +- 注意内联汇编的语法和寄存器使用。 +- 确保程序能够正确处理命令行参数。 +- 参与者需要根据题目要求,使用RISC-V汇编语言完成斐波那契数列的计算。通过这个练习,可以加深对RISC-V汇编语言和内联汇编使用的理解。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/44.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/44.md new file mode 100644 index 0000000000000000000000000000000000000000..15b19fa6cfea92db1c0a418b4e3422fb1ee462e0 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/44.md @@ -0,0 +1,66 @@ +# 使用内联 RISCV 汇编实现整数数组求和 + +## 题目介绍 + +本题目要求参与者编写一个C语言函数 `calculate_sum`,该函数使用内联RISC-V汇编来计算一个整数数组中所有元素的和。然后,编写一个程序调用该函数并输出结果。 + +本题可参考[第六章 RISC-V架构内联汇编](../chapter-6.md)全部小节内容。 + +## 题目要求 + +- 实现一个C语言函数 `calculate_sum`,使用内联RISC-V汇编。 +- 函数接受一个整数数组 `arr` 和数组的长度 `n`。 +- 计算并返回数组中所有元素的和。 + +## 输入 + +- 一个整数数组 `arr`,以逗号分隔,例如 `1,2,3,4,5`。 +- 一个整数 `n`,表示数组的长度。 + +## 输出 + +- 按照格式 `Sum of array elements is result` 输出,其中 `result` 是数组中所有元素的和。 + +## 示例 + +- 如果输入数组是 `1,2,3,4,5` 且 `n = 5`,则输出应该是 `Sum of array elements is 15`。 + +## 代码介绍 + +1. **main.c** 文件包含了主函数和 `calculate_sum` 函数的C包装器。 + - `main` 函数处理命令行参数,解析数组字符串,并调用 `calculate_sum` 函数打印结果。 + - `calculate_sum` 函数使用内联汇编实现,其中包含了多个PLACEHOLDER,需要用正确的RISC-V汇编指令替换。 + +2. 内联汇编使用GCC的扩展语法,其中: + - `"li"` 指令用于将立即数加载到寄存器。 + - `"lw"` 指令用于从内存加载值到寄存器。 + - `"addi"` 指令用于将一个立即数加到寄存器。 + - `"add"` 指令用于将两个寄存器的值相加。 + - `"mv"` 指令用于将一个寄存器的值移动到另一个寄存器。 + - `"bge"` 指令用于比较两个寄存器,并在第一个大于等于第二个时跳转。 + +## 详细提示信息 + +- **内联汇编替换**: + - 使用RISC-V汇编指令替换 `calculate_sum` 函数中的PLACEHOLDER。 + - 对于循环结束条件,使用 `"bge"` 指令比较索引寄存器 `t0` 和 `n` 寄存器。 + - 使用 `"lw"` 从数组中加载元素到临时寄存器 `t2`。 + - 使用 `"add"` 将 `t2` 的值累加到 `result` 寄存器 `t1`。 + - 使用 `"addi"` 将数组指针 `arr` 的地址递增以指向下一个元素。 + +- **数组解析**: + - 在 `main` 函数中,使用 `strtok` 函数解析逗号分隔的数组字符串。 + +- **错误处理**: + - 检查命令行参数的数量和格式。 + - 确保输入的数组元素数量与 `n` 匹配。 + +- **编译与测试**: + - 使用 `gcc` 编译器编译程序,并确保RISC-V汇编语法正确。 + +## 注意事项 + +- 确保替换的汇编指令逻辑正确,能够正确计算数组的和。 +- 注意内联汇编的语法和寄存器使用。 +- 确保程序能够正确处理命令行参数和数组解析。 +- 参与者需要根据题目要求,使用RISC-V汇编语言完成数组求和的计算。通过这个练习,可以加深对RISC-V汇编语言和内联汇编使用的理解,并掌握如何在C语言中嵌入汇编代码来优化性能关键部分。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/45.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/45.md new file mode 100644 index 0000000000000000000000000000000000000000..8139c3564d3ef0195c8bdf23ff3f81443e8a513f --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/45.md @@ -0,0 +1,63 @@ +# 使用内联 RISCV 汇编实现查找整数数组最大值 + +## 题目介绍 + +本题目要求参与者编写一个C语言函数 `find_max`,该函数使用内联RISC-V汇编来找出一个整数数组中的最大值。然后,编写一个程序调用该函数并输出结果。 + +本题可参考[第六章 RISC-V架构内联汇编](../chapter-6.md)全部小节内容。 + +## 题目要求 + +- 实现一个C语言函数 `find_max`,使用内联RISC-V汇编。 +- 函数接受一个整数数组 `arr` 和数组的长度 `n`。 +- 找出并返回数组中的最大值。 + +## 输入 + +- 一个整数数组 `arr`,以逗号分隔,例如 `10,20,30,40`。 +- 一个整数 `n`,表示数组的长度。 + +## 输出 + +- 按照格式 `Maximum element in array is result` 输出,其中 `result` 是数组中的最大值。 + +## 示例 + +- 如果输入数组是 `10,20,30,40` 且 `n = 4`,则输出应该是 `Maximum element in array is 40`。 + +## 代码介绍 + +1. **main.c** 文件包含了主函数和 `find_max` 函数的C包装器。 + - `main` 函数处理命令行参数,解析数组字符串,并调用 `find_max` 函数打印最大值。 + - `find_max` 函数使用内联汇编实现,其中包含了多个PLACEHOLDER,需要用正确的RISC-V汇编指令替换。 + +2. 内联汇编使用GCC的扩展语法,其中: + - `"li"` 指令用于将立即数加载到寄存器。 + - `"lw"` 指令用于从内存加载值到寄存器。 + - `"mv"` 指令用于将一个寄存器的值移动到另一个寄存器。 + - `"bge"` 指令用于在比较操作中,当大于或等于时跳转。 + +## 详细提示信息 + +- **内联汇编替换**: + - 使用RISC-V汇编指令替换 `find_max` 函数中的PLACEHOLDER。 + - 将 `max` 初始化为数组的第一个元素值,使用 `"lw"` 指令从数组加载并使用 `"mv"` 移动到 `t1`。 + - 在循环中,使用 `"lw"` 从数组中加载当前元素到 `t2`,使用 `"bge"` 判断并可能更新 `max`。 + - 使用 `"addi"` 更新数组指针 `arr` 和循环计数器 `t0`。 + +- **数组解析**: + - 在 `main` 函数中,使用 `strtok` 函数解析逗号分隔的数组字符串。 + +- **错误处理**: + - 检查命令行参数的数量和格式。 + - 确保输入的数组元素数量与 `n` 匹配。 + +- **编译与测试**: + - 使用 `gcc` 编译器编译程序,并确保RISC-V汇编语法正确。 + +## 注意事项 + +- 确保替换的汇编指令逻辑正确,能够正确找出数组的最大值。 +- 注意内联汇编的语法和寄存器使用。 +- 确保程序能够正确处理命令行参数和数组解析。 +- 参与者需要根据题目要求,使用RISC-V汇编语言完成找出数组最大值的计算。通过这个练习,可以加深对RISC-V汇编语言和内联汇编使用的理解,并掌握如何在C语言中嵌入汇编代码来优化性能关键部分。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/46.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/46.md new file mode 100644 index 0000000000000000000000000000000000000000..7a33dc3e26fcfd3781531aba07ec56da43431b24 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/46.md @@ -0,0 +1,66 @@ +# 使用内联 RISCV 汇编实现判断给定数组是否有序 + +## 题目介绍 + +本题目要求参与者编写一个C语言函数 `is_sorted`,该函数使用内联RISC-V汇编来检查一个整数数组是否按非降序排列。接着,编写一个程序调用该函数并根据结果输出相应的信息。 + +本题可参考[第六章 RISC-V架构内联汇编](../chapter-6.md)全部小节内容。 + +## 题目要求 + +- 实现C语言函数 `is_sorted`,使用内联RISC-V汇编。 +- 函数接收一个整数数组 `arr` 和数组的长度 `n`。 +- 检查数组是否有序,即对于每个索引 i(0 ≤ i < n-1),arr[i] ≤ arr[i+1]。 + +## 输入 + +- 一个整数数组 `arr`,以逗号分隔,如 `1,2,3,4,5`。 +- 一个整数 `n`,表示数组的长度。 + +## 输出 + +- 如果数组有序,输出 `Array is sorted.`。 +- 如果数组无序,输出 `Array is not sorted.`。 + +## 示例 + +- 对于输入 `n = 5` 和数组 `arr = [1,2,3,4,5]`,输出应为 `Array is sorted.`。 + +## 代码介绍 + +1. **main.c** 文件包含了主函数和 `is_sorted` 函数的C包装器。 + - `main` 函数处理命令行参数,解析数组字符串,并调用 `is_sorted` 函数打印检查结果。 + - `is_sorted` 函数使用内联汇编实现,其中包含多个PLACEHOLDER,需要用正确的RISC-V汇编指令替换。 + +2. 内联汇编使用GCC的扩展语法,其中: + - `"li"` 指令用于加载立即数到寄存器。 + - `"lw"` 指令用于从内存加载值到寄存器。 + - `"bge"` 指令用于在比较操作中,当大于或等于时跳转。 + - `"addi"` 指令用于将立即数加到寄存器。 + - `"mv"` 指令用于将一个寄存器的值移动到另一个寄存器。 + +## 详细提示信息 + +- **内联汇编替换**: + - 使用RISC-V汇编指令替换 `is_sorted` 函数中的PLACEHOLDER。 + - 使用 `"lw"` 指令加载数组元素到寄存器 `t2` 和 `t3`。 + - 使用 `"bge"` 指令比较两个寄存器的值,并在当前元素大于下一个元素时跳转到 `sorted` 标签。 + - 使用 `"li"` 设置结果为0,表示数组无序,并跳转到 `end` 标签。 + - 使用 `"addi"` 更新数组指针和循环计数器。 + +- **数组解析**: + - 在 `main` 函数中,使用 `strtok` 函数解析逗号分隔的数组字符串。 + +- **错误处理**: + - 检查命令行参数的数量和格式。 + - 确保输入的数组元素数量与 `n` 匹配。 + +- **编译与测试**: + - 使用 `gcc` 编译器编译程序,并确保RISC-V汇编语法正确。 + +## 注意事项 + +- 确保替换的汇编指令逻辑正确,能够正确检查数组的有序性。 +- 注意内联汇编的语法和寄存器使用。 +- 确保程序能够正确处理命令行参数和数组解析。 +- 参与者需要根据题目要求,使用RISC-V汇编语言完成数组有序性的检查。通过这个练习,可以加深对RISC-V汇编语言和内联汇编使用的理解,并掌握如何在C语言中嵌入汇编代码来优化性能关键部分。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/47.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/47.md new file mode 100644 index 0000000000000000000000000000000000000000..906b98b46e3a19cc81acba136f876f26b1ebb12e --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/47.md @@ -0,0 +1,65 @@ +# 使用内联 RISCV 汇编实现给定数组目标元素个数 + +## 题目介绍 + +本题目要求编写一个C语言函数 `count_occurrences`,该函数使用内联RISC-V汇编来计算一个整数数组中目标值出现的次数。然后,编写一个程序调用该函数并输出结果。 + +本题可参考[第六章 RISC-V架构内联汇编](../chapter-6.md)全部小节内容。 + +## 题目要求 + +- 实现C语言函数 `count_occurrences`,使用内联RISC-V汇编。 +- 函数接收一个整数数组 `arr`、数组的长度 `n` 和一个目标值 `target`。 +- 计算并返回目标值在数组中出现的次数。 + +## 输入 + +- 一个整数数组 `arr`,以逗号分隔,例如 `1,2,3,4,5`。 +- 一个整数 `n`,表示数组的长度。 +- 一个整数 `target`,表示要计数的目标值。 + +## 输出 + +- 按照格式 `Occurrences of target:OCCURRENCE` 输出,其中 `OCCURRENCE` 是目标值在数组中的出现次数。如果目标值未出现,则输出 `Occurrences of target:0`。 + +## 示例 + +- 对于输入 `n = 5`,数组 `arr = [1,2,2,3,2]` 和 `target = 2`,输出应为 `Occurrences of target:3`。 + +## 代码介绍 + +1. **main.c** 文件包含了主函数和 `count_occurrences` 函数的C包装器。 + - `main` 函数处理命令行参数,解析数组字符串,调用 `count_occurrences` 函数并打印出现次数。 + - `count_occurrences` 函数使用内联汇编实现,包含多个PLACEHOLDER,需要用正确的RISC-V汇编指令替换。 + +2. 内联汇编使用GCC的扩展语法,其中包括: + - `"li"` 指令用于将立即数加载到寄存器。 + - `"lw"` 指令用于从内存加载值到寄存器。 + - `"beq"` 指令用于比较两个寄存器的值,并在相等时跳转。 + - `"addi"` 指令用于将立即数加到寄存器。 + - `"mv"` 指令用于将一个寄存器的值移动到另一个寄存器。 + +## 详细提示信息 + +- **内联汇编替换**: + - 使用RISC-V汇编指令替换 `count_occurrences` 函数中的PLACEHOLDER。 + - 使用 `"li"` 初始化计数器和索引寄存器。 + - 使用 `"lw"` 加载数组元素到寄存器,并使用 `"beq"` 指令比较是否与目标值相等。 + - 使用 `"addi"` 更新计数器和数组索引。 + +- **数组解析**: + - 在 `main` 函数中,使用 `strtok` 函数解析逗号分隔的数组字符串。 + +- **错误处理**: + - 检查命令行参数的数量和格式。 + - 确保输入的数组元素数量与 `n` 匹配。 + +- **编译与测试**: + - 使用 `gcc` 编译器编译程序,并确保RISC-V汇编语法正确。 + +## 注意事项 + +- 确保替换的汇编指令逻辑正确,能够正确计算目标值的出现次数。 +- 注意内联汇编的语法和寄存器使用。 +- 确保程序能够正确处理命令行参数和数组解析。 +- 参与者需要根据题目要求,使用RISC-V汇编语言完成目标值出现次数的计算。通过这个练习,可以加深对RISC-V汇编语言和内联汇编使用的理解,并掌握如何在C语言中嵌入汇编代码来优化性能关键部分。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/50.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/50.md new file mode 100644 index 0000000000000000000000000000000000000000..3751d3edc322c7ce5c233008da827623c04795db --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/50.md @@ -0,0 +1,77 @@ +# 模拟 FIFO 页面置换算法 + +## 题目介绍 + +本题目要求模拟操作系统中使用的FIFO(先进先出)页面置换算法。FIFO算法是一种简单的页置换策略,它总是替换最早被加载进内存的页面。当页面访问序列给定,以及物理内存中可用的页框数量确定时,模拟页面的加载和置换过程,并在每次页面访问后输出内存中的页面状态。 + +本题可参考[第七章 操作系统常用算法](../chapter-7.md)中的[FIFO 页面置换算法](../chapter-7_1.md) + +## 题目要求 + +- 实现 `fifo_page_replacement` 函数,模拟FIFO页面置换算法。 +- 处理页面访问序列,并根据物理内存的页框数量进行页面置换。 +- 每次页面访问后,输出当前物理内存中的页面状态。 + +## 输入 + +- 第一行:虚拟内存页访问序列(以逗号分隔的整数)。 +- 第二行:物理内存的页框数量(整数)。 + +## 输出 + +- 每次页面访问后的物理内存状态,格式为 `Access:,Frames:[,,...]`。 + +## 示例 + +- 输入: + ``` + 0,1,2,0,1,3,0 + 3 + ``` +- 输出: + ``` + Access:0,Frames:[0,-1,-1] + Access:1,Frames:[0,1,-1] + Access:2,Frames:[0,1,2] + Access:0,Frames:[0,1,2] + Access:1,Frames:[0,1,2] + Access:3,Frames:[3,1,2] + Access:0,Frames:[3,0,2] + ``` + +## 代码介绍 + +1. **main.c** 文件包含了主函数,用于处理命令行参数并调用 `fifo_page_replacement` 函数。 + +2. **fifo.c** 文件应包含 `fifo_page_replacement` 函数的实现,但当前为空,需要编写。 + +3. **fifo.h**(未展示)可能包含 `fifo_page_replacement` 函数的声明和任何必要的数据结构。 + +## 详细提示信息 + +- **FIFO算法逻辑**: + - 使用队列(或数组)来模拟物理内存中的页框。 + - 遍历页面访问序列,对于每个页面访问: + - 如果页面已加载在内存中,则不进行置换。 + - 如果页面未加载,检查是否有足够的空闲页框: + - 如果有,加载页面。 + - 如果没有,根据FIFO算法置换最早加载的页面,然后加载新页面。 + +- **内存管理**: + - 使用额外的数据结构(如哈希表)来跟踪页面是否已加载。 + +- **输入处理**: + - 在 `main.c` 中,解析命令行参数,获取页面访问序列和页框数量。 + +- **输出格式化**: + - 在每次页面访问后,按照要求的格式输出当前内存状态。 + +- **测试**: + - 编译程序,并使用示例输入进行测试,验证输出的正确性。 + +## 注意事项 + +- 确保 `fifo_page_replacement` 函数正确实现FIFO置换逻辑。 +- 注意内存分配和释放,避免内存泄漏。 +- 确保输出格式与题目要求一致。 +- 参与者需要根据题目要求,完成 `fifo_page_replacement` 函数的实现,确保能够正确模拟FIFO页面置换算法。通过这个练习,可以加深对操作系统内存管理和页面置换算法的理解。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/51.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/51.md new file mode 100644 index 0000000000000000000000000000000000000000..f42a3981bf91c2f0340e69b8b3142e08217a47fd --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/51.md @@ -0,0 +1,102 @@ +# 模拟 LRU 页面置换算法 + +## 题目介绍 + +本题目要求模拟操作系统中使用的LRU(最近最少使用)页面置换算法。LRU算法是一种性能优异的页置换策略,它根据页面的使用频率来决定哪些页面应该被置换出物理内存。当一个页面被访问时,如果物理内存已满,LRU算法会替换掉最长时间未被访问的页面。 + +本题可参考[第七章 操作系统常用算法](../chapter-7.md)中的[LRU 页面置换算法](../chapter-7_2.md) + +### 题目要求 + +- 实现 `lru_page_replacement` 函数,模拟LRU页面置换算法。 +- 处理页面访问序列,并根据物理内存的页框数量进行页面置换。 +- 每次页面访问后,输出当前物理内存中的页面状态。 + +### 输入 + +- 第一行:虚拟内存页访问序列(以逗号分隔的整数)。 +- 第二行:物理内存的页框数量(整数)。 + +### 输出 + +- 每次页面访问后的物理内存状态,格式为 `Access:,Frames:[,,...]`。 + +### 示例 + +- 输入: + ``` + 0,1,2,0,1,3,0 + 3 + ``` +- 输出: + ``` + Access:0,Frames:[0,-1,-1] + Access:1,Frames:[0,1,-1] + Access:2,Frames:[0,1,2] + Access:0,Frames:[0,1,2] + Access:1,Frames:[0,1,2] + Access:3,Frames:[0,1,3] + Access:0,Frames:[0,1,3] + ``` + +## 代码介绍 + +### main.c + +主函数文件,用于处理命令行参数并调用 `lru_page_replacement` 函数。它使用 `strdup` 函数复制输入的页面访问序列,并检查内存分配是否成功。 + +```c +int main (int argc, char *argv[]) { + // ... + lru_page_replacement(queue_frames, num); + return EXIT_SUCCESS; +} +``` + +### lru.c + +应包含 `lru_page_replacement` 函数的实现,但当前为空,需要编写。该函数需要模拟LRU页面置换算法。 + +```c +void lru_page_replacement(char *queue_frames, int num) { + // TODO +} +``` + +### lru.h + +包含 `lru_page_replacement` 函数的声明和任何必要的数据结构。 + +## 详细提示信息 + +### LRU算法逻辑 + +- 使用数据结构(如链表或哈希表)来跟踪页面的访问顺序和内存中的页面状态。 +- 遍历页面访问序列,对于每个页面访问: + - 如果页面已加载在内存中,更新其为最近访问的状态。 + - 如果页面未加载,检查是否有足够的空闲页框: + - 如果有,加载页面。 + - 如果没有,根据LRU算法找到并置换最长时间未被访问的页面,然后加载新页面。 + +### 内存管理 + +- 确保使用合适的数据结构来有效模拟页面的加载和置换过程。 + +### 输入处理 + +- 在 `main.c` 中,解析命令行参数,获取页面访问序列和页框数量。 + +### 输出格式化 + +- 在每次页面访问后,按照要求的格式输出当前内存状态。 + +### 测试 + +- 编译程序,并使用示例输入进行测试,验证输出的正确性。 + +## 注意事项 + +- 确保 `lru_page_replacement` 函数正确实现LRU置换逻辑。 +- 注意内存分配和释放,避免内存泄漏。 +- 确保输出格式与题目要求一致。 +- 参与者需要根据题目要求,完成 `lru_page_replacement` 函数的实现,确保能够正确模拟LRU页面置换算法。通过这个练习,可以加深对操作系统内存管理和页面置换算法的理解。 \ No newline at end of file diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/52.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/52.md new file mode 100644 index 0000000000000000000000000000000000000000..3aa55aaf6bc135f9e1599faa6b1f7dfda4e0d1d7 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/52.md @@ -0,0 +1,55 @@ +# 获取容器主机名 + +## 题目介绍 + +本题目要求参与者完成一个C语言程序,该程序的功能是获取并输出当前容器(或虚拟机)的主机名。 + +本题可参考[Linux系统编程基础](../chapter-5_6.md) + +### 题目要求 + +- 完成给定程序中的 `main` 函数。 +- 函数的作用是获取当前环境(容器或虚拟机)的主机名,并将其作为程序输出。 + +### 输入 + +- 无输入要求。 + +### 输出 + +- 应输出当前容器或虚拟机的主机名,例如 `admin_virtual_machine`。 + +## 代码介绍 + +### main.c + +主函数文件,需要实现获取主机名的逻辑。 + +```c +#include +#include + +int main () { + // TODO +} +``` + +## 详细提示信息 + +### 获取主机名 + +在Unix-like系统中,可以使用系统调用 `gethostname` 来获取当前主机名。这个函数需要一个字符数组作为参数,并将主机名复制到这个数组中。 + +### 使用 `gethostname` + +- 声明一个足够大的字符数组来存储主机名,通常这个数组的大小至少为 `HOST_NAME_MAX`。 +- 调用 `gethostname` 函数,传入字符数组和数组的大小作为参数。 +- 检查 `gethostname` 的返回值,确保主机名被成功获取。 +- 使用 `printf` 或 `puts` 函数输出获取到的主机名。 + +## 注意事项 + +- 确保包含正确的头文件,以便使用 `gethostname` 函数。 +- 检查 `gethostname` 的返回值,并对可能的错误进行处理。 +- 确保输出的主机名是正确的,并且符合题目要求的格式。 +- 参与者需要根据题目要求,使用 `gethostname` 函数完成 `main` 函数的实现,确保程序能够正确获取并输出当前容器或虚拟机的主机名。通过这个练习,可以加深对Unix系统调用和主机名获取机制的理解。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/53.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/53.md new file mode 100644 index 0000000000000000000000000000000000000000..8523c06026f9292e084effefef15389d94af8c3a --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/53.md @@ -0,0 +1,94 @@ +# 简单虚拟地址到物理地址的转换 + +## 题目介绍 + +本题目要求实现一个虚拟地址到物理地址的转换过程,这是操作系统内存管理中的一个重要概念。通过给定的页表和虚拟地址,参与者需要完成地址转换的函数实现。 + +本题可参考[第一章 虚拟内存与物理内存管理](../chapter-1.md) + +### 题目要求 + +- 实现 `translate_address` 函数,将虚拟地址转换为物理地址。 +- 假设页大小为 4KB,页表是一个包含32位地址的数组。 + +### 输入 + +- 一个无符号 32 位整数,表示虚拟地址(十六进制格式)。 + +### 输出 + +- 一个无符号 32 位整数,表示对应的物理地址(十六进制格式)。 + +### 示例 + +- 输入:`0x00002234` +- 输出:`Physical_Address:0x20002234` + +## 代码介绍 + +### main.c + +主函数文件,用于处理命令行参数并调用 `translate_address` 函数。 + +```c +int main (int argc, char *argv[]) { + // ... + uint32_t physical_address = translate_address(virtual_address); + printf("Physical_Address:0x%x\n", physical_address); + return EXIT_SUCCESS; +} +``` + +### v2p.c + +包含 `translate_address` 函数的实现,需要根据页表完成地址转换。 + +```c +uint32_t page_table[] = { + // 模拟页表项 +}; + +uint32_t translate_address(uint32_t virtual_address) { + // TODO +} +``` + +### v2p.h + +头文件,包含宏定义和函数声明。 + +```c +#ifndef _V2P_H__ +#define _V2P_H__ +#include +#define PAGE_SIZE 4096 +// ... +uint32_t translate_address(uint32_t virtual_address); +#endif +``` + +## 详细提示信息 + +### 地址转换逻辑 + +- 根据虚拟地址确定页表项索引。 +- 使用索引从页表中获取物理页帧基地址。 +- 将虚拟地址的偏移量加到物理页帧基地址上,得到完整的物理地址。 + +### 使用页表 + +- 页表 `page_table` 是一个数组,每个元素代表一个页的物理页帧基地址。 + +### 示例转换 + +- 假设虚拟地址为 `0x00002234`: + - 页号为 `0x00002`,页内偏移为 `0x234`。 + - 页表项索引为 `0x00002 / PAGE_SIZE`。 + - 页表项值为 `page_table[索引]`。 + - 物理地址为 `page_table[索引] + 页内偏移`。 + +### 注意事项 + +- 确保正确处理页号和页内偏移的计算。 +- 考虑页表项未找到的情况,并进行适当处理。 +- 参与者需要根据题目要求,完成 `translate_address` 函数的实现,确保能够正确地将虚拟地址转换为物理地址。通过这个练习,可以加深对操作系统内存管理和地址转换机制的理解。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/54.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/54.md new file mode 100644 index 0000000000000000000000000000000000000000..7349dcb98103d7f3033d12d68fd6f979b67edc43 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/54.md @@ -0,0 +1,91 @@ +# 模拟时间片轮转调度算法 + +## 题目介绍 + +本题目旨在模拟操作系统中的时间片轮转(Round Robin, RR)调度算法。时间片轮转是一种公平的进程调度策略,它通过为每个进程分配固定长度的时间片来实现CPU时间的分配。 + +本题可参考[第七章 操作系统常用算法](../chapter-7.md)中的[模拟时间片轮转调度算法](../chapter-7_3.md) + +### 题目要求 + +- 补充实现 `calculateTimes` 函数,根据时间片轮转算法计算每个进程的完成时间、周转时间和等待时间。 + +### 输入 + +- 程序中硬编码了所有数据,包括时间片长度、进程数量、每个进程的到达时间和运行时间。 + +### 输出 + +- 计算并输出每个进程的完成时间、周转时间和等待时间。 + +### 示例 + +- 给出的进程列表和时间片长度下,输出类似如下结果: + ``` + P1: 完成时间: 28, 周转时间: 28, 等待时间: 18 + P2: 完成时间: 22, 周转时间: 21, 等待时间: 15 + P3: 完成时间: 26, 周转时间: 24, 等待时间: 16 + P4: 完成时间: 16, 周转时间: 13, 等待时间: 9 + ``` + +## 代码介绍 + +### main.c + +主函数文件,包含了进程数组的定义和调用 `calculateTimes` 函数的逻辑。 + +```c +int main() { + // ... + calculateTimes(processes, n, time_slice); + for (int i = 0; i < n; i++) { + printf("P%d:%d,%d,%d\n", processes[i].pid, processes[i].completion_time, + processes[i].turnaround_time, processes[i].waiting_time); + } + return EXIT_SUCCESS; +} +``` + +### RR.c + +包含 `calculateTimes` 函数的实现,该函数需要根据时间片轮转算法填充进程结构体中的相关字段。 + +```c +void calculateTimes(Process *processes, int n, int time_slice) { + // TODO +} +``` + +### RR.h + +头文件,定义了进程结构体 `Process` 和 `calculateTimes` 函数的声明。 + +```c +typedef struct { + // ... +} Process; + +void calculateTimes(Process *processes, int n, int time_slice); +``` + +## 详细提示信息 + +### 时间片轮转算法逻辑 + +- 维护一个就绪队列来存储所有等待执行的进程。 +- 将CPU时间分配给队首进程,长度为时间片。 +- 如果进程在时间片内完成,则将其移出队列。 +- 如果进程未完成,则将其移动到队列末尾,并继续处理下一个进程。 + +### 计算指标 + +- **完成时间**:进程实际完成执行的时间点。 +- **周转时间**:从进程到达到完成的总时间(完成时间 - 到达时间)。 +- **等待时间**:进程在就绪队列中等待的时间(周转时间 - 运行时间)。 + +### 注意事项 + +- 确保正确处理进程的到达时间和运行时间。 +- 考虑进程可能在一个时间片内完成或需要多个时间片完成。 +- 维护一个当前时间变量,以跟踪模拟的进度。 +- 参与者需要根据题目要求,完成 `calculateTimes` 函数的实现,确保能够正确模拟时间片轮转调度算法,并计算出每个进程的相关指标。通过这个练习,可以加深对操作系统进程调度机制的理解。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/exercises/55.md b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/55.md new file mode 100644 index 0000000000000000000000000000000000000000..8c2999dea0b90a02ed361f80bcd61e076aae441c --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/exercises/55.md @@ -0,0 +1,78 @@ +# 模拟虚拟内存限制 + +## 题目介绍 + +本题目要求编写一个C程序,用于演示如何在Unix-like系统中设置进程的内存限制,并运行一个子进程来测试这个限制。这涉及到资源限制的概念,是操作系统中进程管理的一个重要方面。 + +本题可参考[Linux系统编程基础](../chapter-5_6.md) + +### 题目要求 + +- 实现 `set_memory_limit` 函数,设置进程的内存限制。 +- 实现 `run_child_process` 函数,创建并运行一个子进程,并为其设置内存限制。 +- 子进程应尝试分配超出限制的内存,并观察结果。 + +### 输入 + +- 子进程尝试分配的内存大小(在 `child_program.c` 中硬编码为120MB)。 + +### 输出 + +- 子进程因超出内存限制而失败的信号或退出状态。 + +## 代码介绍 + +### child_program.c + +子进程的程序,尝试分配指定大小的内存。 + +```c +void allocate_memory(size_t size_in_mb) { + // ... +} + +int main() { + allocate_memory(120); // 尝试分配120MB内存 + return 0; +} +``` + +### main.c + +主进程的程序,设置内存限制并运行子进程。 + +```c +void set_memory_limit(rlim_t memory_limit) { + // 设置内存限制 +} + +void run_child_process(rlim_t memory_limit, const char *program_path) { + // 运行子进程并设置内存限制 +} + +int main() { + // 设置内存限制为100MB并运行子进程 + run_child_process(memory_limit, program_path); + return 0; +} +``` + +## 详细提示信息 + +### 设置内存限制 + +- 使用 `setrlimit` 函数设置进程的资源限制,具体是 `RLIMIT_AS`(进程的地址空间限制)。 + +### 运行子进程 + +- 使用 `fork` 创建子进程。 +- 在子进程中调用 `set_memory_limit` 设置内存限制。 +- 使用 `execl` 替换子进程的映像,运行 `child_program`。 +- 在父进程中等待子进程结束,并根据其退出状态给出相应的输出。 + +### 注意事项 + +- 确保 `setrlimit` 正确设置内存限制,注意资源限制的结构体 `rlimit` 的设置方式。 +- 子进程的内存分配失败时,应正确捕捉并输出错误信息。 +- 父进程应正确解释子进程的退出状态,并给出清晰的输出。 +- 参与者需要根据题目要求,完成内存限制设置和子进程运行的相关函数实现。通过这个练习,可以加深对操作系统资源限制和进程管理的理解。 diff --git a/mdbook/eulixos-camp-book-stage2-docs/src/preface.md b/mdbook/eulixos-camp-book-stage2-docs/src/preface.md new file mode 100644 index 0000000000000000000000000000000000000000..c5071eb9b73c91e4711480ea1d24946fbc60e255 --- /dev/null +++ b/mdbook/eulixos-camp-book-stage2-docs/src/preface.md @@ -0,0 +1,21 @@ +# 前言 + +大家好!经过了基础阶段的努力学习,相信大家已对计算机有了基本的了解。希望大家能在进阶阶段学到更多内容,并在之后的学习中取得更大的进步。 + +在本书中,我们将基于基础阶段之上进一步加大难度,同时会带领大家串讲基础阶段的知识,并拓展新的内容————正所谓温故而知新。我们默认进入此阶段后你们已掌握了环境搭建、基础编程、基础操作系统等知识,故本书也将不再详细介绍这些内容。 + +本阶段主要将从以下几个理论课开展: + +1. 虚拟内存与物理内存管理 +2. 系统虚拟化与容器 +3. RISC-V向量拓展基础与优化基础C库实践 + +同时为了提升大家的实战能力,便于完成我们的课后练习题,我们后面又为大家准备了大量的实战编程方面的教学内容: + +1. 矩阵与卷积算法 +2. Linux内核模块 +3. RISC-V架构内联汇编 +4. 操作系统常用算法 +5. Linux系统编程基础 + +希望大家从这里扬帆起航,取得好成绩!