【uCore 操作系统】1. 应用程序与基本执行环境

文章目录

  • 【 1. 代码框架简述 】
    • 1.1 OS 是怎么跑起来的?
      • 1.1.1 qemu 的作用
      • 1.1.2 rustsbi.bin 的作用
    • 1.2 qemu 是怎么跑起来的?
    • 1.3 OS 文件夹
      • 1.3.1 kernel.ld
      • 1.3.2 entry.S
      • 1.3.3 main.c
      • 1.3.4 sbi.c
    • 1.4 bootloader 文件夹
  • 【 2. makefile 和 qemu 】
    • 2.1 makefile 内部
      • 2.1.1 指定编译使用的工具
      • 2.1.2 添加编译flag
      • 2.1.3 设置编译目标
    • 2.2 运行 qemu
    • 2.3 gdb 调试
    • 2.4 LOG 支持
  • 参考

  • 本章展现了操作系统的一个基本目标:让应用与硬件隔离,这种形式 简化了应用访问硬件的难度和复杂性。这也是远古操作系统雏形和现代的一些简单嵌入式操作系统的主要功能。具有这样功能的操作系统形态 就是一个函数库,可以被应用访问,并 通过函数库的函数来访问硬件
  • 大多数程序员的第一行代码都从 Hello, world! 开始,当我们满怀着好奇心在编辑器内键入仅仅数个字节,再经过几行命令编译(靠的是编译器)、运行(靠的是操作系统),终于在黑洞洞的终端窗口中看到期望中的结果的时候,一扇通往编程世界的大门已经打开。生成应用程序二进制执行代码所依赖的是以 编译器 为主的开发环境 ;运行应用程序执行码所依赖的是以 操作系统 为主的执行环境
  • 系统调用(syscall) 是操作系统提供给软件的一系列接口,使得软件能够使用系统的功能。
    syscall 本质上属于一种 异常/中断,它在 riscv 的汇编指令中以 ecall 的形式出现。
    syscall 的种类有很多,操作系统通过区分 syscall 的 id 来判断是哪一个syscall。
  • 本章我们将从操作系统最简单但也是最重要的println入手,要求大家实现一个裸机上的println以及带色彩的LOG,如info和warn,error等功能。本章的 println 所需要的在 console 中打印字符,也需要调用到 syscall。

【 1. 代码框架简述 】

  • Ucore 项目的代码树如下:
.
├── bootloader
│   └── rustsbi-qemu.bin
├── LICENSE
├── Makefile
├── os
│   ├── console.c
│   ├── console.h
│   ├── defs.h
│   ├── entry.S
│   ├── kernel.ld
│   ├── log.h
│   ├── main.c
│   ├── printf.c
│   ├── printf.h
│   ├── riscv.h
│   ├── sbi.c
│   ├── sbi.h
│   └── types.h
├── README.md

1.1 OS 是怎么跑起来的?

1.1.1 qemu 的作用

  • 我们的 OS 的运行,是要依赖著名的模拟器软件 qemu的。
    比较形象的比喻是,OS 就是一个内核软件,qemu就类似一个主板,它模拟了许多硬件,比如CPU,I/O串口等等,OS 会和 qemu 模拟出来的这些硬件打交道,而 qemu 则把得到的指令分配给实际存在的硬件完成

1.1.2 rustsbi.bin 的作用

  • OS 启动的时候,就像一个真正的操作系统启动一样。qemu 使用我们提供的 rustsbi 的bin文件做为引导程序 来启动OS。
  • SBI 是 RISC-V 的一种底层规范,RustSBI 是SBI的一种实现。 操作系统内核与 RustSBI 的关系有点像应用与操作系统内核的关系,后者(即RustSBI)向前者(即操作系统内核)提供一定的服务,只是SBI提供的服务很少, 比如关机,显示字符串,读入字符串等。
  • SBI 的一个直接作用:提供输入输出。
    我们的内核作为运行在qemu中的虚拟机,是无法直接和我们的外部 host系统通信的。因此我们OS自己实现的printf函数,想要真正地输出到我们外部运行的shell上被我们看到,是要经过qemu的。实际上,在 启动时sbi已经初始化好了,经过 qemu模拟出来的串口,最终打印到我们外部的shell上 的,从我们的shell之中读取输入,也是同样的道理。
    SBI 为我们内核提供的功能不止于输入输出,在sbi.c文件的可以看到其他支持的功能,比如关机。

