网站建设 采集,WordPress中设置域名的数据库在哪,福步外贸论坛怎样注册,wordpress顶部图像使用小工具前言#xff1a;今天我们要完成我们操作系统的内存管理#xff0c;以及一些数据结构和小组件的实现#xff0c;在此之前大家需要了解我们前几天一些重要文件的内存地址存放在哪#xff0c;以便我们更好的去编写内存管理模块 一#xff0c;实现ASSERT断言 不知道大家有没有… 前言今天我们要完成我们操作系统的内存管理以及一些数据结构和小组件的实现在此之前大家需要了解我们前几天一些重要文件的内存地址存放在哪以便我们更好的去编写内存管理模块 一实现ASSERT断言 不知道大家有没有在C或者Java中使用ASSERT断言函数没用过的话也没关系我们要实现这个函数的原因很直接当内核出现问题我们不可能在bochs中一行一行汇编代码区查看问题所以我们需要一个函数来起到Debug的作用废话不多说先开始着手准备吧 ASSERT函数实现流程
① 在关中断下运行毕竟发生异常时不能被其他的中断给干扰
② 实现其在屏幕上的输出问题代码函数行数 ① 在关中断下运行毕竟发生异常时不能被其他的中断给干扰 首先在interrupt.c中我们来实现开关中断(部分代码更新) #define EFLAGS_IF 0x00000200 //eflags寄存器的if位
#define GET_EFLAGS(EFLAG_VAR) asm volatile(pushfl; popl %0 : g (EFLAG_VAR)) //获取eflags的值/**获取当前中断**/
enum intr_status intr_get_status() {uint32_t eflags 0;GET_EFLAGS(eflags);return (EFLAGS_IF eflags) ? INTR_ON : INTR_OFF;
}/**打开中断**/
enum intr_status intr_enable() {enum intr_status old_status;if (intr_get_status() INTR_ON) {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_get_status() INTR_ON) {old_status INTR_ON;asm volatile(cli);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();
} interrupt.h #ifndef _KERNEL_INTERRUPT_H
#define _KERNEL_INTERRUPT_H
#include stdint.h
typedef void* intr_handler;void idt_init(void);enum intr_status
{INTR_OFF,INTR_ON
};
enum intr_status intr_get_status();
enum intr_status intr_enable();
enum intr_status intr_disable();
enum intr_status intr_set_status(enum intr_status status);//void register_intr(uint32_t vectr, intr_handler func, char* name);
#endif ② 实现ASSERT kernel/debug.h #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__) //这里是ansi c中常用的宏分别是文件地址代码行数函数以及变量#ifdef NDEBUG //这是一个宏变量如果定义了这个宏变量则ASSERT不会起作用#define ASSERT(CONDITION) ((void)0)
#else#define ASSERT(CONDITION)\if(CONDITION){}else{\PANIC(#CONDITION); \}
#endif // NDEBUG#endif // !__KERNEL_DEBUG_Hkernel/debug.c #include debug.h
#include print.h
#include interrupt.hvoid panic_spin(char* filename, int line,const char* func,const char* condition) {intr_disable();put_str(\n\n\n------------!!!OS ERROR!!!------------\n\n\n);put_str(filename:);put_str(filename);put_str(\n);put_str(line:);put_int(line);put_str(\n);put_str(function:);put_str(func);put_str(\n);put_str(condition:);put_str(condition);put_str(\n);put_str(\n\n\n------------!!!OS ERROR!!!------------\n\n\n);while (1);
}c 这样子ASSERT就完成了如果你迫不及待想要试一下你现在可以在main函数里面写一段
ASSERT(12); 然后运行一下 看看是否会出现报错如果出现报错就说明你成功了由于我已经完成后面好几章了忘记截屏成功截图了所以我们接着往下面走吧 二实现字符串操作函数 为了给我们后面的系统代码打下基础我们先把这些路给铺好字符串操作这里我就不过多介绍了相信大家肯定看得懂的 lib/string.h #include string.h
#include global.h
#include debug.h/*将dst_地址起始的后size位置为value*/
void memset(void *dst_, uint8_t value, uint32_t size) {ASSERT(dst_ ! NULL);uint8_t* dst (uint8_t*)dst_;while (size-- 0) {*(dst) value;}
}/*将src_地址后size位的值复制到dst_中*/
void memcpy(void *dst_, const void *src_, uint32_t size) {ASSERT(dst_ ! NULL);ASSERT(src_ ! NULL);uint8_t* dst dst_;const uint8_t* src src_;while (size-- 0) {*(dst) *(src);}
}/*比较如果相等则为0ab为1ab为-1*/
int memcmp(const void* a_, const void* b_, uint32_t size) {const char* a a_;const char* b b_;ASSERT(a ! NULL || b ! NULL);while (size-- 0) {if (*a *b) {return 1;}else if (*a *b) {return -1;}a;b;}return 0;
}/*字符串从src_复制到dst_*/
char* strcpy(char* dst_, const char* src_) {ASSERT(dst_ ! NULL src_ ! NULL);char* dst dst_;while ((*(dst_) *(src_)));return dst;
}/*返回字符串长度*/
uint32_t strlen(const char* str_) {ASSERT(str_ ! NULL);const char* p str_;while (*(p));return p - str_ - 1;
}/*比较两个字符串,ab返回0,ab返回1,ab返回-1*/
int8_t strcmp(const char* a, const char* b) {ASSERT(a ! NULL b ! NULL);while (*a ! 0 *a *b) {a;b;}return *a*b ? -1 : *a*b;
}/*从前往后找到ch在str中首次出现的位置*/
char* strch(const char* str, const uint8_t ch) {ASSERT(str ! NULL);while (*str!0){if (*str ch) {return (char*)str;}str;}return NULL;
}/*从后往前找到ch在str中首次出现的位置*/
char* strrch(const char* str, const uint8_t ch) {ASSERT(str ! NULL);const char* chr NULL;while (*str ! 0) {if (*str ch) {chr str;}str;}return (char*)chr;
}/*将字符串src_拼接到dst后*/
char* strcat(char* dst_, const char* src_) {ASSERT(dst_ ! NULL src_ ! NULL);char* dst dst_;while (*(dst_));--dst_;while ((*(dst_) *(src_)));return dst;
}/*解决内存覆盖问题将拼接的字符串重新分配一个地址*/
char* newstrcat(char* dst_, const char* src_) {ASSERT(dst_ ! NULL src_ ! NULL);char* dst ;char* p dst;while (*(p)*(dst_));--p;while ((*(p) *(src_)));return dst;
}/*在字符串str中查找字符ch出现的次数*/
uint32_t strchrs(const char* str, uint8_t ch) {ASSERT(str ! NULL);uint32_t count 0;while (*str ! 0) {if (*(str) ch) {count;}}return count;
} lib/string.h #ifndef _LIB_STRING_H
#define _LIB_STRING_H
#include stdint.h
void memset(void *dst_, uint8_t value, uint32_t size);
void memcpy(void *dst_, const void *src_, uint32_t size);
int memcmp(const void* a_, const void* 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* strch(const char* str, const uint8_t ch);
char* strrch(const char* str, const uint8_t ch);
char* strcat(char* dst_, const char* src_);
char* newstrcat(char* dst_, const char* src_);
uint32_t strchrs(const char* str, uint8_t ch);
#endif 三bitmap 前面大费周章进行的铺路现在终于走到内存管理的门口了我们现在要介绍的是管理内存的数据结构---bitmap 什么是bitmap从英语的角度解读你应该可以理解就是位图也就是一个矩阵中存放的都是bit位0和1。 那这个位图又怎么应用到我们内存管理中来呢 大家仔细想一想在之前我们启用了内存分页对不对在我们的分页系统中一个页对应4KB物理内存那在bitmap中我们这些bit映射的就是一个页而0和1代表此页是否被占用是不是很简单而bit的位置就可以映射到当前物理内存的位置。 我们该如何设计我们的位图 ① 用bit数组数组元素存放的是0或者1 ② 用字节数组一个字节有8位代表这一个数组元素管理8个资源单位 在此我选择的是第二种方法两种方法都不难你也可以选择第一种方法去实现都是的 位图实现 lib/kernel/bitmap.h #ifndef _LIB_BITMAP_H
#define _LIB_BITMAP_H
#include global.h
#include stdint.h
#define BITMAP_MASK 1
struct bitmap {uint32_t btmp_bytes_len; //bitmap的字节长度uint8_t* bits; //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 // _LIB_BITMAP_Hlib/kernel/bitmap.c #include bitmap.h
#include string.h
#include debug.h/*初始化bitmap*/
void bitmap_init(struct bitmap* btmp) {memset(btmp-bits, 0, btmp-btmp_bytes_len);
}/*判断 bit_idx位是否为1为1则返回1否则返回0*/
bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx) {uint32_t arr_index bit_idx / 8;//在bits的数组索引uint32_t arr_member_index bit_idx % 8;//在bits的数组元素中的索引return (btmp-bits[arr_index]) (BITMAP_MASK arr_member_index);
}/*在位图中申请连续cnt个位成功返回下标失败返回-1*/
int bitmap_scan(struct bitmap* btmp, uint32_t cnt) {int arr_index 0;//先8位8位的判断看看是否有0while ((0xff btmp-bits[arr_index]) arr_index btmp-btmp_bytes_len) {arr_index;}ASSERT(arr_index btmp-btmp_bytes_len);if (arr_index btmp-btmp_bytes_len) {return -1;}//发现该字节段有0后便进行遍历查找0int arr_member_index 0;while ((btmp-bits[arr_index])(BITMAP_MASK arr_member_index)) {arr_member_index;}int bit_idx_start arr_index * 8 arr_member_index;if (cnt 1) {return bit_idx_start;}int bits_left btmp-btmp_bytes_len * 8 - bit_idx_start;int count 1;int next_bit bit_idx_start 1;//向后遍历有0就1碰到1就重新计数直到走到尽头bit_idx_start -1;while (bits_left--) {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 arr_index bit_idx / 8;//在bits的数组索引uint32_t arr_member_index bit_idx % 8;//在bits的数组元素中的索引if (value) {btmp-bits[arr_index] | (value arr_member_index);}else {btmp-bits[arr_index] ~(value arr_member_index);}} 四内存管理系统
在我们的操作系统中地址被分为了两部分虚拟地址和物理地址物理地址对应着内存资源而虚拟地址经过分页机制映射到相应的物理地址而目前我们内存管理系统要做的事情是
划分相应的内存池物理地址与虚拟地址建立联系为进程分配页空间Ⅰ. 划分内存地址池
首先什么是池深入学习过某些语言的同学应该知道线程池常量池内存池等等
池将资源统一集中的放入池中管理从中取出用后放回这样就可以减少资源的开辟和浪费 那我们该怎么划分内存呢 物理内存地址池在我们的操作系统中有着内核进程与用户进程所以根据此我们应该划分出两个池子内核池与用户池。 虚拟内存地址池虚拟内存其实在分页中我们已经划分好高1G为内核地址低3G为用户地址所以虚拟内存池我们就无需划分了。 池的存取 首先我们内存池存取内存也要按照单位来存取自然而然就是4KB大小的内存块当内存被耗尽时就返回内存不足。 内存管理流程 首先不管是用户进程还是内核进程都会有中途申请内存的过程那么一开始呢就先去虚拟内存池中查看是否有空闲的页可以分配如果有的话再去对应职责的物理内存池中查看是否有分配当分配成功时将虚拟内存的地址和物理内存的地址建立上练习即可。 实现内存管理代码 kernel/memory.h #ifndef _KERNEL_MEMORY_H
#define _KERNEL_MEMORY_H
#include stdint.h
#include bitmap.h/* 内存池职责标记 */
enum pool_flags {PF_KERNEL 1,PF_USER 2
};/* 虚拟内存池 */
struct virtual_addr {struct bitmap vaddr_bitmap;uint32_t vaddr_start; //管理的虚拟位置的起始
};/*页的属性*/#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;
void mem_init(void);
void* get_kernel_pages(uint32_t pg_cnt);
#endifkernel/memory.c #include memory.h
#include stdint.h
#include print.h
#include bitmap.h
#include debug.h
#include string.h#define PG_SIZE 4096 //页尺寸4096
#define PDE_IDX(addr) ((addr0xffc00000)22) //页目录项索引
#define PTE_IDX(addr) ((addr0x003ff000)12) //页表项索引
/**
该项为位图地址主程序的栈顶为0xc009f000主程序的PCB为0xc009e000(实际上0xc009f000也是合理的)一个页框大小的位图可以表示128MB内存(4KB * 每位为8bit * 每位表达4KB)
本系统支持4个页框大小的位图所以就是512MB地址就为0xc009e00-4*0xc0001000
**/
#define MEM_BITMAP_BASE 0xc009a000#define K_HEAP_START 0xc0100000 //内核的低1MB的虚拟地址是0xc0000000~~0xc00fffff所以为了是地址紧凑我们将堆地址放在0xc01000000struct 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;/*
在pf表示的虚拟内存池中申请pg_cnt个虚拟页
成功则返回虚拟页的起始地址失败则返回NULL
*/
static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) {int vaddr_start 0, bit_idx_start -1;uint32_t cnt 0;//1根据你的pflags决定获取哪个池子if (pf PF_KERNEL) {bit_idx_start bitmap_scan(kernel_vaddr.vaddr_bitmap, pg_cnt);if (bit_idx_start -1) {return NULL;}//根据页数去填充bitmapwhile (cnt pg_cnt) {bitmap_set(kernel_vaddr.vaddr_bitmap, bit_idx_start cnt, 1);}//返回虚拟分配后的虚拟地址 注意bitmap中一个bit位代表一页 4KBvaddr_start kernel_vaddr.vaddr_start bit_idx_start * PG_SIZE;}else {//用户池之后补充}return (void*)vaddr_start;
}uint32_t* pte_ptr(uint32_t vaddr) {/*先访问到页表自己,页目录项pde作为pte的索引访问到页表再用pte的索引作页内偏移不理解的可以看7 Day虚拟地址访问页表*/uint32_t* pte (uint32_t*)(0xffc00000 ((vaddr 0xffc00000) 10) PTE_IDX(vaddr) 2);return pte;
}uint32_t* pde_ptr(uint32_t vaddr) {uint32_t* pde (uint32_t*)(0xfffff000 PDE_IDX(vaddr) * 4);return pde;
}/*m_pool指向物理内存池中分配1个物理页成功返回页框的物理地址
*/
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;
}/*在页表中添加虚拟地址_vaddr与物理地址_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* pde pde_ptr(vaddr); //pde_ptr(vaddr)是得到的页目录项的地址*pde就是页目录项里面的地址也就是页表的地址uint32_t* pte pte_ptr(vaddr); //pte_ptr(vaddr)得到的是页表的地址*pte就是页表项的内容也就是对应的物理地址/*判断页目录项p位如果为1则表示表已存在*/if (*pde 0x00000001) {//判断页目录项是否存在ASSERT(!(*pte 0x00000001));if (!(*pte 0x00000001)) {*pte page_phyaddr | PG_US_U | PG_RW_W | PG_P_1;}}else {//实际上是分配页的物理内存地址uint32_t pde_phy_addr (uint32_t)palloc(kernel_pool);*pde pde_phy_addr | PG_US_U | PG_RW_W | PG_P_1;memset((void*)((int)pte 0xfffff000),0,PG_SIZE);//因为新创了一个页目录项也就是一个页表一个页表大小为4KB0xfffff定位到本目录页的某个目录项(通过分页查询找到页表地址)清空该页目录项的4KB物理内存地址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);//1获取虚拟内存的地址void* vaddr_start vaddr_get(pf, pg_cnt);if (vaddr_start NULL) return NULL;uint32_t vaddr (uint32_t)vaddr_start, cnt pg_cnt;//2根据flag确定内存池struct pool* mem_pool pf PF_KERNEL ? kernel_pool : user_pool;//3根据需要分配的页数来不断从内存池中分配内存while (cnt-- 0){ //4获取池子中的物理地址void* page_phyaddr palloc(mem_pool);if (page_phyaddr NULL) return NULL;//5形成虚拟地址与物理地址的映射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) memset(vaddr, 0, pg_cnt*PG_SIZE);return vaddr;
}static void mem_pool_init(uint32_t all_mem) {uint32_t page_table_size PG_SIZE * 256; //页表占用的大小 769~1023页 第0页 第768页uint32_t used_mem page_table_size 0x100000; //低端1MB的内存 页表所占用的大小uint32_t free_mem all_mem - used_mem;uint32_t all_free_pages free_mem / PG_SIZE; //空余的页数 总空余内存 / 一页的大小uint32_t kernel_free_pages all_free_pages / 2; //内核 与 用户 各平分剩余内存uint32_t user_free_pages all_free_pages - kernel_free_pages; //万一是奇数 就会少1 减去即可//kbm kernel_bitmap ubm user_bitmapuint32_t kbm_length kernel_free_pages / 8; //一位即可表示一页 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.bits (void*)MEM_BITMAP_BASE;user_pool.pool_bitmap.bits (void*)(MEM_BITMAP_BASE kbm_length);kernel_pool.pool_bitmap.btmp_bytes_len kbm_length;user_pool.pool_bitmap.btmp_bytes_len ubm_length;put_str(\n-------kernel_pool init start!-------\n);put_str(\nkernel_pool bitmap start addr:);put_int((int)kernel_pool.pool_bitmap.bits);put_str(\n);put_str(\nkernel_pool phy addr start:);put_int(kernel_pool.phy_addr_start);put_str(\n);bitmap_init(kernel_pool.pool_bitmap);put_str(\n------- kernel_pool init end! -------\n);put_str(\n-------user_pool init start!-------\n);put_str(\nuser_pool bitmap start addr:);put_int((int)user_pool.pool_bitmap.bits);put_str(\n);put_str(\nuser_pool phy addr start:);put_int(user_pool.phy_addr_start);put_str(\n);bitmap_init(user_pool.pool_bitmap);put_str(\n------- user_pool init end! -------\n);put_str(\n-------vitrual_kernel_pool init start!-------\n);kernel_vaddr.vaddr_bitmap.bits (void*)MEM_BITMAP_BASE kbm_length ubm_length;kernel_vaddr.vaddr_bitmap.btmp_bytes_len kbm_length;kernel_vaddr.vaddr_start K_HEAP_START;put_str(vitrual_kernel_pool vaddr_start:);put_int(kernel_vaddr.vaddr_start);put_str(\n);put_str(\n------- vitrual_kernel_pool init end! -------\n);}void mem_init() {put_str(\n******************mem_init start******************\n);uint32_t mem_bytes_total *((uint32_t*)(0xb00)); //之前内存总量数据存放的地方mem_pool_init(mem_bytes_total);put_str(\n******************mem_init done******************\n);}这么大一串代码我相信大家肯定没看到懂讲实话在一开始的时候我也理解困难因为之前写的一些核心组件以及咱们整个系统内存分布已经忘记了所以在此我来讲解一下一些关键点。 当前系统各个模块的内存 物理地址和虚拟地址 注0xc00代表着第768个页目录项对应的是高1G的内核态 Loader0x9000xc0000900MBR0x7c000xc0007c00内核文件缓冲区0x700000xc0070000内核0x15000xc0001500当前esp栈顶0x9f0000xc009f000显卡地址0xb8000~0xbffff页目录地址0x100000第一个页表地址0x101000 首先是0xc009a000这行地址是怎么来的呢? 我们要清楚在之前的loader里面我们将esp栈顶赋值为0x9f000对应的虚拟地址也就是0xc009f000在之后我们要编写线程等等需要存放PCB首当其冲的就是main函数的PCB每个PCB大概要占用4KB且地址必须为0xXXXXX000~0xXXXXXfff 同时我们的位图可以管理512MB的内存大家可以算一算一个页框可以管理128MB的内存, 4KB*数组元素是8bit*一位代表4KB)那么512MB就是4KB 由此可得 0xc009f000 - 0xc0001000*5 0xc009a000 关于分页中页目录访问修改和页表访问修改我已经做介绍过再次我就贴出截图给大家看看 五开始编译
上面的代码都完成后就可以开始编译了注意哈要在init.c文件中添加上mem_init()函数进行初始化哦
紧接着在main.c中添加下以下测试代码即可 在这里我们不再使用脚本编译了而是使用MakeFile关于什么是MakeFile我这里不多赘述因为在这里不是核心点我这里直接给出步骤大家照做即可。
1首先在自己的项目文件下新建一个MakeFile文件(不要在意为什么是windows因为我懒得开linux了) 2其次编写MakeFile
BUILD_DIR ./build
ENTRY_POINT 0xc0001500
AS nasm
CC gcc
LD ld
LIB -m32 -I ./lib -m32 -I ./lib/kernel -m32 -I ./lib/usr -m32 -I ./kernel -m32 -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
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)/bitmap.o \$(BUILD_DIR)/memory.o########## C Code Compile Part #############
$(BUILD_DIR)/main.o: kernel/main.c lib/kernel/print.h \lib/stdint.h kernel/init.h kernel/debug.h$(CC) $(CFLAGS) -o $ $ $(BUILD_DIR)/init.o: kernel/init.c kernel/init.h lib/kernel/print.h \lib/stdint.h kernel/interrupt.h device/timer.h thread/thread.h$(CC) $(CFLAGS) -o $ $$(BUILD_DIR)/interrupt.o: kernel/interrupt.c kernel/interrupt.h \lib/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/stdint.h\lib/kernel/io.h lib/kernel/print.h kernel/debug.h$(CC) $(CFLAGS) -o $ $ $(BUILD_DIR)/debug.o: kernel/debug.c kernel/debug.h \lib/kernel/print.h lib/stdint.h kernel/interrupt.h lib/string.h$(CC) $(CFLAGS) -o $ $$(BUILD_DIR)/string.o: lib/string.c lib/string.h\kernel/global.h kernel/debug.h$(CC) $(CFLAGS) -o $ $$(BUILD_DIR)/bitmap.o: lib/kernel/bitmap.c lib/kernel/bitmap.h\kernel/global.h kernel/debug.h lib/stdint.h lib/string.c$(CC) $(CFLAGS) -o $ $$(BUILD_DIR)/memory.o: kernel/memory.c kernel/memory.h\kernel/debug.h lib/stdint.h lib/kernel/bitmap.h lib/kernel/print.h$(CC) $(CFLAGS) -o $ $######### ASM Code Compile Part #############
$(BUILD_DIR)/kernel.o: kernel/kernel.S$(AS) $(ASFLAGS) -o $ $ $(BUILD_DIR)/print.o: lib/kernel/print.S$(AS) $(ASFLAGS) -o $ $######### LD All Files ##############
$(BUILD_DIR)/kernel.bin: $(OBJS)$(LD) $(LDFLAGS) $^ -o $ .PHONY :mk_dir os clean all mk_dir:if [[! -d $(BUILD_DIR) ]];then mkdir $(BUILD_DIR);fi ######### 注意注意我这里的seek是6你们之前的seek是多少要填写你们自己的否则会出错 #######
os:dd if$(BUILD_DIR)/kernel.bin \of../geniusos.img bs512 count200 seek6 convnotruncclean:cd $(BUILD_DIR) rm -f ./*build: $(BUILD_DIR)/kernel.binall: mk_dir build os
3在项目目录下建立一个build文件夹从今往后这里就集中存访编译好的内核代码
4 使用make all是编译所有文件make clean是清理build文件中所有编译好的文件注意使用make all的时候要在当前makefile所在的目录下
紧接着你的bochs里面应该可以看到初始化成功的输出了如果没有的话那就再去看看代码吧然后我们在bochs中用info tab来验证我们main.c的分配三页内存页是否成功了 可以看到0xc0100000~0xc0102fff这里是3KB的大小正好对应三页说明我们成功啦然后再看看我们bitmap我们内核bitmap的地址在0xc009a000我们用 x/10 0xc009a000来查看可以发现第一个数组元素为7也就是0111说明我们bitmap也运作成功了 今天可算完成了一个大工程(好像也没多大)芜湖 快去休息一下吧我也要去休息一下打瓦洛兰特了明天我们来一起编写操作系统的线程好好加油吧~。