
李叙铜 操作系统笔记
对李老师的每节课 做个笔记。
看星星的派大星
这个作者很懒,什么都没留下…
展开
专栏收录文章
- 默认排序
- 最新发布
- 最早发布
- 最多阅读
- 最少阅读
-
6-10 异常除零捕获(2)
所以 由于硬件 保存的 eflags , cs ,ip , 所以 手动的话, 只保存其他的寄存器就可以了。这里面的 C语言又调用了 另一个函数, 继续实现这个函数。但是 像一些 用于通知的异常 就需要 保护寄存器了。然后他没有保存 ss 段寄存器,这里不清楚为什么?然后是 在 汇编中 再调用C函数 进行实现。接下来就是 中断中关于 寄存器的保护。像这种 出错的异常 是不需要保存寄存器的。然后使用 adb 查看一下 内存的栈。pusha 保存的是通用的寄存器。然后就是 编译 + 测试了。原创 2024-12-15 18:36:07 · 137 阅读 · 0 评论 -
6-9 捕获 0 异常(1)
据说 函数返回值是 ret 类型, 但是中断处理函数 必须是 iret 类型 才行。疑问 :也就是说这里的 C的 函数定义, 只是 为了 让 C编译器通过吗?3、 由于使用是的 平坦模型,所以只需要 将具体的函数给到 中断向量表的 offset 字段就可以了。这要 在调试的时候 就会 不断的 返回到 这个 iret 指令的地方,1、 cpu 根据中断号 去中断向量表 去找 第几个 表。2、 而 中断向量表 的内容是 GDT 的选择子。接下来 就是 在 代码中定义 中断门的属性值。中断号的 处理是这样的。原创 2024-12-15 18:05:37 · 241 阅读 · 0 评论 -
6-8 初始化 IDT表
这个中断号 就是 对应着 这个中断向量表的 数组的下标, 这个是 cpu 设置的,程序员设置不了。注意: 时间上 一个 中断表的 数组,也就是一个 中断号 对应一个 中断表。首先是 创建 irq.h irq.c。然后是 关于 lidt 的函数的封装。主要是 完成这个表。原创 2024-12-09 08:00:36 · 201 阅读 · 0 评论 -
6-6 异常的简介, 以及 触发异常
2、在运行的时候,到这里之后,会反复的 加载 栈。就是会反复的跳转到这里。0-31 是内部使用的,我只可以使用32-255 的中断。3、 这其实是一个 异常,但是在代码中没有处理 异常。首先是再代码中 测试异常, 可以看到程序跑飞了。1、 在运行的时候,编译器器 会有警告。中断也是 异常的一种。cpu 处理中断的流程。然后是 什么是异常。原创 2024-11-26 07:49:01 · 201 阅读 · 0 评论 -
6-5 重新加载GDT(2)
然受 在 汇编start.s 中进行远跳转,这里不是很理解,不是已经设置了 gdt 表了吗?这里注意jump 的用法。然后再跳转到 init.c 中的 init_main 函数。注意再汇编中 包含头文件。汇编中 使用include , 名称后缀 要是 大写的S,并且使用 gcc ,而不是 asm。目前不知道如何 进入 qemu 的monitor 窗口。首先是 利用 设置函数 , 对 gdt 表进行设置。首先是可以走到 init_main 函数中的。然后是重新 设置了一下栈。然后就是 关于调试 了。原创 2024-11-26 07:35:44 · 181 阅读 · 0 评论 -
6-4 重新加载GDT(1)
首先是 采用的平坦模型,一段用来放代码,一段用来放 数据。使用之前的函数加载。我之前已经加载过一次了。然后是代码中 初始化,代码段 与数据段。cpu.h 中定义 GDT表的属性字段。os_cfg.h 中定义宏。os_cfg.h 加入这些。还是要以这个表为基础。cpu.h 加入这些。cpu.c 添加这些。目前编译是没有错误的。原创 2024-11-04 00:16:46 · 201 阅读 · 0 评论 -
6-3 保护模式下的内存管理
主要是 这里的 logical adrress 的 , 选择子:偏移。选择子就是 页表的偏移, 这里的偏移 ,就是指 在具体的某一个也表内的 偏移,有几个专门的位 来描述。32位的就是 IA32, 64位的就是 IA32e.目前采用的是 flat module。选择子 这里 我觉的我之前 有些过。再来 看一下 段式存储 与页式存储。到目前为止 ,MMU并没有 打开。首先是关于手册的内容。原创 2024-11-03 23:22:20 · 175 阅读 · 0 评论 -
6.3 关于保护模式的内存管理,就是mmu
这里我有一个疑问:0x8:0x1234 中, 段选择子中存储的是 0x8 ,还是 0x1234。只有 2-15 才是可以用的,但是 如果 0-3 的标志位都是0 的话,就可以忽略 标志位了。但是目前由于没有打开 页式存储,所以 段里面的地址 ,实际就是 实际的物理地址。我的理解,段式存储,定义了一大段连续的内存空间,那么如何去访问这段内存呢?而页表里面存储的内容,才真正对应到了 实际的物理内存。段里面的每个地址, 都会转换成 对应的页表的内容。选择子的值, 也是 分段的。然后是关于 选择子。原创 2024-10-28 13:54:26 · 226 阅读 · 0 评论 -
6.2 创建GDT表(2)
创建了三个文件夹, cpu , include , cpu (include 下)然后是创建 os_cfg.h , 放的是操作系统配置有关的宏定义。接下来 是 创建 cpu.h, 关于 x86 cpu 相关的东西。然后是将 实现的函数 加到 cpu.h 中,这里已经加过了。然后是在 init.c 文件中 对 gpt 表进行初始化。gdt 表的个数 是有限制的,具体多少限制 不清楚。首先是 创建 include 文件夹,然后是 修改 cmakelist。经过编译+ 测试是可以的。然后是创建 cpu.c。原创 2024-10-27 13:52:56 · 347 阅读 · 0 评论 -
6.1 创建gdt 表(1)
在卷三的数据手册中找到 关于 x86 的架构图:分段存储 与分页存储的图。从现在开始 便进入了 内核的编写。原创 2024-10-27 13:12:37 · 215 阅读 · 0 评论 -
5.16 加载内核映像文件(2)
然后去改调试文件的内容。改 launch.json, 这个地方不理解,可能跟 调试有关。主要是elf 文件头, 这个主要用于找到 section的地址。然后是 load_32.c 中实现对elf 文件的解析。这两的差值 ,就是需要 填0 的 .bss 段。然后是 loader_32.c 中的死机函数。感觉我的程序不对呀。但是确实能够走到最后。程序的入口地址 在elf 的头里面。然后需要注意 bss 段的拷贝。首先是 编写 elf 头文件。来看看 我的程序的反汇编。程序是可以走到最后的。原创 2024-10-27 11:50:01 · 498 阅读 · 0 评论 -
5.15 加载内核映像文件(1)
然后是新的存储规划: elf 文件格式 是不能 直接运行的,他首先将 elf 格式的文件放到1M的地方,然后自己提取处 代码 + 数据,放到 64KB的地方。然后 loarder 执行 64KB的代码。然后是修改 loader32.c ,这里有个细节, 就是 真正的 代码的地址,并不是 64KB 的地方,有些出入。也就是说, section值得是 单个C文件的各个段,segment 是指 合成之后的 程序中体的各个段。如何通过 ELF 头, 找到各个段。然后是 修改 elf 文件的生成。原创 2024-10-27 09:27:39 · 174 阅读 · 0 评论 -
5-15 连接脚本(2)
我的疑问: 为什么会需要一个绝对地址呢?我的猜测,在编译完之后,指令肯定是已经定死了,去哪取数据,也是定死的。我的疑问: 难道说 是把编译出来的整个程序 全部放到内存中吗?那万一程序很大怎么办呢?然后是 更改 cmakelist。原创 2024-10-26 09:16:50 · 127 阅读 · 0 评论 -
5-13 连接脚本(1)
还记得会有一条指令, -ttext=0x80000000 , 这里设置的是一个绝对的地址,在这个C文件中的 所有的 局部变量,以及全局变量都要以这个为基础,他们应该是一个相对的地址。也就是 符号表中的内容基本上都在 .text , .data, .rodata , .bss 段中了。然后还有一点是, 连接器 会将 所有的 C文件的 这个四个段 分别的合并在一起。.rodata 存放的是 const , 以及字符串。.bss 存放的是 没有初始值的 全局变量以及static。关于连接地址的内容。原创 2024-10-22 23:54:12 · 201 阅读 · 0 评论 -
5.12 向内核传递信息(2)
最关键的就是 这里了, call kernel_init 相当于 在调用一个函数, 但是在汇编中需要手动的 将参数入栈, 并且这里还需要手动的 将 C语言函数中的 入栈的参数取出来。总结 : 所以 我写的函数的逻辑是, 先保存 上一个函数的ebp ,然后设置当前函数的ebp , 然后将 将要调用的函数的参数 手动入栈,然后调用函数。不去理会 所谓的栈顶, 栈底, ebp 就是栈的最高地址,esp 是不断变化的,如果想要取寻找变量,就通过ebp 取寻找。所以我们最终是要去改动 init.c 的。原创 2024-10-19 00:51:29 · 255 阅读 · 0 评论 -
5.11 向内核传递信息(1)
ebp 我的理解 就是 某个函数 所使用的 栈的最高的位置,它 是固定的,编译器 寻找 局部变量使用的就是 这个指针。注意: 栈的顺序, 首先是 局部变量,然后是 函数的参数,然后是返回地址, 然后是 ebp , 然后是esp , 这里的ebp , esp 不是很理解。1 在进入到这个函数之后, 上来就 将ebp 的值 , 压入栈中, 这个 ebp 的值 ,是上一个 函数的值。2 然后将当前 esp 的值 给到了 ebp , 这是 ebp 指向的就是 这个函数的栈的栈顶了。原创 2024-09-25 08:02:19 · 353 阅读 · 0 评论 -
5.10 创建内核目录
然后是 创建 cmake , kernel 目录下的init 目录, init 目录下的start.s 文件,再init目录下 创建init.c init.h 文件。然后是通过修改 loarder_32.c 跳到 内存的1M地址处。然后是 修改 img-write-linux.sh 文件。然后是 创建 init.c init.h 文件。然后是 修改 lancher.json 文件。然后是 start.S 的编写。首先是 创建内核的目录。然后是修改cmake。原创 2024-09-20 00:22:46 · 199 阅读 · 0 评论 -
5.9 使用LBA 的模式来读取磁盘
1 也是说 是 MBR还是 LBA 硬盘是可以控制的。注意: 这里是遵循了 LBA48 的读取方式的。对于 LBA48 具体的操作是这样的。这里只找到了 LBA28 的寄存器。首先是 要读取的扇区数量的高8位 ,LBA 没有 磁盘, 柱面的概念。再然后是 要读取的扇区数量的低8位。3 实现 读取LBA的总体的函数。然后是 16---32。然后是 8---15。然后是是 0---7 位。2 对于寄存器的说明。然后是 24---31。然后是 40---47。然后是 32--39。原创 2024-09-13 00:25:47 · 466 阅读 · 0 评论 -
5.8 切换保护模式(5)
1 首先测试 了, 之前的代码 是 没有问题的,确实会 停在 汇编处。1 首先是 设置 除了 CS 之外的寄存器 进入 32为模式。2 然后是 从 汇编 再 跳转到 C语言。原创 2024-09-07 19:52:23 · 235 阅读 · 0 评论 -
5.7 切换保护模式 (4)
为了使C语言编译通过,还需要 加上一个函数定义。然后还要再 start.s 中做一些设置。2 然后是 进行园跳转, 清空流水线。1 首先是 设置 CR0。原创 2024-09-07 19:35:37 · 128 阅读 · 0 评论 -
5.6 切换进程保护模式(3)
1 实现 A20地址线的打开。然后是配置 一个内联函数。2 配置 gdt 表。然后是 应用这个函数。原创 2024-09-07 19:14:37 · 142 阅读 · 0 评论 -
5.5 切换进保护模式(2)
2 然后是 开A20 地址线。原创 2024-09-06 23:25:58 · 244 阅读 · 0 评论 -
5.4 切换进程保护模式(1)
bios 的 中断的服务, 只有再 实模式 下才可以使用, 保护模式就不可以使用了。这里比较重要的是 打开A20地址线, 以及 清空流水线。从实模式 到保护模式的切换的流程。原创 2024-09-06 23:10:39 · 207 阅读 · 0 评论 -
5-2 检测内存容量
1 使用的是bios 中断, 每次进行检测都会返回一块 内容。最后的解决方案是 ,自己写了个头文件, 但是 课程的头文件依然保留着, 只是 里面没有内容。myloader.h 是自己写的头文件。这个结构体是 int15 中断需要的。boot_info.h 设置一个结构体用于存放探测到的 内存容量。loader.h 什么也不写, 就是这个头文件出问题的。然后是对于 头文件的包含。接下来是对 顶层 cmake 的修改。types.h 是 一些宏定义。boot_info.h 的内容。types.h 的内容。原创 2024-09-06 23:00:11 · 312 阅读 · 0 评论 -
5.1 利用内联汇编 显示字符串
注意: asm 也可以写成 __asm__ , 如果不想要 编译器做优化,还可以 __asm__ __volatile__如果需要分行,写多行汇编语句的话,可以加上\n\t , 或者是加上分号。1 显示字符串 依然是使用的 bios 的13 号中断。3 简单看一下 内联汇编的 规则。2 然后是 编写内联汇编函数。这里的内容就比较重要了。注意这里的 = 号。原创 2024-09-01 17:54:51 · 318 阅读 · 0 评论 -
4-8 进入C语言,并跳转到loarder(1)
所以, 可以知道, 首先是 编译出 boot.bin . loader.bin , 然后 将这两个文件 拼接到 disk1.img 中,并且具体的位置,也设置好了。然后再 loader 目录先 , 创建 start.S , loader.h , loader_16.h , loader_32.h 文件。3 一直跳转到 loader 目录的C语言函数里面, 做循环操作。那么如何 从 , boot 目录,跳转到 loader 目录呢?2 然后是 , 再源码中新建新的loader 目录。原创 2024-09-01 17:14:07 · 404 阅读 · 0 评论 -
4-7 使用bios 中断 读取磁盘
首先测试的是, 将第0 个扇区后面的几个扇区,全部加载到 1M 的内存中, boot+loader 都是再boot.bin 中。可以看到我 生成的 实际上是 Boot.bin , 这个文件可不止一个扇区, 然后将这个文件写入到了, disk1.img。DH: 磁头,为0, DL : 选择硬盘, BX: 就是加载到的内存中的偏移地址,ES之前已经设置成平坦模型了。CH:柱面,为0 , CL:扇区,启始的扇区数,再bios中是从 1 开始的。这里也是0 , 并且 调试没有报错, 暂时 认为是可以的。原创 2024-09-01 14:27:25 · 465 阅读 · 0 评论 -
4-6 使用bios 中断 显示字符
bios 首先通过中断,访问到 最前面的中断向量表,然后 通过中断向量表然后 访问到具体的 bios 的函数,这些函数是bios 自带的,具体的位置 , 我也不知道。注意: 再编译的时候,首先是 点击 build 选项,然后直接 F5 就可以了。这里的AH 应该是控制光标的,然后前景色与背景色没有设置。主要用到的就是 AX 以及BX。原创 2024-09-01 13:46:30 · 251 阅读 · 0 评论 -
4-5 初始化引导程序
1 原理, 8086 cpu 再bois 中,会自动的将 磁盘的第一个扇区加载到 0x7c00的地址处。2 这里不是很理解 , 为甚么再连接的时候,会指定一个地址,这个地址的作用是什么呢?这里主要是 对环境的设置。3 设置 平坦模型。原创 2024-09-01 13:17:19 · 227 阅读 · 0 评论 -
4-4 初始化引导程序
他采用的方式,没有采用分段的方式,采用的平坦模型,直接给出偏移地址就可以了。也就是说 再实模式下,16位,只能使用 AX,BX,CX,DX,寄存器。可以看到 这里的boot 程序,仅指 512 字节的程序。所以这512 字节的主要任务就是 再做一次跳转。3 然后就是加载操作系统,并跳转到操作系统执行。1 他这个检测内存的容量,我想知道是怎么做的。他采用的是第二种方式。ES,FS,GS, 都是数据寄存器。接下来就是关于 通用寄存器的了解。2 然后就是模式的切换。这是 他的总体的逻辑。8086 的内存映射。原创 2024-08-31 11:56:58 · 195 阅读 · 0 评论 -
4-3 创建可引导的启动程序
ubuntu 需要安装的包, 再前置课上已经安装好了。这里比较重要的就是 cmake 的使用方法了。下载就是直接再工程模板上 进行编写就可以了。然后是直接使用 工程模板就可以了。目前应该还测试不了,因为没有代码。编译测试一下这个模板看看。首先是介绍了代码的目录。首先是编译环境的搭建。原创 2024-08-31 11:37:37 · 187 阅读 · 0 评论 -
4-2 接管运行控制权
第0个扇区通过 最后的 55 aa 来进行区分,是不是 引导代码。bios 会将第0个扇区的内容加载 到0x7c00 的地址处。后面的 运行引导代码+进入操作系统,是需要自己实现的。实模式 就是16位的模式。原创 2024-08-29 08:02:51 · 208 阅读 · 0 评论 -
37 使用LDT(3)
1 目前 再进图 task0 之前, 汇编中设置的还是 应用的数据段+应用的代码段,所以,还需要再改一下,改成 LDT的数据段+LDT的代码段。解决: 从新 将代码该回到 LDT 之前,测试没问题,然后重新改 LDT。所以: 以后,没测试一步,都要做好git。测试出现问题,莫名司机,但是没有报错。原创 2024-08-26 01:46:42 · 258 阅读 · 0 评论 -
36 使用LDT(2)
并且再 汇编中进入 task0 之前, 之前是压栈的是 GDT中的应用数据段+应用代码段,所以这里也要改成 LDT的数据段+LDT的代码段。然后是更改 TSS表中,将 应用数据段+应用代码段 全部改成 LDT的数据段+代码段。然后再汇编总,再进入 task0 之前 ,设置 LDTR寄存器。选择子 是加载 GDT的表项, 还是加载LDT的表象呢?然后是在 TSS表中,设置LDT的表的地址。然后是在GDT表中 增加关于LDT的描述。首先是 定义 两个任务的LDT表。这是再GDT表中的选择子。原创 2024-08-25 21:21:11 · 201 阅读 · 0 评论 -
34 增加系统调用(3)
为了保证在系统调用所调用的函数的正常,需要在系统调用调用函数之前,需要再次将参数压入栈中。系统调用 调用完函数之后,需要在释放一下 之前压入的参数。然后是汇编中实现 syscall_hander。然后是使用C语言函数 实现在屏幕上显示字符。然后 sys_show 去调用 系统调用。在进行系统调用前需要 保存现场。然后是在 任务中调用这个函数。系统调用之后需要 释放现场。原创 2024-08-25 09:48:21 · 148 阅读 · 0 评论 -
33 增加系统调用(2)
然后在 任务中调用 系统调用的时候,会从特权级3的栈,切换到特权级0的栈,还需要手动的 压入三个参数,3个参数是在 SYSTEM_CALL 的GDT表中指定的。然后在调用 do_syscalll 之前还要手动的 压入栈中三个参数,这三个参数是从 已经有的栈中取的。然后在调用完do_syscall 之后,还要手动的释放一下。然后是 在汇编中实现关于 系统调用门描述符指向的函数。然后是在汇编中,系统调用完之后,进行 寄存器的出栈。然后是在汇编中,切换内核的数据段。系统调用VS切换特权级的图。原创 2024-08-25 09:48:11 · 216 阅读 · 0 评论 -
32 增加系统调用(1)
这个系统调用 segment selector 指向的时 内核的代码段。因为系统调用需要的权限比较高。offset 指的时 在内核代码中的具体的函数的地址。系统调用在 数据手册中的描述。这是在 GDT 中的描述符。原创 2024-08-24 22:44:34 · 238 阅读 · 0 评论 -
31 两个任务切换 3
4 然后是 还要手动设置一下, 进入中断时 所用到的 DS 数据段寄存器,因为之前未进入中断之前,使用的一直是,特权级3 的数据段。注意: 这里有一个问题,就是, 虽然定时器 设置的时500ms ,但是实际的时间要比这个长,可能时qemu 的问题。首先判断 当前是 tss0 还是tss1 ,如果是tss0 就切换到tss1 , 如果是tss1 就切换到tss0。2 然后在中断中 实现 对于函数的调用,也就是在进行任务的切换。3 然后在实现 task_sched 过程中, 这个函数的实现逻辑是这样的,原创 2024-08-24 12:23:10 · 146 阅读 · 0 评论 -
30 两个任务切换(2)
由于是最开始运行的就是 task0 , 所以 只需要设置部分的 task0 的TSS的内容,当进行切花的时候,cpu 会自动的 将task0 的运行的寄存器,保存到 task0 的TSS的结构体中, 然后当 task1--->task0 的时候,在使用 task0 的TSS的结构体,进行恢复。2 然后是 在GDT表中定义 两个 TSS表, tss 表 有点像 二级页表,指向的是一个 内存的结构体,在结构体中有保存这 关于 TSS的信息。task1 的 TSS的段都要提前的手工的设置。原创 2024-08-04 22:20:49 · 229 阅读 · 0 评论 -
29 两个任务切换(1)
2 给每个进程 在 GDT表中,分配一个 TSS, 这个TSS中 保存着这个进程 所用到的 通用寄存器+段寄存器+ 3个可能的栈, 当进行 进程切换的时候,就是切换到 另一个 TSS表,这个表保存着另一个进行的信息。3 总体的逻辑,是 在一个任务中,会有一个for 循环执行的是 打印的函数,这是一个死循环,然后启用定时器,到时间了切换到另一个任务,这个任务也是一个打印的死循环。1 这里涉及到进程的切换与之前的 特权级的切换还是不一样的。原创 2024-08-04 12:46:46 · 161 阅读 · 0 评论