1.2 qemu 是怎么跑起来的?

  • qemu 拓展阅读: qemu参数
  • makefile 之中提供了具体运行qemu所需要的参数:
    在这里插入图片描述
  • make run这条指令首先执行上面 kernel 所需要的链接以及编译操作,得到一个二进制的 kernel,之后执行按照 QEMUOPTS 变量指定的参数启动qemu。
    QEMUOPTS意义如下:
    • nographic: 无图形界面。
    • smp 1: 单核 (默认值,可以省略)。
    • machine virt: 模拟硬件 RISC-V VirtIO Board。
    • bios $(bios): 使用指定 bios,这里指向的是我们提供的 rustsbi 的bin文件。
    • kernel: 使用 elf 格式的 kernel。这里就是我们需要写的OS内核了。
  • makerun 指令完成了内核代码的编译生成kernel,并按照QEMUOPTS变量指定的参数加载我们的kernel,“加电”启动qemu。 此时,CPU 的其它通用寄存器清零,而 PC 会指向 0x1000 的位置,这里有固化在硬件中的一小段引导代码,它会很快跳转到 0x80000000 的 RustSBI 处。 RustSBI完成硬件初始化后,会跳转到 $(KERNEL_BIN) 所在内存位置 0x80200000 处, 执行我们操作系统的第一条指令。
    在这里插入图片描述
  • 那么,知道了这些步骤之后,关键就是怎么去写我们的OS了,这也是我们接下来各个实验的内容。

1.3 OS 文件夹

  • os文件夹下存放了所有我们构建操作系统的源代码,是本次实验中最最重要的一部分,也是整个实验过程中唯一需要修改的部分。在开始实验之前,大家一定要清楚我们这是自己设计的 OS,是无法使用C提供的官方标准库的,也就是说,就算是最简单的 printf 之类的函数都无法使用。还好,作为一个轻量级的 OS,我们也用不到那么多函数。
  • 我们的os是一个由 makefile 来构建的C项目。下面介绍框架之中一些重要文件的作用,以及整个项目是如何链接及编译的。

1.3.1 kernel.ld

  • kernel.ld是我们用于链接项目的脚本。链接脚本决定了 elf 程序的内存空间布局(严格的讲是虚存映射,注意程序中的各种绝对地址就在链接的时候确定),由于刚进入 S 态的时候我们尚未激活虚存机制,我们必须把 os 置于物理内存的 0x80200000 处(这个地址的来由请参考 rustsbi)
