一、makefile简介
1. makefile是什么
- make和makefile:
- 依赖关系定义在makefile文件中,make程序通过解析makefile文件自动找出变更的文件以及依赖此变更文件的相关文件,然后对所有受影响的相关文件执行事先定义好的命令规则。
- make和makefile并不编译文件。
- 关系类似于脚本解析器和脚本语言文件。makefile相当于脚本语言文件,其中所写的内容必须遵循make所定义的语法;make程序是文件makefile的解析器,它定义了各种关键字、语法结构、函数、变量等。make程序解析makefile中的内容,从而产生不同的行为。
2. makefile基本语法
目标文件:依赖文件
[Tab]命令
- makefile基本语法包括三部分
- 目标文件是指此规则中想要生成的文件,可以是.o的目标文件,也可以是可执行文件,也可以是个伪目标。
- 依赖文件是指要生成此规则中的目标文件,需要哪些文件,是列表。
- 命令是指此规则中要执行的动作,这些动作是各种shell命令。命令可以有多个,但是一个命令要单独占一行,在行首必须以Tab开头。
- 意义:要想生成目标文件,需要提前准备好依赖文件。如果依赖文件列表中任意一个文件比目标文件新,就去执行规则中的命令。
- 在Linux中,文件分为属性和数据两部分。每个文件有三种时间,分别用于记录与文件属性和数据相关的时间:
时间 | 英文 | 作用 |
---|
atime | access time | 访问文件数据部分的时间,每次读取文件数据部分时都会更新atime |
ctime | change time | 文件属性或数据的改变时间,每当文件的属性或数据被修改时,就会更新ctime |
mtime | modify time | 文件数据部分的修改时间,每次文件的数据被修改时就会更新mtime |
- make程序分别获取依赖文件和目标文件的mtime,对比依赖文件的mtime是否比目标文件的mtime新,就知道是否要执行规则中的命令。
- stat命令可以查看时间
cat -n makefile
1:2
echo "makefile test ok"
- 若文件是最新的,则make不会执行echo,会提示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c8a75b7b15f943908603678bfa6761de.png)
- 当makefile中有很多目标时,可以用目标名称作为make的参数,采用“make 目标名称”的方式单独执行目标名称处的规则。即使后面有其他目标也不会执行。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/22595a9c88b14a1c81459e10fd9200fb.png)
3. 伪目标
- make规定:当规则中不存在依赖文件时,这个目标文件名就称为——伪目标。
- 伪目标:不产生真正的目标文件,所以也就不需要依赖文件。伪目标就是单纯地执行指令。
all:
@echo "test ok"
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/705d6049ef00486f8eb1ab7f23580cd9.png)
- 伪目标不能与真实目标文件同名,可以用关键字“.PHONY”来修饰伪目标,格式为:.PHONY:伪目标名。
.PHONY:clean
clean:
rm ./build
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/189f4725a47a4d79a5a80fe58b100ff2.png)
4. make:递归式推导目标
- 在makefie的目标中,是以递归的方式逐层向上查找目标的。
- 例:通过make all来编译test.bin。make发现all的依赖文件test.bin不存在,于是去找test.bin的目标文件test1.o和test2.o。发现他们都没有,于是根据目标文件test1.c和test2.c来编译得到目标文件。最后生成可执行文件test.bin进行编译。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/865e1d96b4954ec7a690b85709d7fb2f.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d31f40ce2783478e9640e7d16045a512.png)
5. 自定义变量与系统变量
- makefile中定义变量:变量名=值(字符串),多个值之间用空格分开。
- 引用变量:$(变量名)。在引用变量时,变量名就会被其值(字符串)替换。
- NOTE:字符串不加引号。
test2.o:test2.c
gcc -c -o test2.o test2.c
test1.o:test1.c
gcc -c -o test1.o test1.c
objfiles = test1.o test2.o
test.bin:$(objfiles)
gcc -o test.bin $(objfiles)
all:test.bin
@echo "compile done"
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ddf9833577fa4059831eba2d9ba6b49a.png)
- 在命令相关的系统变量是有默认值的,一般参数相关的变量没有默认值,可以通过重新赋值的方式修改。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d39985e8d99040ad9d509fbcaf52ff0f.png)
6. 隐含规则
- ‘’:一行写不下,在行尾添加字符 ‘’,下一行的内容便被认为是一行的。
- #:注释符。
- 隐含规则只限于那些编译过程中基本固定的依赖关系。
- 若想通过隐含规则自动推导生成目标,存在于文件系统上的文件,除扩展名之外的文件名必须相同。如x.o的C源文件必须为x.c,才能通过隐含规则生成x.o。
objfiles = test1.o test2.o
test.bin:$(objfiles)
gcc test.bin $(objfiles)
all:test.bin
@echo "compile done"
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/648be8da8bb449dfbefa24e8f8a1a5bd.png)
- C程序
$(CC) -c $(CPPFLAGS) $(CFLAGS)
- C++程序
- x.o的生成依赖于x.cc或x.C,生成x.o的命令为:
$(CXX) -c $(CPPFLAGS) $(CFLAGS)
7. 自动化变量
自动化变量 | 代表文件 |
---|
$@ | 规则中目标文件名的集合 |
$< | 规则中依赖文件的第一个文件 |
$^ | 规则中所有依赖文件的集合 |
$? | 规则中,所有比目标文件mtime更新的依赖文件集合 |
test2.o:test2.c
gcc -c -o test2.o test2.c
test1.o:test1.c
gcc -c -o test1.o test1.c
objfiles = test1.o test2.o
test.bin:$(objfiles)
gcc -o $@ $^
all:test bin
@echo :"compile done"
8. 规则模式
- 模式:字符串模子。
- %:用来匹配任意多个非空字符。比如%.o代表所有以.o结尾的文件,g%s.o代表以g开头的所有以.o结尾的文件。
%.o:%.c
gcc -c -o $@ %^
objfiles = test1.o test2.o
test.bin:objfiles
gcc -o $@ $^
all:test.bin
@echo "compile done"
二、实现assert断言
1. 实现开、关中断
- 断言:程序员断定程序运行到此处时,某数据的值一定为多少多少。
- 断言分类:
- 为内核系统使用的ASSERT。
- 为用户进程使用的assert。
- 内核的ASSERT
- 当内核运行出现问题时,多属于严重错误,没必要继续运行下去。
- ASSERT排查出错误后,最好在关中断的情况下打印报错信息。
--------------------代码----------------------
- 关中断函数
- “boot/kernel/interrupt.c”
#include "interrupt.h"
#include "stdint.h"
#include "global.h"
#include "io.h"
#include "print.h"
#define IDT_DESC_CNT 0x21
#define PIC_M_CTRL 0x20
#define PIC_M_DATA 0x21
#define PIC_S_CTRL 0xa0
#define PIC_S_DATA 0xa1
#define EFLAGS_IF 0x00000200
#define GET_FLAGS(EFLAG_VAR) asm volatile("pushfl;popl %0":"=g"(EFLAG_VAR))
struct gate_desc{
uint16_t func_offset_low_word;
uint16_t selector;
uint8_t dcount;
uint8_t attribute;
uint16_t func_offset_high_word;
};
static void make_idt_desc(struct gate_desc* p_gdesc,uint8_t attr,intr_handler function);
static struct gate_desc idt[IDT_DESC_CNT];
extern intr_handler intr_entry_table[IDT_DESC_CNT];
char* intr_name[IDT_DESC_CNT];
intr_handler idt_table[IDT_DESC_CNT];
static void make_idt_desc(struct gate_desc* p_gdesc,uint8_t attr,intr_handler function){
p_gdesc->func_offset_low_word = (uint32_t)function & 0x0000ffff;
p_gdesc->selector = SELECTOR_K_CODE;
p_gdesc->dcount = 0;
p_gdesc->attribute = attr;
p_gdesc->func_offset_high_word = ((uint32_t)function & 0xffff0000) >> 16;
}
static void idt_desc_init(void){
int i;
for(i = 0;i < IDT_DESC_CNT;i++){
make_idt_desc(&idt[i],IDT_DESC_ATTR_DPL0,intr_entry_table[i]);
}
put_str(" idt_desc_init done\n");
}
static void pic_init(void){
outb(PIC_M_CTRL,0x11);
outb(PIC_M_DATA,0x20);
outb(PIC_M_DATA,0x04);
outb(PIC_M_DATA,0x01);
outb(PIC_S_CTRL,0x11);
outb(PIC_S_DATA,0x28);
outb(PIC_S_DATA,0x02);
outb(PIC_S_DATA,0x01);
outb(PIC_M_DATA,0xfe);
outb(PIC_S_DATA,0xff);
put_str(" pic_init done\n");
}
static void general_intr_handler(uint8_t vec_nr){
if(vec_nr == 0x27 || vec_nr == 0x2f){
return;
}
put_str("int vector : 0x");
put_int(vec_nr);
put_char('\n');
}
static void exception_init(void){
int i;
for(i = 0;i < IDT_DESC_CNT;i++){
idt_table[i] = general_intr_handler;
intr_name[i] = "unknown";
}
intr_name[0] = "#DE Divide Error";
intr_name[1] = "#DB Debug Exception";
intr_name[2] = "NMI Interrupt";
intr_name[3] = "BP Breakpoint Exception";
intr_name[4] = "#OF Overflow Exception";
intr_name[5] = "#BR BOUND Range Exceeded Exception";
intr_name[6] = "#UD Invalid Opcode Exception";
intr_name[7] = "#NM Device Not Available Exception";
intr_name[8] = "#DF Double Fault Exception";
intr_name[9] = "Coprocessor Segment Overrun";
intr_name[10] = "#TS Invalid TSS Exception";
intr_name[11] = "#NP Segment Not Present";
intr_name[12] = "#SS Stack Fault Exception";
intr_name[13] = "#GP General Protection Exception";
intr_name[14] = "#PF Page-Fault Exception";
intr_name[16] = "#MF x87 FPU Floating-Point Error";
intr_name[17] = "#AC Alignment Check Exception";
intr_name[18] = "#MC Machine-Check Exception";
intr_name[19] = "#XF SIMD Floating-Point Exception";
}
void idt_init(){
put_str("idt_init start\n");
idt_desc_init();
exception_init();
pic_init();
uint64_t idt_operand = ((sizeof(idt)-1) | ((uint64_t)(uint32_t)idt << 16));
asm volatile("lidt %0" : : "m" (idt_operand));
put_str("idt_init done\n");
}
enum intr_status intr_enable(){
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(){
enum intr_status old_status;
if(INTR_ON == intr_get_status()){
old_status = INTR_ON;
asm volatile("cli" : : : "memory");
return old_status;
}else{
old_status = INTR_OFF;
return old_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(){
uint32_t eflags = 0;
GET_FLAGS(eflags);
return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF;
}
#ifndef __KERNEL_INTERRUPT_H
#define __KERNEL_INTERRUPT_H
#include "stdint.h"
typedef void* intr_handler;
void idt_init(void);
enum intr_status{
INTR_OFF = 0,
INTR_ON = 1
};
enum intr_status intr_get_status(void);
enum intr_status intr_set_status(enum intr_status);
enum intr_status intr_enable(void);
enum intr_status intr_disable(void);
#endif
2. 实现断言ASSERT
- C语言中使用ASSERT:ASSERT(条件表达式);
- C语言中ASSERT是用宏来定义的,原理是判断传给ASSERT的表达式是否成立。成立则什么都不做;不成立则打印错误信息。
--------------------代码----------------------
#ifndef __KERNEL_DEBUG_H
#define __KERNEL_DEBUG_H
void panic_spin(char *filename,int line,const char* func,const char* condition);
#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
#endif
#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!!!!!error!!!!!\n");
put_str("filename:");put_str(filename);put_str("\n");
put_str("line:0x");put_str(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);
}
#include "print.h"
#include "init.h"
#include "debug.h"
int main(void){
put_str("I am kernel\n");
init_all();
ASSERT(1==2);
while(1);
return 0;
}
3. 通过makefile编译
--------------------代码----------------------
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
##-fno-builtin是告诉编译器不要采用内部函数 -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
## OBJS用来存储所有目标文件名,不要用%.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 lib/kernel/io.h lib/kernel/print.h
$(CC) $(CFLAGS) $< -o $@
$(BUILD_DIR)/timer.o:device/timer.c device/timer.h lib/kernel/stdint.h lib/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
###fi为终止符
hd:
dd if=$(BUILD_DIR)/kernel.bin of=/home/lily/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc
clean: ##将build目录下文件清空
cd $(BUILD_DIR) && rm -f .
- 执行make all
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/66a609df5ea34e92a3164e2d988d7f91.png)
- 启动bochs
bin/bochs -f bochsrc.disk
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/cf1ba2f987cc4ecf92b23bc8f7935bd7.png)
三、实现字符串操作函数
--------------------代码----------------------
#include "string.h"
#include "global.h"
#include "debug.h"
void memset(void* dst_,uint8_t value,uint32_t size){
ASSERT(dst_ != NULL);
uint8_t* dst = (uint8_t*)dst_;
while(size--)
*dst++ = value;
}
void memcpy(void* dst_,const void* src_,uint32_t size){
ASSERT(dst_ != NULL && src_ != NULL);
uint8_t* dst = (uint8_t*)dst_;
uint8_t* src = (uint8_t*)src_;
while(size--)
*dst++ = *src++;
}
int memcmp(const char* a_,const char* b_,uint32_t size){
const char* a = a_;
const char* b = b_;
ASSERT(a != NULL && b != NULL);
while(size--){
if(*a != *b)
return *a > *b ? 1 : -1;
a++;
b++;
}
return 0;
}
char* strcpy(char* dst_,const char* src_){
ASSERT(dst_ != NULL && src_ != NULL);
char* temp = dst_;
while((*temp++ = *src_++));
return dst_;
}
uint32_t strlen(const char* str){
ASSERT(str != NULL);
uint32_t len = 0;
const char* p = str;
while(*p++){
len++;
}
return len;
}
int8_t strcmp(const char* a,const char* b){
ASSERT(a != NULL && b != NULL);
while(*a != 0 && *a++ == *b++);
return *a > *b ? 1 : *a < *b;
}
char* strchr(const char* str,const uint8_t ch){
ASSERT(str != NULL );
while(*str != 0){
if(*str == ch)
return (char*)str;
str++;
}
return NULL;
}
char* strtchr(const char* str,const uint8_t ch){
ASSERT(str != NULL);
char* last_str = NULL;
while(*str != 0){
if(*str == ch)
last_str = (char*)str;
str++;
}
return last_str;
}
char* strcat(char* dst_,const char* src_){
ASSERT(dst_ != NULL && src_ != NULL);
char* p = dst_;
while(*p++);
p--;
while((*p++ = *src_++));
return dst_;
}
uint32_t strchrs(const char* str,uint8_t ch){
ASSERT(str != NULL);
uint32_t cnt = 0;
while(*str != 0){
if(*str == ch)
cnt++;
str++;
}
return cnt;
}
- “/home/lily/OS/boot/lib/string.h”
#ifndef __LIB_STRING_H
#define __LIB_STRING_H
#include "stdint.h"
#define NULL 0
void memset(void* dst_,uint8_t value,uint32_t size);
void memcpy(void* dst_,const void* src_,uint32_t size);
int memcmp(const char* a_,const char* b_,uint32_t size);
char* strcpy(char* dst_,const char* src_);
uint32_t strlen(const char* str);
int8_t strcmp(const char* a,const char* b);
char* strchr(const char* str,const uint8_t ch);
char* strtchr(const char* str,const uint8_t ch);
char* strcat(char* dst_,const char* src_);
uint32_t strchrs(const char* str,uint8_t ch);
#endif
- “/home/lily/OS/boot/lib/kernel/stdint.h”
- 如果编译器识别不了bool的话:
#ifndef __LIB_STDINT_H
#define __LIB_STDINT_H
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed long long int int64_t;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long int uint64_t;
typedef enum{false = 0,true = 1} bool;
#endif
四、位图bitmap及其函数实现
1. 位图的定义与实现
- 位图的本质是一串二进制位,用字节型数组实现。
- 在一个字节里,位是从右向左排序的:例如0xff。所以在位图中,一字节中的最右位为0位,做左位为7位。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/3746020bd9bb47bf932743b1f772835f.png)
--------------------代码----------------------
- “boot/lib/kernel/bitmap.h”
#ifndef __LIB_KERNEL_BITMAP_H
#define __LIB_KERNEL_BITMAP_H
#include "global.h"
#define BITMAP_MASK 1
struct bitmap{
uint32_t btmp_bytes_len;
uint8_t* bits;
};
void bitmap_init(struct bitmap* btmp);
bool bitmap_scan_test(struct bitmap* btmp,uint32_t bit_idx);
int bitmap_scan(struct bitmap* btmp,uint32_t cnt);
void bitmap_set(struct bitmap* btmp,uint32_t bit_idx,int8_t value);
#endif
- “boot/lib/kernel/bitmap.c”
#include "bitmap.h"
#include "stdint.h"
#include "string.h"
#include "print.h"
#include "interrupt.h"
#include "debug.h"
void bitmap_init(struct bitmap* btmp){
memset(btmp->bits,0,btmp->btmp_bytes_len);
}
bool bitmap_scan_test(struct bitmap* btmp,uint32_t bit_idx){
uint32_t byte_idx = bit_idx / 8;
uint32_t bit_odd = bit_idx % 8;
return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd));
}
int bitmap_scan(struct bitmap* btmp,uint32_t cnt){
uint32_t idx_byte = 0;
while ((btmp->bits[idx_byte] == 0xff) && (idx_byte < btmp->btmp_bytes_len))
idx_byte++;
ASSERT(idx_byte <= btmp->btmp_bytes_len);
if(idx_byte == btmp->btmp_bytes_len)
return -1;
int idx_bit = 0;
while (btmp->bits[idx_byte] & (uint8_t)(BITMAP_MASK << idx_bit))
idx_bit++;
int bit_idx_start = -1;
if(cnt == 1){
bit_idx_start = idx_byte*8 + idx_bit;
return bit_idx_start;
}
uint32_t bit_left = btmp->btmp_bytes_len*8 - (idx_byte*8 + idx_bit);
uint32_t next_bit = idx_byte*8 + idx_bit + 1;
uint32_t count = 1;
while (bit_left > 0){
if(!bitmap_scan_test(btmp,next_bit))
count++;
else
count = 0;
if(count == cnt){
bit_idx_start = next_bit - cnt + 1;
break;
}
next_bit++;
}
return bit_idx_start;
}
void bitmap_set(struct bitmap* btmp,uint32_t bit_idx,int8_t value){
ASSERT((value == 0) | (value == 1));
uint32_t byte_idx = bit_idx / 8;
uint32_t bit_odd = bit_idx % 8;
if(value){
btmp->bits[byte_idx] |= (BITMAP_MASK << bit_odd);
}else{
btmp->bits[byte_idx] &= ~(BITMAP_MASK << bit_odd);
}
};
五、内存管理系统
1. 内存池规划
- 为保证OS的正常运行,将物理内存池分为用户物理内存池和内核物理内存池。
- 内存池中的内存单位为页,4KB。
- 内存分配过程:
- 内核申请内存:当内核申请内存时,从内核自己的虚拟地址池中分配虚拟地址,再从内核物理池(内核专用)中分配物理内存,然后在内核自己的页表中将两种地址建立好映射关系。
- 用户进程申请内存:OS从用户进程自己的虚拟地址池中分配空闲虚拟地址,然后再从用户物理内存池(所有用户进程共享的)中分配空闲的物理内存,然后在该用户进程自己的页表中将这两种地址建立好映射关系。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/179a9dd2bd4c4e24920afd2dbe7fe1ab.png)
- 多个进程可以拥有相同的虚拟地址,是因为这些虚拟地址所对应的物理地址是不同的。但在同一个进程中的虚拟地址是唯一的,这通常是由链接器为其分配的,由链接器负责虚拟地址的唯一性。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/847934a81c4f41f08c7cf6f956b6fc17.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/53aaf8c07bc54b4c965f34d190371da3.png)
--------------------代码----------------------
- “/home/lily/OS/boot/kernel/memory.h”
#ifndef __KERNEL_MEMORY_H
#define __KERNEL_MEMORY_H
#include "stdint.h"
#include "bitmap.h"
struct virtual_addr
{
struct bitmap vaddr_bitmap;
uint32_t vaddr_start;
};
extern struct pool kernel_pool, user_pool;
void mem_init(void);
#endif
- “/home/lily/OS/boot/kernel/memory.c”
#include "memory.h"
#include "stdint.h"
#include "print.h"
#define PG_SIZE 4096
#define MEM_BITMAP_BASE 0xc009a000
#define K_HEAP_START 0xc0100000
struct pool{
struct bitmap pool_bitmap;
uint32_t phy_addr_start;
uint32_t pool_size;
};
struct pool kernel_pool,user_pool;
struct virtual_addr kernel_vaddr;
static void mem_pool_init(uint32_t all_mem){
put_str(" mem_pool_init start\n");
uint32_t page_table_size = PG_SIZE * 256;
uint32_t used_mem = page_table_size + 0x100000;
uint32_t free_mem = all_mem - used_mem;
uint16_t all_free_pages = free_mem/PG_SIZE;
uint16_t kernel_free_pages = all_free_pages / 2;
uint16_t user_free_pages = all_free_pages - kernel_free_pages;
uint32_t kbm_length = kernel_free_pages / 8;
uint32_t ubm_length = user_free_pages / 8;
uint32_t kp_start = used_mem;
uint32_t up_start = kp_start + kernel_free_pages*PG_SIZE;
kernel_pool.phy_addr_start = kp_start;
user_pool.phy_addr_start = up_start;
kernel_pool.pool_size = kernel_free_pages*PG_SIZE;
user_pool.pool_size = user_free_pages*PG_SIZE;
kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;
user_pool.pool_bitmap.btmp_bytes_len = ubm_length;
kernel_pool.pool_bitmap.bits = (void*)MEM_BITMAP_BASE;
user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length);
put_str(" kernel_pool_bitmap_start:");
put_int((int)kernel_pool.pool_bitmap.bits);
put_str(" kernel_pool_phy_addr_start:");
put_int(kernel_pool.phy_addr_start);
put_str("\n");
put_str("user_pool_bitmap_start:");
put_int((int)user_pool.pool_bitmap.bits);
put_str(" user_pool_phy_addr_start:");
put_int((int)user_pool.phy_addr_start);
put_str("\n");
bitmap_init(&kernel_pool.pool_bitmap);
bitmap_init(&user_pool.pool_bitmap);
kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;
kernel_vaddr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length);
kernel_vaddr.vaddr_start = K_HEAP_START;
bitmap_init(&kernel_vaddr.vaddr_bitmap);
put_str(" mem_pool_init done\n");
}
void mem_init(){
put_str("mem_init start\n");
uint32_t mem_bytes_total = (*(uint32_t*)(0xb00));
mem_pool_init(mem_bytes_total);
put_str("mem_init done\n");
}
- “/home/lily/OS/boot/kernel/init.c”
#include "init.h"
#include "print.h"
#include "interrupt.h"
#include "../device/timer.h"
#include "memory.h"
void init_all(){
put_str("init_all\n");
idt_init();
timer_init();
mem_init();
}
- “/home/lily/OS/boot/makefile”
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
##-fno-builtin是告诉编译器不要采用内部函数 -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 \
$(BUILD_DIR)/string.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/bitmap.o
## OBJS用来存储所有目标文件名,不要用%.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 lib/kernel/io.h lib/kernel/print.h
$(CC) $(CFLAGS) $< -o $@
$(BUILD_DIR)/timer.o:device/timer.c device/timer.h lib/kernel/stdint.h lib/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)/string.o:lib/string.c lib/string.h kernel/debug.h kernel/global.h lib/kernel/stdint.h
$(CC) $(CFLAGS) $< -o $@
$(BUILD_DIR)/memory.o:kernel/memory.c kernel/memory.h lib/kernel/stdint.h lib/kernel/bitmap.h kernel/debug.h lib/string.h
$(CC) $(CFLAGS) $< -o $@
$(BUILD_DIR)/bitmap.o:lib/kernel/bitmap.c lib/kernel/bitmap.h lib/string.h kernel/interrupt.h lib/kernel/print.h kernel/debug.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
###fi为终止符
hd:
dd if=$(BUILD_DIR)/kernel.bin of=/home/lily/bochs/hd60M.img bs=512 count=200 seek=9 conv=notrunc
clean: ##将build目录下文件清空
cd $(BUILD_DIR) && rm -f .
- make all编译
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/6cf6233825824fb29a4cda4c15220c04.png)
- 启动bochs
bin/bochs -f bochsrc.disk
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/145a4a5712894feeb5b6401d553b251b.png)
2. 内存管理第一步:分配页内存
- 32位虚拟地址的转换过程:
- 页目录表的物理地址 + pde索引*4 = 页目录pde的物理地址。
- 页目录pde的物理地址 + 页目录项pte*4 = 页目录项pte物理地址
- 页目录项pte物理地址 + 物理页内偏移地址 = 物理地址
- 如何得到虚地址vaddr的pte*指针(页表项的地址 = 指向物理页的地址)?
- 高10位放置页目录表的物理地址(0xffc0_0000),找到页目录表的物理地址。
- 中10位放置PDE(vaddr的高10位,pde,pte,12位偏移),找到页目录项,即页表的地址。
- 低12位放置PTE*4,得到页表项,即页的物理地址。
- 如何得到虚地址vaddr的pde*指针(页目录项的地址 = 指向页表的物理地址)?
- 高10位和中10位都放置页目录表的物理地址(0xffc0_0000),使指针停留在页目录表。
- 低12位放置PDE*4,得到页目录项,即页表的物理地址。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/073004a3e5bd44288c85638a04966560.png)
--------------------代码----------------------
- “/home/lily/OS/boot/kernel/memory.c”
#include "memory.h"
#include "bitmap.h"
#include "stdint.h"
#include "global.h"
#include "debug.h"
#include "print.h"
#include "string.h"
#define PG_SIZE 4096
#define PDE_IDX(addr) ((addr & 0xffc00000) >> 22)
#define PTE_IDX(addr) ((addr & 0x003ff000) >> 12)
#define MEM_BITMAP_BASE 0xc009a000
#define K_HEAP_START 0xc0100000
struct pool{
struct bitmap pool_bitmap;
uint32_t phy_addr_start;
uint32_t pool_size;
};
struct pool kernel_pool,user_pool;
struct virtual_addr kernel_vaddr;
static void* vaddr_get(enum pool_flags pf,uint32_t pg_cnt){
int vaddr_start = 0,bit_idx_start = -1;
uint32_t cnt = 0;
if(pf == PF_KERNEL){
bit_idx_start = bitmap_scan(&kernel_vaddr.vaddr_bitmap,pg_cnt);
if(bit_idx_start == -1)
return NULL;
while (cnt < pg_cnt)
bitmap_set(&kernel_vaddr.vaddr_bitmap,bit_idx_start + cnt++,1);
vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start*PG_SIZE;
}else{
}
return (void*)vaddr_start;
}
uint32_t* pte_ptr(uint32_t vaddr){
uint32_t* pte = (uint32_t*)(0xffc00000 + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr)*4);
return pte;
}
uint32_t* pde_ptr(uint32_t vaddr){
uint32_t* pde = (uint32_t*)((0xfffff000) + PDE_IDX(vaddr)*4);
return pde;
}
static void* palloc(struct pool* m_pool){
int bit_idx = bitmap_scan(&m_pool->pool_bitmap,1);
if(bit_idx == -1)
return NULL;
bitmap_set(&m_pool->pool_bitmap,bit_idx,1);
uint32_t page_phyaddr = m_pool->phy_addr_start + (bit_idx*PG_SIZE);
return (void*)page_phyaddr;
}
static void page_table_add(void* _vaddr,void* _page_phyaddr){
uint32_t vaddr = (uint32_t)_vaddr;
uint32_t page_phyaddr = (uint32_t)_page_phyaddr;
uint32_t* pte = pte_ptr(vaddr);
uint32_t* pde = pde_ptr(vaddr);
if(*pde & 0x00000001){
ASSERT(!(*pte & 0x00000001));
if(!(*pte & 0x00000001)){
*pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
}else{
PANIC("pte repeat!");
*pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
}
}else{
uint32_t pde_phyaddr = (uint32_t)palloc(&kernel_pool);
*pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
memset((void*)((int)pte & 0xfffff000),0,PG_SIZE);
ASSERT(!(*pte & 0x00000001));
*pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
}
}
void* malloc_page(enum pool_flags pf,uint32_t pg_cnt){
ASSERT(pg_cnt > 0 && pg_cnt < 3840);
void* vaddr_start = vaddr_get(pf,pg_cnt);
if(vaddr_start == NULL)
return NULL;
uint32_t vaddr = (uint32_t)vaddr_start;
uint32_t cnt = (uint32_t)pg_cnt;
struct pool* mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool;
while (cnt--){
void* page_phyaddr = palloc(mem_pool);
if(page_phyaddr == NULL)
return NULL;
page_table_add((void*)vaddr,page_phyaddr);
vaddr += PG_SIZE;
}
return vaddr_start;
}
void* get_kernel_pages(uint32_t pg_cnt){
void* vaddr = malloc_page(PF_KERNEL,pg_cnt);
if(vaddr == NULL)
return NULL;
memset(vaddr,0,pg_cnt*PG_SIZE);
return vaddr;
}
static void mem_pool_init(uint32_t all_mem){
put_str(" mem_pool_init start\n");
uint32_t page_table_size = PG_SIZE * 256;
uint32_t used_mem = page_table_size + 0x100000;
uint32_t free_mem = all_mem - used_mem;
uint16_t all_free_pages = free_mem/PG_SIZE;
uint16_t kernel_free_pages = all_free_pages / 2;
uint16_t user_free_pages = all_free_pages - kernel_free_pages;
uint32_t kbm_length = kernel_free_pages / 8;
uint32_t ubm_length = user_free_pages / 8;
uint32_t kp_start = used_mem;
uint32_t up_start = kp_start + kernel_free_pages*PG_SIZE;
kernel_pool.phy_addr_start = kp_start;
user_pool.phy_addr_start = up_start;
kernel_pool.pool_size = kernel_free_pages*PG_SIZE;
user_pool.pool_size = user_free_pages*PG_SIZE;
kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;
user_pool.pool_bitmap.btmp_bytes_len = ubm_length;
kernel_pool.pool_bitmap.bits = (void*)MEM_BITMAP_BASE;
user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length);
put_str(" kernel_pool_bitmap_start:");
put_int((int)kernel_pool.pool_bitmap.bits);
put_str(" kernel_pool_phy_addr_start:");
put_int(kernel_pool.phy_addr_start);
put_str("\n");
put_str("user_pool_bitmap_start:");
put_int((int)user_pool.pool_bitmap.bits);
put_str(" user_pool_phy_addr_start:");
put_int((int)user_pool.phy_addr_start);
put_str("\n");
bitmap_init(&kernel_pool.pool_bitmap);
bitmap_init(&user_pool.pool_bitmap);
kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;
kernel_vaddr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length);
kernel_vaddr.vaddr_start = K_HEAP_START;
bitmap_init(&kernel_vaddr.vaddr_bitmap);
put_str(" mem_pool_init done\n");
}
void mem_init(){
put_str("mem_init start\n");
uint32_t mem_bytes_total = (*(uint32_t*)(0xb00));
mem_pool_init(mem_bytes_total);
put_str("mem_init done\n");
}
- “/home/lily/OS/boot/kernel/memory.h”
#ifndef __KERNEL_MEMORY_H
#define __KERNEL_MEMORY_H
#include "stdint.h"
#include "bitmap.h"
struct virtual_addr
{
struct bitmap vaddr_bitmap;
uint32_t vaddr_start;
};
enum pool_flags{
PF_KERNEL = 1,
PF_USER = 2
};
#define PG_P_1 1
#define PG_P_0 0
#define PG_RW_R 0
#define PG_RW_W 2
#define PG_US_S 0
#define PG_US_U 4
extern struct pool kernel_pool, user_pool;
static void* vaddr_get(enum pool_flags pf,uint32_t pg_cnt);
uint32_t* pte_ptr(uint32_t vaddr);
uint32_t* pde_ptr(uint32_t vaddr);
static void* palloc(struct pool* m_pool);
static void page_table_add(void* _vaddr,void* _page_phyaddr);
void* malloc_page(enum pool_flags pf,uint32_t pg_cnt);
void* get_kernel_pages(uint32_t pg_cnt);
static void mem_pool_init(uint32_t all_mem);
void mem_init(void);
#endif
- “/home/lily/OS/boot/kernel/main.c”
#include "print.h"
#include "init.h"
#include "debug.h"
#include "memory.h"
int main(void){
put_str("I am kernel\n");
init_all();
void* addr = get_kernel_pages(3);
put_str("\n get_kernel_page start vaddr is:");
put_int((uint32_t)addr);
put_str("\n");
while(1);
return 0;
}
make all
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/92a064e0e6c04e5a8e11682b1ca0d131.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/eff2911eb1ee49199e70443b8bae8a43.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/3d561e2428f34d788751abee89ffed8a.png)