前言
前两天北方大风,不太好去实行室,《真象还原》的学习有所搁置,这两天继承。
第八章标题内存管理系统,下面分5节。分别是makefile相关、实现assert断言、实现字符串操纵函数、位图相关、内存管理系统。我们的博客也分成这几部分。
贴一下我的第七章博客链接和本章参考的love6博客链接:
《操纵系统真象还原》第七章(1)——中断-CSDN博客
《操纵系统真象还原》第七章(2)——中断-CSDN博客
《操纵系统真象还原》第八章 ---- 初入内存管理系统 涉足MakeFile 了解摸谈一二_makefile内存管理-CSDN博客
makefile相关
简单来说makefile是一种脚本,由make指令实行。通过查验依赖文件的mtime是否比目标文件mtime新,决定是否实行makefile里的指令。
这部分内容在P358页,博客不再具体阐明,使用时多查阅即可。
根本语法
跳转到目标处实行
实行完一个目标后就退出,不再实行背面的指令,格式如下
伪目标
常见伪目标名称截图
自定义变量与系统变量
- 自定义变量定义的格式:变量名=值(字符串)
- 自定义变量引用的格式:$(变量名)
复制代码 系统变量表截图
隐含规则
反斜杠\是多行之间的连续符。井号#用来注释。
在缺少依赖文件时,make指令按照一些默认的习惯,用隐含的规则完善依赖文件。简单的说就是主动编译,根据源码文件.c、.cc、.C、.p生成目标文件.o。
主动化变量
一种能代表一类文件文件名的符号。
- @ ,表现规则中的目标文件名聚集,如果存在多个目标文件, @,表现规则中的目标文件名聚集,如果存在多个目标文件, @,表现规则中的目标文件名聚集,如果存在多个目标文件,@则表现其中每一个文件名。助记,’@’ 很像是at,aim at,表现对准目标。
- $<,表现规则中依赖文件中的第1个文件。助记,‘<’很像是聚集的最左边,也就是第1个。
- , 表现规则中所有依赖文件的聚集,如果聚集中有重复的文件, ^,表现规则中所有依赖文件的聚集,如果聚集中有重复的文件, ,表现规则中所有依赖文件的聚集,如果聚集中有重复的文件,会主动去重。助记,’’很像从上往下 罩的动作,能罩住很大的范围,所以称为聚集。
- $?,表现规则中,所有比目标文件 mtime 更新的依赖文件聚集。助记,’?’表现疑问,make 最大的疑 问就是依赖文件的mtime是否比目标文件的mtime要新。
模式规则
就一句话,%用来匹配恣意多个非空字符。好比%.o代表所有以.o为结尾的文件,g%s.o是以字符g开头的所有以.o 为结尾的文件,make会拿这个字符串模式去文件系统上查找文件,默认为当前路径下。
实现assert断言
简单来说就是写一个debug程序,如果内核运行过程中出错,这个程序告诉我们哪个文件哪个函数哪行出了问题,把相关信息打印到屏幕上,帮助我们解决问题。下面直接给出代码。
interrupt.c
新增部分是两行宏定义和四个函数。关于这个罗列体还有四个函数的声明,看下面的interrupt.h。
- #define EFLAGS_IF 0x00000200 // 中断标志位IF,在EFLAGS寄存器中
- #define GET_EFLAGS_IF(EFLAGS_VAR) asm volatile ("pushfl ; popl %0" : "=g" (EFLAGS_VAR)) // 获取中断标志位IF
- /* 开启中断并返回开启中断之前的状态 */
- enum intr_status intr_enable(void) {
- enum intr_status old_status;
- if(INTR_ON == intr_get_status()) {
- old_status= INTR_ON; // 如果当前中断已经打开,则直接返回
- return old_status;
- }
- else {
- old_status = INTR_OFF; // 如果当前中断关闭,则打开中断
- asm volatile ("sti"); // 开中断
- return old_status;
- }
- }
- /* 关闭中断并返回在关闭中断之前的状态 */
- enum intr_status intr_disable(void) {
- enum intr_status old_status;
- if(INTR_OFF == intr_get_status()) {
- old_status = INTR_OFF; // 如果当前中断已经关闭,则直接返回
- return old_status;
- }
- else {
- old_status = INTR_ON; // 如果当前中断打开,则关闭中断
- asm volatile ("cli" : : : "memory"); // 关中断
- return old_status;
- }
- }
- /* 将中断状态设置为status */
- enum intr_status intr_set_status(enum intr_status status) {
- return (status & INTR_ON) ? intr_enable() : intr_disable();
- }
- /* 获取当前中断状态 */
- enum intr_status intr_get_status(void) {
- uint32_t eflags = 0;
- GET_EFLAGS_IF(eflags);
- return (eflags & EFLAGS_IF) ? INTR_ON : INTR_OFF;
- }
复制代码 interrupt.h
新增部分是一个罗列声明,四个函数声明,直接贴出完整代码
- #ifndef __KERNEL_INTERRUPT_H
- #define __KERNEL_INTERRUPT_H
- #include "stdint.h"
- typedef void* intr_handler;
- void idt_init(void);
- /* 定义中断的两种状态 */
- /*关于枚举体enum,第一项默认值0,后面每项默认+1*/
- enum intr_status {
- INTR_OFF, // 关中断
- INTR_ON // 开中断
- };
- enum intr_status intr_get_status(void); // 获取中断状态
- enum intr_status intr_set_status(enum intr_status status); // 设置中断状态
- enum intr_status intr_enable(void); // 打开中断
- enum intr_status intr_disable(void); // 关闭中断
- #endif
复制代码 debug.h
放在kernel文件夹下。主要内容是assert的声明和打印函数的声明,代码如下。
- #ifndef __KERNEL_DEBUG_H
- #define __KERNEL_DEBUG_H
- void panic_spin(char* filename, int line, const char* func, const char* condition);
- /* __VA_ARGS__代表若干个参数,对应前面的... */
- #define PANIC(...) panic_spin(__FILE__, __LINE__, __func__, __VA_ARGS__)
- #ifdef NDEBUG
- #define ASSERT(condition) ((void)0)
- #else
- #define ASSERT(condition) \
- if (condition){} \
- else { \
- PANIC(#condition); \ /*关于#,学名字符串化宏,相当于宏定义了""字符串标号*/
- }
- #endif /*结束__NDEBUG*/
- #endif /*结束__KERNEL_DEBUG_H*/
复制代码 debug.c
同样放在kernrl文件夹下,实现报错打印函数
- #include "debug.h"
- #include "print.h"
- #include "interrupt.h"
- /* 打印相关信息并悬停程序 */
- void panic_spin(char* filename, int line, const char* func, const char* condition){
- intr_disable(); // 关闭中断
- put_str("\n\n\n!!!kernel panic!!!\n");
- put_str("filename:");put_str((char*)filename);put_str("\n");
- put_str("line:0x"); put_int(line);put_str("\n");
- put_str("function:");put_str((char*)func);put_str("\n");
- put_str("condition:");put_str((char*)condition);put_str("\n");
- while (1);
- }
复制代码 main.c
简单修改一下内核,用来测试assert
- #include "print.h"
- #include "init.h"
- #include "debug.h"
- int main(void){
- put_str("HongBai's OS\n");
- init_all();
- asm volatile("sti");
- ASSERT(1 == 2); // 断言失败,会调用panic_spin函数
- while(1);
- }
复制代码 makefile
相当于是写脚本,目的是编译、毗连、写入之前的文件,整体cv的love6的博客,修改了路径。如果要cv下面的代码,记得修改路径。
- BUILD_DIR = ./build
- ENTRY_POINT = 0xc0001500
- AS = nasm
- CC = gcc
- LD = ld
- LIB = -I lib/ -I lib/kernel/ -I lib/user/ -I kernel/ -I device/
- ASFLAGS = -f elf
- CFLAGS = -Wall -m32 -fno-stack-protector $(LIB) -c -fno-builtin -W -Wstrict-prototypes -Wmissing-prototypes
- LDFLAGS = -m elf_i386 -Ttext $(ENTRY_POINT) -e main -Map $(BUILD_DIR)/kernel.map
- OBJS = $(BUILD_DIR)/main.o $(BUILD_DIR)/init.o $(BUILD_DIR)/interrupt.o \
- $(BUILD_DIR)/timer.o $(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o \
- $(BUILD_DIR)/debug.o
- ############## c代码编译 ###############
- $(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \
- lib/kernel/stdint.h kernel/init.h
- $(CC) $(CFLAGS) $< -o $@
- $(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \
- lib/kernel/stdint.h kernel/interrupt.h device/timer.h
- $(CC) $(CFLAGS) $< -o $@
- $(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \
- lib/kernel/stdint.h kernel/global.h kernel/io.h lib/kernel/print.h
- $(CC) $(CFLAGS) $< -o $@
- $(BUILD_DIR)/timer.o: device/timer.c device/timer.h lib/kernel/stdint.h\
- kernel/io.h lib/kernel/print.h
- $(CC) $(CFLAGS) $< -o $@
- $(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \
- lib/kernel/print.h lib/kernel/stdint.h kernel/interrupt.h
- $(CC) $(CFLAGS) $< -o $@
- ############## 汇编代码编译 ###############
- $(BUILD_DIR)/kernel.o: kernel/kernel.S
- $(AS) $(ASFLAGS) $< -o $@
- $(BUILD_DIR)/print.o: lib/kernel/print.S
- $(AS) $(ASFLAGS) $< -o $@
- ############## 链接所有目标文件 #############
- $(BUILD_DIR)/kernel.bin: $(OBJS)
- $(LD) $(LDFLAGS) $^ -o $@
- .PHONY : mk_dir hd clean all
- mk_dir:
- if [ ! -d $(BUILD_DIR) ]; then mkdir $(BUILD_DIR); fi
- hd:
- dd if=$(BUILD_DIR)/kernel.bin \
- of=/home/hongbai/bochs/bin/c.img \
- bs=512 count=200 seek=10 conv=notrunc
- clean:
- cd $(BUILD_DIR) && rm -f ./*
- build: $(BUILD_DIR)/kernel.bin
- all: mk_dir build hd
复制代码 测试
我们make all,效果如下
运行一下bochs
实现了99%功能,就是打印的有点歪,bug应该出现在print.S里,留待后续修改。
debug(无果)
先去研究了一下print.S,重写了一下换行回车部分代码
- ;关于换行符:我们按照习惯,把换行回车结合起来,实现日常敲下enter的效果,所以换行=换行+回车
- .is_line_feed: ;换行操作:光标挪到下一行行首
- ;内联的回车逻辑
- xor dx,dx
- mov ax,bx
- mov si,80
- div si
- sub bx,dx
- ;单独的换行逻辑
- add bx,80 ;目前是本行行首,再加80就是下一行行首
- cmp bx,2000 ;是否清屏
- jl .set_cursor ;bx<2000,即还在这一屏,就执行
- .is_backspace: ;退格操作:光标bx前移一个显存位置,待删除位置补空格字符
- dec bx ;自减,前移到上一个坐标
- shl bx,1 ;左移1位,等价于*2,坐标位置*2=实际字节偏移量
- mov byte [gs:bx],0x20
- inc bx
- mov byte [gs:bx],0x07 ;上面三行代码等价于mov word [gs:bx],0x0720,即打印一个空格字符,属性是黑底白字。
- shr bx,1 ;右移1位,将地址重新转化为坐标
- jmp .set_cursor ;将光标转移到新位置
复制代码 然而仍旧没解决问题 |