BASE_ADDRESS = 0x80200000;
SECTIONS
{. = BASE_ADDRESS;skernel = .;stext = .;.text : {*(.text.entry)   # 第一行代码*(.text .text.*)}...
}
  • SECTIONS 之中是从 BASE_ADDRESS 开始的各段。对程序内存布局还不太熟悉的同学可以翻看后面内存布局的章节。以 text 段为例,它是由不同文件的 text 组成。我们没有规定这些 text 段的具体顺序,但是我们规定了一个特殊的 text 段:.text.entry 段,该 text 段是 BASE_ADDRESS 后的第一个段,该段的第一行代码就在 0x80200000 处。这个特殊的段不是编译生成的,它在 entry.S 中人为设定。

1.3.2 entry.S

# entry.S.section .text.entry.globl _entry
_entry:la sp, boot_stackcall main.section .bss.stack.globl boot_stack
boot_stack:.space 4096 * 16.globl boot_stack_top
boot_stack_top:
  • .text.entry 段中只有一个函数 _entry,它干的事情也十分简单,设置好 os 运行的堆栈(la sp, boot_stack语句。bootloader 并没有好心的设置好这些),然后调用 main 函数。main 函数位于 main.c 中,从此开始我们就基本进入了 C 的世界。

1.3.3 main.c

  • 它是os的入口函数。在其中我们会完成一系列的初始化并开始运行os。 作为第一章,它在初始化完毕之后实际上起到了一个测试的作用。如果你的main.c能够完成一系列打印并且最后成功退出(Shutdown),那么祝贺你,你完成了os的第一步。
extern char s_text[];
extern char e_text[];
// ...void main()
{clean_bss();console_init();printf("\n");printf("hello wrold!\n");errorf("stext: %p", s_text);// ...errorf("ebss : %p", e_bss);panic("ALL DONE");
}
  • 其中,main.c 之中众多的 extern 声明的内存段是在 ld 文件之中定义的,通过这些 symbol 我们可以大致了解 OS 的内存布局。
  • 此外 clean_bss() 清空了 bss 段,注意,清空 elf 程序 .bss 段这一工作通常是由 OS 做的,而我们就只好自立更生了。
  • 你可能注意到除了 printf 之外,还有一些用于 log 的彩色输出宏。感兴趣的同学可以看看 log.h 。

1.3.4 sbi.c

  • printf 的实现在 printf.c,在函数之中我们完成了对 format 字符串的解析工作。那么我们是如何把字符串真正地打印到 shell 上的呢? 我们 调用consputc 函数输出一个 char 到 shell,而 consputc 函数其实就是调用了 sbi.c 之中的 console_putchar 函数。这个 console_putchar 函数的本质是调用了 sbi_call。剥开层层套娃,大家可以发现 打印的最终实现是使用 sbi 包装好的 ecall 汇编代码,通过指定 ecall 的 idx 为 SBI_CONSOLE_PUTCHAR,并将我们的字符做为参数传入到 ecall 指定的寄存器之中完成一次系统调用来实现的。 本来,作为一个 OS,串口输出(也就是输出到 shell)的事情也应该我们自己来做,但这里为了简化这些硬件强相关的实现,我们利用 rust-sbi 的 M 态支持。这也是 riscv 灵活性的一个体现。
    rustsbi 拓展阅读: rsutsbi。

1.4 bootloader 文件夹

  • 这个文件夹是用来存放 bootloader(也就是 rustsbi) 的 bin 文件的,这一章以及之后都无需我们做任何修改。
  • 硬件加电之后是处于M态,而 rustsbi 帮助我们完成了 M 态的初始化,最终将 PC 移动至我们 os 开始执行的位置。同时,它也会帮助S态的 os 完成一些基本管理,详情可以看 os/sbi.c 文件。

【 2. makefile 和 qemu 】

  • makefile 在整个实验过程中不可修改,否则可能导致 CI 无法通过!

2.1 makefile 内部

2.1.1 指定编译使用的工具

  • 这里 makefile 调用了大家 设定好的PATH之中的riscv64工具链。如果没有设置好,那么之后的编译就会因为找不到这些文件而出错。
TOOLPREFIX = riscv64-unknown-elf-
CC = $(TOOLPREFIX)gcc
AS = $(TOOLPREFIX)gas
LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump
GDB = $(TOOLPREFIX)gdb

2.1.2 添加编译flag

  • 比较需要注意的是 这里设置了警告也会报错,因此大家写代码的时候最好避免 warning 的出现,这是良好的编程习惯。
CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb
CFLAGS += -MD
CFLAGS += -mcmodel=medany
CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax
CFLAGS += -I.
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)

2.1.3 设置编译目标

