Os_fundmental_more
-
80386实模式 : 而且每一个指针都是指向实际的物理地址
保护模式:保护模式的一个主要目标是确保应用程序无法对操作系统进行破坏
实际上,80386就是通过在实模式下初始化控制寄存器(如GDTR,LDTR,IDTR与TR等管理寄存器)以及页表,然后再通过设置CR0寄存器使其中的保护模式使能位置位,从而进入到80386的保护模式 -
支持内存分页机制,提供了对虚拟内存的良好支持
-
地址是访问内存空间的索引
int boo=1; int *foo=&a; //这里的boo是一个整型变量,foo变量是一个指向boo地址的整型指针变量,foo中储存的内容就是boo的逻辑地址。
-
线性地址空间是80386处理器通过段(Segment)机制控制下的形成的地址空间
每个运行的应用程序有相对独立的一个或多个内存空间段,每个段有各自的起始地址和长度属性 -
在操作系统完成对80386处理器页机制的初始化和配置(主要是需要操作系统通过特定的指令和操作建立页表,完成虚拟地址与线性地址的映射关系)后,应用程序看到的逻辑地址先被处理器中的段管理功能单元转换为线性地址,然后再通过处理器中的页管理功能单元把线性地址转换成物理地址。
- 分段机制启动、分页机制未启动:逻辑地址—>*段机制处理*—>线性地址=物理地址
- 分段机制和分页机制都启动:逻辑地址—>*段机制处理*—>线性地址—>*页机制处理*—>物理地址
-
uCore设计中采用了一定的面向对象编程方法
虽然C 语言对面向对象编程并没有原生支持,但没有原生支持并不等于我们不能用 C 语言写面向对象程序- 目前主要是采用了类似C++的接口(interface)概念,即是让实现细节不同的某类内核子系统(比如物理内存分配器、调度器,文件系统等)有共同的操作方式
- 接口在 C 语言中,表现为一组函数指针的集合
- 物理内存管理子系统 pmm_manager (位于lab2/kern/mm/pmm.h)
-
这样基于此数据结构,我们可以实现不同连续内存分配算法的物理内存管理子系统,而这些物理内存管理子系统需要编写算法,把算法实现在此结构中定义的init(初始化)、init_memmap(分析空闲物理内存并初始化管理)、alloc_pages(分配物理页)、free_pages(释放物理页)函数指针所对应的函数中。而其他内存子系统需要与物理内存管理子系统交互时,只需调用特定物理内存管理子系统所采用的pmm_manager数据结构变量中的函数指针即可
-
这个bootloader可以切换到X86保护模式
lab1中的OS只是一个可以处理时钟中断和显示字符的幼儿园级别OS -
实际make qemu,执行了makefile中 “qemu:”后的命令,
在命令中,由qemu加载了img
命令eg:`-parallel dev' 重定向虚拟并口到主机设备 dev 中。
qemu -hda ucore.img -parallel stdio # 让ucore在qemu模拟的x86硬件环境中执行
-
qemu monitor:
- 很多功能也都是通过Qemu的monitor机制实现的
- Qemu monitor主要由三部分构成monitor client,QMP,monitor server
- 使用 monitor command 监控 QEMU 运行状态
- 在启动 QEMU 的时候,同时也会启动 monitor 的控制台,通过这个控制台,可以与 QEMU 或者运行状态的虚拟机进行交互
gdb之target — GDB远程调试
-
嵌入式开发,一般要用到远程调试,即在本地PC上debug远程target上运行的程序 (这里是因为img/kernel跑在机器简陋的qemu上而们没有gdb调试功能),(gdb通过localhost:1234,链接到qemu中 对 可直接执行文件 进行调试)
-
gdb>target remote IP:2345
-
Ucore中用到的是AT&T格式的汇编
-
这里正好说了一下bootasm.S文件的作用。计算机加电后,由BIOS将bootasm.S生成的可执行代码从硬盘的第一个扇区复制到内存中的物理地址0x7c00处,并开始执行。 此时系统处于实模式。可用内存不多于1M。
-
GDT全称是Global Descriptor Table,中文名称叫“全局描述符表”,想要在“保护模式”下对内存进行寻址就先要有 GDT。GDT 表里的每一项叫做“段描述符”,用来记录每个内存分段的一些属性信息,每个“段描述符”占 8 字节。
-
Bootload的启动过程可以概括如下:
首先,BIOS将第一块扇区(存着bootloader)读到内存中物理地址为0x7c00的位置,同时段寄存器CS值为0x0000,IP值为0x7c00,之后开始执行bootloader程序。CLI屏蔽中断(屏蔽所有的中断:为中断提供服务通常是操作系统设备驱动程序的责任,因此在bootloader的执行全过程中可以不必响应任何中断,中断屏蔽是通过写CPU提供的中断屏蔽寄存器来完成的);CLD使DF复位,即DF=0,通过执行cld指令可以控制方向标志DF,决定内存地址是增大(DF=0,向高地址增加)还是减小(DF=1,向地地址减小)。设置寄存器 ax,ds,es,ss寄存器值为0;A20门被关闭,高于1MB的地址都默认回卷到0,所以要激活A20,给8042发命令激活A20,8042有两个IO端口:0x60和0x64, 激活流程: 发送0xd1命令到0x64端口 --> 发送0xdf到0x60,打开A20门。从实模式转换到保护模式(实模式将整个物理内存看成一块区域,程序代码和数据位于不同区域,操作系统和用户程序并没有区别对待,而且每一个指针都是指向实际的物理地址,地址就是IP值。这样,用户程序的一个指针如果指向了操作系统区域或其他用户程序区域,并修改了内容,那么其后果就很可能是灾难性的),所以就初始化全局描述符表使得虚拟地址和物理地址匹配可以相互转换;lgdt汇编指令把通过gdt处理后的(asm.h头文件中处理函数)描述符表的起始位置和大小存入gdtr寄存器中;将CR0的第0号位设置为1,进入保护模式;指令跳转由代码段跳到protcseg的起始位置。设置保护模式下数据段寄存器;设置堆栈寄存器并调用bootmain函数;
-
GRUB是一个专业的Bootloader,它对这些提供了很好的支持。
-
中断描述符表(Interrupt Descriptor Table,IDT)
-
可以通过函数print_stackframe来跟踪函数调用堆栈中记录的返回地址
-
sign.c:一个C语言小程序,是辅助工具,用于生成一个符合规范的硬盘主引导扇区。
-
tools/vector.c:生成vectors.S,此文件包含了中断向量处理的统一实现。
-
kern/driver/intr.[ch]:实现了通过设置CPU的eflags来屏蔽和使能中断的函数;
-
kern/driver/intr.[ch]:实现了通过设置CPU的eflags来屏蔽和使能中断的函数;
-
kern/trap/trap.[ch]:紧接着第二步初步处理后,继续完成具体的各种中断处理操作;
-
libs/defs.h:包含一些无符号整型的缩写定义。
-
Libs/x86.h:一些用GNU C嵌入式汇编实现的C函数(由于使用了inline关键字,所以可以理解为宏)。
-
以Intel 80386为例,计算机加电后,CPU从物理地址0xFFFFFFF0(由初始化的CS:EIP确定,此时CS和IP的值分别是0xF000和0xFFF0))开始执行。在0xFFFFFFF0这里只是存放了一条跳转指令,通过跳转指令跳到BIOS例行程序起始点。BIOS做完计算机硬件自检和初始化后,会选择一个启动设备(例如软盘、硬盘、光盘等),并且读取该设备的第一扇区(即主引导扇区或启动扇区)到内存一个特定的地址0x7c00处,然后CPU控制权会转移到那个地址继续执行。至此BIOS的初始化工作做完了,进一步的工作交给了ucore的bootloader。
-
键盘的初始化函数kbd_init
-
...... // 通过中断控制器使能键盘输入中断 pic_enable(IRQ_KBD);
-
时钟的初始化函数clock_init
-
电脑上有个硬件设备叫做中断控制器
-
从CPU的角度看,它的工作就是一直读取指令,然后执行。如果没有意外,这个过程会一直持续下去。
-
现在我的年到不就是while(1)吗
-
关键原理就是CPU有个外部时钟,这是一个倒数计时器,初始时会设置一个数字,比如1000,然后每个时钟脉冲数字减一,减到0的时候,就给CPU发一个信号,CPU会中断当前程序,来处理这个信号,这个信号的处理程序会重置计时器,并执行信号处理函数,如此反复,起到了时间分片的效果。
信号处理函数可能会重新选择另一个任务来执行,这个就是进程切换。 -
实际中,能打断当前程序的中断事件有很多种,包括硬中断和软中断两大类