# 目录定义
K = os
BUILDDIR = build
# .o 目标的确定,也就是 os 目录下所有的 .c 和 .s 都编译成 .o
C_SRCS = $(wildcard $K/*.c)
AS_SRCS = $(wildcard $K/*.S)
C_OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(C_SRCS))))
AS_OBJS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(AS_SRCS))))
OBJS = $(C_OBJS) $(AS_OBJS)
# kernel 镜像由所有的 .o 按照 kernel.ld 链接而成
$(BUILDDIR)/kernel: $(OBJS) $(K)/kernel.ld$(LD) $(LDFLAGS) -T kernel.ld -o kernel $(OBJS)

2.2 运行 qemu

  • 这里和前面一致。大家不需要太关心qemu的更多细节,我们涉及它的操作已经在makefile和sbi之中处理了。
QEMU = qemu-system-riscv64
QEMUOPTS = \-nographic \-smp $(CPUS) \-machine virt \-bios $(BOOTLOADER) \-kernel kernelrun: $(BUILDDIR)/kernel
$(QEMU) $(QEMUOPTS)

2.3 gdb 调试

  • 使用 make debug 来使用 gdb 调试 qemu,程序自身执行的机制和直接 make run 一样。
  • 在解析 bootloader 的行为时可以使用 gdb 在其中添加断点来查看对应寄存器和内存的内容。
# QEMU's gdb stub command line changed in 0.11
QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \then echo "-gdb tcp::1234"; \else echo "-s -p 1234"; fi)debug: kernel .gdbinit$(QEMU) $(QEMUOPTS) -S $(QEMUGDB) &sleep 1$(GDB)

2.4 LOG 支持

  • 我们的 log 等级选择是通过 -D 参数来实现的,这也是大家 make run LOG=xxx 的原理。从这里我们也可以看到 LOG 的可选值。
ifeq ($(LOG), error)
CFLAGS += -D LOG_LEVEL_ERROR
else ifeq ($(LOG), warn)
CFLAGS += -D LOG_LEVEL_WARN
else ifeq ($(LOG), info)
CFLAGS += -D LOG_LEVEL_INFO
else ifeq ($(LOG), debug)
CFLAGS += -D LOG_LEVEL_DEBUG
else ifeq ($(LOG), trace)
CFLAGS += -D LOG_LEVEL_TRACE
endif

参考

代码框架简述

makefile 和 qemu

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/688499.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【SpringBoot】项目启动增加自定义Banner

SpringBoot项目启动增加自定义Banner 前言 最近有个老哥推荐我给博客启动的时候加上自定义Banner,开始我还不太明白他说的是那部分,后面给我发了这样一个,瞬间就懂了~ // _ooOoo_ …

C++入门篇——命名空间

在C/C中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存 在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化, 以避免命名冲突或名字污染,namespace关键…

数字经济政策 | ZF工作报告-60个文本词频

根据各省政府工作报告,参考金灿阳(2022)和陶长琪(2022),借助Python软件,统计数字经济相关的关键词词频,分别记为数字经济政策词频A、数字经济政策词频B A文献参考 B文献参考 年度趋势 一、数据介绍 数据名称: 政府工…

Spring Boot java -jar --spring.profiles.active=dev 失效问题

之前动态部署修改配置文件的情况不多&#xff0c;所以也没注意过&#xff0c;这个问题今天困扰了好久&#xff0c;经过多方查询后得到了解决办法 直接上代码 <profiles><profile><!-- 本地开发环境 --><id>dev</id><properties><profi…

债券专题二:可转债估值-二叉树模型

1. 模型背景 由于可转债自身的属性较多&#xff0c;因此对其定价的难度也会加大&#xff0c;在诸多影响因素中&#xff0c;未来的股价占比最高。由于股价的不可预测性&#xff0c;导致了可转债的定价在实际交易中作用非常有限。随着可转债发行数量和规模的增大&#xff0c;越…

2.18 C++ day6

思维导图 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff0c;动物园里有一位讲…

C++初阶(十三) 模板

一、非类型模板参数 模板参数分类类型形参与非类型形参。类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成常量…

基于微信小程序的健身房私教预约系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

嵌入式调试工具之GDB

在单片机开发中&#xff0c;我们可以通过集成式的IDE 来进行调试&#xff0c;比如 MDK、IAR 等。 GDB 工具是 GNU 项目调试器&#xff0c;基于命令行使用。和其他的调试器一样&#xff0c;可使用 GDB工具单步运行程序、单步执行、跳入/跳出函数、设置断点、查看变量等等&#…

2023我患上了AI焦虑

2023我患上了AI焦虑 来自&#xff1a;宝玉 原文链接&#xff1a;https://baoyu.io/blog/ai/i-am-suffering-from-ai-anxiety-in-2023 2023 年对我来说是神奇的一年&#xff0c;我意外的从一个程序员变成了一个 AI 资讯届的“网红”&#xff0c;到年底的时候我在 X 平台的阅读量…

IT行业高含金量证书全解析:开启职业生涯新篇章

在快速发展的IT行业&#xff0c;持续学习和专业认证是提升个人竞争力的重要途径。全球范围内存在着众多的IT认证&#xff0c;它们不仅能够验证你的技术能力&#xff0c;还能在求职和职业晋升中起到关键作用。 本篇博客将深入探讨IT行业中部分高含金量的证书&#xff0c;包括中…

最长子串和回文子串相关的算法题解

这里写目录标题 一、3. 无重复字符的最长子串二、5. 最长回文子串三、647. 回文子串四、516. 最长回文子序列 一、3. 无重复字符的最长子串 中等 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释:…

MyBatis核心配置文件详解

MyBatis核心配置文件详解 一、Environments标签1.Environment标签详解&#xff08;1&#xff09;如何创建对应环境的 SqlSessionFactory对象 2.transactionManager标签详解3.dataSource标签详解&#xff08;1&#xff09;UNPOOLED&#xff08;2&#xff09;POOLED&#xff08;3…

线性回归-使用ClickHouse机器学习函数

本文字数&#xff1a;5923&#xff1b;估计阅读时间&#xff1a;15 分钟 作者&#xff1a;Ensemble 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 这原本是转发的ensemble analytics的文章。 【https://ensembleanalytics.io/blog/l…

Java面试第一站:计算机网络基础知识

该系列会持续更新&#xff0c;关注我&#xff0c;第一时间获取我的最新动态哟 Java面试中&#xff0c;经常会问到跟计算机网络知识相关的考点&#xff0c;有的小伙伴不是很明白。考察网络知识有什么意义&#xff1f; 因为编程的时候&#xff0c;多数的情况下是不用我们来编写 …

春节专题|产业7问:区块链厂商的现在和未来——数字资产厂商

2023转瞬即逝&#xff0c;不同于加密领域沉寂一整年后在年末集中爆发&#xff0c;对于我国的区块链厂商而言&#xff0c;稳中求胜才是关键词&#xff0c;在平稳发展的基调下&#xff0c;产业洗牌也悄无声息的到来。 从产业总体而言&#xff0c;在经过了接近3年的快速发展后&…

【鸿蒙手机】获取UDID,并添加签名认证

一、打开开发者模式 1、手机型号华为nova 10 pro , HarmonyOS版本 4.0&#xff0c;路径&#xff1a;设置-> 关于本机-> 多次连续点击”软件版本“ 这一行&#xff0c;一般是是5到7次&#xff08;我是点击了5次&#xff09;&#xff0c;第一次会弹出输入密码&#xff0c;验…

SPSSAU【文本分析】|文本情感

文本情感分析 文本分析模块中&#xff0c;SPSSAU共提供两种方式的情感分析&#xff0c;分别是按词情感分析和按行情感分析。按词情感分析是指针对提取的关键词进行情感分析&#xff0c;并且进行可视化展示&#xff1b;按行情感分析是指针对分析的原始数据以‘行’为单位进行情…

安装cockpit

1、下载cockpit yum -y install cockpit 下载相关环境 yum install qemu-kvm libvirt libvirt-daemon virt-install virt-manager libvirt-dbus 2、启动libvirtd systemctl start libvirtd.service systemctl enable libvirtd.service 3、设置开机自启动 systemctl enabl…

2022长安杯复现

案件情况 某地警方接到受害人报案称其在某虚拟币交易网站遭遇诈骗&#xff0c;该网站号称使用“USTD 币”购买所谓的“HT 币”&#xff0c;受害人充 值后不但“HT 币”无法提现、交易&#xff0c;而且手机还被恶意软件锁定 勒索。警方根据受害人提供的虚拟币交易网站调取了对应…