《操作系统真象还原》第十二章(3)——进一步完善内核
文章目录
前言
上一部分博客链接:《操作系统真象还原》第十二章(2)——进一步完善内核-CSDN博客
这部分完成堆内存管理。会大改之前的内存管理系统,实现更精细粒度下的内存管理,下面分为六个小节,代码量很大,做好准备。
malloc 底层原理
之前我们实现了4KB标准页粒度的内存管理,为了更精细管理内存,自然引入了更小单位的内存单元arena。arena是把一大块内存平均分成无数小内存块后的仓库。arena本身还是1或多个内存页,但是它内部还有细分。不同的arena内部小内存块的大小不同,我们想要申请多大的内存,就会有对应的arena响应我们。
arena 是个提供内存分配的数据结构,它分为两部分,一部分是元信息,用来描述自己内存池中空闲 内存块数量,这其中包括内存块描述符指针(后面介绍),通过它可以间接获知本 arena 所包含内存块的 规格大小,此部分占用的空间是固定的,约为12字节。另一部分就是内存池区域,这里面有无数的内存 块,此部分占用arena大量的空间。我们把每个内存块命名为mem_block,它们是内存分配粒度更细的资 源,最终为用户分配的就是这其中的一个内存块。在咱们的实现中,针对小内存块的arena占用1页框内 存,除了元信息外的剩下的内存被平均分成多个小内存块。整个arena就像个仓库一样,元信息部分相当 于库房管理员,内存块相当于库中物品。
arena示意图:
一个arena容纳的内存块是有限的,总会有内存块供不应求的时候。当某一规格arena中的内存 块全部分配出去时,必须再增加新的同一规格arena,由多个同一规格 的arena组合为一个“大的仓库”,为同一规格的内存块提供货源。面馆开始卖面时,起初也只是用一口锅供应大碗,根据实际销售情况才增加供货规模。arena也是一样的,起始为某一类型内存块供货的arena 只有1个,当此arena中的全部内存块都被分配完时,系统再创建一个同规格的arena继续提供该规格 的内存块,当此arena又被分配完时,再继续创建出同规格的arena,arena规模逐渐增大,逐步形成arena 集群。既然同一类内存块可以由多个arena 提供,为了跟踪每个 arena 中的空闲内存块,分别为每一种 规格的内存块建立一个内存块描述符,即 mem_block_desc,在其中记录内 存块规格大小,以及位于所有同类arena中的空闲内存块链表。
mem_block_desc示意图:
还有一种情况,当申请的内存量大于1024字节时,会有一种特殊的arena结构,如图所示:
小内存块总共有7种规模:16、32、64、128、256、512、1024字节
总逻辑示意图:
这部分有个大概的概念即可,后续一边写代码一边加深理解。
底层初始化
从这一小节开始写代码,这部分更改不少,我先在每部分放出更改的内容,最后在放一个代码总览。
更新memory.h
...
/*内存块*/
struct mem_block
{
struct list_elem free_elem;
};
/*内存块描述符*/
struct mem_block_desc
{
uint32_t block_size; // 小内存块大小
uint32_t block_per_arena; // 每个arena拥有的小内存块数量
struct list free_list; // 目前空闲的小内存块链表
};
#define DESC_CNT 7 // 总共有7种mem_block_desc
...
更新memory.c
...
struct arena
{
struct mem_block_desc *desc; // 此arena关联的mem_block_desc
// 如果large为true,cnt代表arena拥有的页数
// 否则代表空闲的mem_block数
bool large;
uint32_t cnt;
};
struct mem_block_desc k_block_descs[DESC_CNT]; // 内核内存块描述符数组
...
/*初始化mem_block_desc,为malloc作准备*/
void block_init(struct mem_block_desc *desc_array)
{
uint16_t desc_index; // 内存块描述符数组索引
uint16_t block_size = 16; // 目前每个小块的大小
for (desc_index = 0; desc_index < DESC_CNT; desc_index++)
{
desc_array[desc_index].block_size = block_size;
desc_array[desc_index].block_per_arena = (PAGE_SIZE - sizeof(struct arena)) / block_size;
list_init(&desc_array[desc_index].free_list);
block_size *= 2;
}
}
/* 内存管理初始化入口 */
void mem_init(void)
{
put_str("mem_init start\n");
// 之前loader在开启分页时就获取了全部内存的大小,放到了0xb00中
uint32_t mem_bytes_total = (*(uint32_t *)(0xb00));
mem_pool_init(mem_bytes_total); // 初始化内存池
block_init(k_block_descs); // 初始化mem_block_desc数组
put_str("mem_init done\n");
}
新增的函数记得在头文件里声明,后面会用到
实现sys_malloc
到了这部分第一个重点内容了,前面我们已经初始化了mem_block_desc。这部分我们实现sys_malloc,它是malloc对应的子功能处理函数,分配维护内存块资源,动态创建arena。
修改thread.h
改动了pcb,看代码:
...
/* 线程或进程的pcb程序控制块 */
struct task_struct
{
uint32_t *self_kstack; // 线程自己的栈的栈顶指针
pid_t pid; // 线程的pid,系统调用部分对它进行操作
enum thread_status status; // 线程的状态
uint8_t priority; // 线程的优先级
uint8_t ticks; // 线程的时间片,在处理器上运行的时间滴答数
uint32_t elapsed_ticks; // 线程的运行时间,也就是这个线程已经执行了多久
char name[16]; // 线程的名字
struct list_elem general_tag; // 用于线程在一般队列中的节点
struct list_elem all_list_tag; // 用于线程在thread_all_list队列中的节点
uint32_t *pgdir; // 如果是进程,这是进程的页表结构中页目录表的虚拟地址,线程则置为NULL
struct virtual_addr userprog_vaddr; // 用户进程的虚拟地址,后续转化为物理地址后存入cr3寄存器
struct mem_block_desc u_block_desc[DESC_CNT]; //进程内存块描述符数组,用于用户进程的堆内存管理
uint32_t stack_magic; // 线程栈的魔数,边界标记,用来检测栈溢出
};
...
修改process.c
给上面那个新增变量做初始化
...
/*通过线程创建用户进程*/
void process_execute(void *filename, char *name)
{
struct task_struct *thread = get_kernel_pages(1);
init_thread(thread, name, default_prio); // 初始化线程
create_user_vaddr_bitmap(thread); // 位图
thread_create(thread, start_process, filename); // 线程结构体-具体功能(创建进程)-线程名
thread->pgdir = create_page_dir(); // 页目录表
block_init(thread->u_block_desc); // 进程内存块描述符数组初始化
enum intr_status old_status = intr_disable();
ASSERT(!elem_find(&thread_ready_list, &thread->general_tag));
list_append(&thread_ready_list, &thread->general_tag);
ASSERT(!elem_find(&thread_all_list, &thread->all_list_tag));
list_append(&thread_all_list, &thread->all_list_tag);
intr_set_status(old_status);
}
...
再次修改memory.c
再次新增100行左右代码。
/*返回arena中第idx内存块的地址*/
/*参考arena示意图,还是很好理解的,某块地址=起始地址+元数据+若干个块*/
static struct mem_block *arena2block(struct arena *ar, uint32_t idx)
{
return (struct mem_block *)((uint32_t)ar + sizeof(struct arena) + idx * ar->desc->block_size);
}
/*返回b内存块对应的arena起始地址*/
/*由于arnea占据一个完整的页,被4kb整除,所以块的前20位地址就是arnea的地址*/
static struct arena *block2arena(struct mem_block *b)
{
return (struct arena *)((uint32_t)b & 0xfffff000);
}
/*在堆中申请size个字节的内存*/
void *sys_malloc(uint32_t size)
{
enum pool_flags PF;
struct pool *mem_pool;
uint32_t pool_size;
struct mem_block_desc *descs;
struct task_struct *cur = running_thread();
// 判断使用哪个内存池
if (cur->pgdir == NULL)
{
PF = PF_KERNEL;
mem_pool = &kernel_pool;
pool_size = kernel_pool.pool_size;
descs = k_block_descs; // 全局变量
}
else
{
PF = PF_USER;
mem_pool = &user_pool;
pool_size = user_pool.pool_size;
descs = cur->u_block_desc;
}
// 如果申请的内存不在内存池容量范围内
if (!(size > 0 && size < pool_size))
{
return NULL;
}
struct arena *a;
struct mem_block *b;
lock_acquire(&mem_pool->lock); // 保证互斥
if (size > 1024) // 需要整页分配
{
// 计算需要的页数,向上取整
uint32_t page_cnt = DIV_ROUND_UP(size + sizeof(struct arena), PG_SIZE);
a = malloc_page(PF, page_cnt);
if (a != NULL) // 成功申请
{
memset(a, 0, page_cnt * PG_SIZE); // 清零以备使用
a->desc = NULL;
a->cnt = page_cnt;
a->large = true;
return (void *)(a + 1); // 返回剩余内存
}
else
{
lock_release(&mem_pool->lock);
return NULL;
}
}
else // 分配小块即可
{
uint8_t desc_idx;
for (desc_idx = 0; desc_idx < DESC_CNT; desc_idx++)
{
if (size <= descs[desc_idx].block_size) // 寻找大小合适的块
{
break;
}
}
if (list_empty(&descs[desc_idx].free_list)) // 如果对应的大小已经没有空余的块,就要创建新的arena
{
a = malloc_page(PF, 1);
if (a == NULL)
{
lock_release(&mem_pool->lock);
return NULL;
}
memset(a, 0, PG_SIZE);
// 更新desc信息
a->desc = &descs[desc_idx];
a->cnt = descs[desc_idx].block_per_arena;
a->large = false;
// 将新的arena拆成小块放入队列
uint32_t block_idx;
enum intr_status old_status = intr_disable();
for (block_idx = 0; block_idx < descs[desc_idx].block_per_arena;block_idx++){
b = arena2block(a, block_idx);
ASSERT(!elem_find(&a->desc->free_list, &b->free_elem));
list_append(&a->desc->free_list, &b->free_elem);
}
intr_set_status(old_status);
}
//开始分配内存块
b = (struct mem_block *)(list_pop(&descs[desc_idx].free_list));
memset(b, descs[desc_idx].block_size, 0); // 清理一个小块
a = block2arena(b);
a->cnt--;
lock_release(&mem_pool->lock);
return (void *)b;
}
}
第一次功能测试
main.c
// 内核的入口函数
#include "../lib/kernel/print.h"
#include "./init.h"
#include "../thread/thread.h"
#include "../device/console.h"
#include "./interrupt.h"
#include "../userprog/process.h"
// 本章测试头文件
#include "../lib/user/syscall.h"
#include "../userprog/syscall-init.h"
#include "../lib/stdio.h"
#include "./memory.h"
void k_thread_a(void *);
void k_thread_b(void *);
void u_prog_a(void);
void u_prog_b(void);
int main(void)
{
put_str("HongBai's OS kernel\n");
init_all(); // 初始化所有模块
// process_execute(u_prog_a, "user_prog_a");
// process_execute(u_prog_b, "user_prog_b");
intr_enable();
thread_start("k_thread_a", 31, k_thread_a, "argA: ");
thread_start("k_thread_b", 31, k_thread_b, "argB: ");
while (1)
{
};
}
void k_thread_a(void *arg)
{
char *para = arg;
void *addr = sys_malloc(33);
printf(" I am thread_a,sys_malloc(33),addr is%x\n", (int)addr);
while (1)
{
};
}
void k_thread_b(void *arg)
{
char *para = arg;
void *addr = sys_malloc(63);
printf(" I am thread_b,sys_malloc(63),addr is%x\n", (int)addr);
while (1)
{
};
}
void u_prog_a(void)
{
printf("%s%d%c", " program_a_pid:", getpid(), '\n');
while (1)
{
};
}
void u_prog_b(void)
{
printf("%s%d%c", " program_b_pid:", getpid(), '\n');
while (1)
{
};
}
结果
ok了下一部分。
内存的释放
释放内存是与分配内存相反的过程,咱们对照着设计一套释放内存的方法。
- 在物理地址池中释放物理页地址,相关的函数是pfree,操作的位图同palloc。
- 在页表中去掉虚拟地址的映射,原理是将虚拟地址对应pte的P位置0,相关的函数是page_table_ pte_remove。
- 在虚拟地址池中释放虚拟地址,相关的函数是vaddr_remove,操作的位图同vaddr_get。
我们将以上三个功能封装到函数mfree_page中。
继续修改memory.c
直接上代码吧,上面是说明,一些我觉得的要点也写了注释
/*将物理地址pg_phy_addr回收到物理内存池,实现单页回收*/
void pfree(uint32_t pg_phy_addr)
{
struct pool *mem_pool;
uint32_t bit_idx = 0;
if (pg_phy_addr >= user_pool.phy_addr_start)
{
mem_pool = &user_pool;
bit_idx = (pg_phy_addr - user_pool.phy_addr_start) / PG_SIZE;
}
else
{
mem_pool = &kernel_pool;
bit_idx = (pg_phy_addr - kernel_pool.phy_addr_start) / PG_SIZE;
}
bitmap_set(&mem_pool->pool_bitmap, bit_idx, 0);
}
/*去除页表中vaddr虚拟地址的映射,即vaddr对应的pte页表项设为0*/
static void page_table_pte_remove(uint32_t vaddr)
{
uint32_t *pte = pte_ptr(vaddr);
*pte &= ~PG_P_1; // 只清除P位,不影响其他位
asm volatile("invlpg %0" : : "m"(vaddr) : "memory");
}
/*在虚拟地址池中释放vaddr起始的连续pg_cnt个内存页*/
static void vaddr_remove(enum pool_flag pf, void *_vaddr, uint32_t pg_cnt)
{
uint32_t bit_idx_start = 0, vaddr = uint32_t(_vaddr), cnt = 0;
if (pf == PF_KERNEL)
{
bit_idx_start = (vaddr - kernel_vaddr.vaddr_start) / PG_SIZE;
while (cnt < pg_cnt)
{
bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt, 0);
cnt++;
}
}
else
{
struct task_struct *cur = running_thread();
bit_idx_start = (vaddr - cur->userprog_vaddr.vaddr_start) / PG_SIZE;
while (cnt < pg_cnt)
{
bitmap_set(&cur->userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt, 0);
cnt++;
}
}
}
/*释放以虚拟地址vaddr为起始的cnt个物理页框*/
void mfee_page(enum pool_flag pf, void *_vaddr, uint32_t pg_cnt)
{
uint32_t pg_phy_addr;
uint32_t vaddr = (uint32_t)_vaddr, cnt = 0;
ASSERT(pg_cnt >= 1 && vaddr % PG_SIZE);
pg_phy_addr = addr_v2p(vaddr);
// 确保待释放的物理内存在1MB低端内存+1KB页目录表+1KB页表地址外
// 同时还必须是整4KB地址
ASSERT(pg_phy_addr >= 0x102000 && (pg_phy_addr % PG_SIZE));
if (vaddr >= user_pool.phy_addr_start)
{
vaddr -= PG_SIZE; // 为了对齐
while (cnt < pg_cnt)
{
vaddr += PG_SIZE; // 每次都能正确指向下一个页
pg_phy_addr = addr_v2p(vaddr);
// 确保待回收地址在用户物理地址池内,并且是整4KB地址
ASSERT(pg_phy_addr >= user_pool.phy_addr_start && (pg_phy_addr % PG_SIZE));
pfree(pg_phy_addr);
page_table_pte_remove(vaddr);
cnt++;
}
vaddr_remove(pf, _vaddr, pg_cnt);
}
else
{
vaddr -= PG_SIZE;
while (cnt < pg_cnt)
{
vaddr += PG_SIZE;
pg_phy_addr = addr_v2p(vaddr);
// 确保待回收地址只在内核物理地址池内,并且是整4KB地址
ASSERT(pg_phy_addr >= kernel_pool.phy_addr_start &&
pg_phy_addr < user_pool.phy_addr_start &&
(pg_phy_addr % PG_SIZE));
pfree(pg_phy_addr);
page_table_pte_remove(vaddr);
cnt++;
}
vaddr_remove(pf, _vaddr, pg_cnt);
}
}
实现sys_free
基于上一部分的mfree_page和arena,实现任意字节级别的回收。
sys_free 是内存释放的统一接口,无论是页框级别的内存和小的内存块,都统一用sys_free处理。因 此,sys_free 针对这两种内存的处理有各自的方法,对于大内存的处理称之为释放,就是把页框在虚拟内 存池和物理内存池的位图中将相应位置0。对于小内存的处理称之为“回收”,是将arena中的内存块重新 放回到内存块描述符中的空闲块链表free_list。
/*回收ptr处的内存*/
void sys_free(void *ptr)
{
ASSERT(ptr != NULL);
if (ptr != NULL)
{
enum pool_flags pf;
struct pool *mem_pool;
/*判断是线程还是进程*/
if (running_thread()->pgdir == NULL)
{
ASSERT((uint32_t)ptr >= K_HEAP_START);
pf = PF_KERNEL;
mem_pool = &kernel_pool;
}
else
{
pf = PF_USER;
mem_pool = &user_pool;
}
lock_acquire(&mem_pool->lock);
struct mem_block *b = ptr;
struct arena *a = block2arena(b);
ASSERT(a->large == 1 || a->large == 0);
// 判断是整页还是小块
if (a->desc == 0 || a->large == true)
{
mfree_page(pf, a, a->cnt);
}
else
{
// 先把目前这个小块放回空闲队列
list_append(&a->desc->free_list, &b->free_elem);
// 然后判断这个arena是否空闲,是的话释放整个arena
if (++a->cnt == a->desc->block_per_arena)
{
uint32_t block_idx;
for (block_idx = 0; block_idx < a->desc->block_per_arena; ++block_idx)
{
struct mem_block *b = arena2block(a, block_idx);
ASSERT(elem_find(&a->desc->free_list, &b->free_elem));
list_remove(&b->free_elem);
}
mfree_page(pf, a, 1);
}
}
lock_release(&mem_pool->lock);
}
}
第二次测试
修改main.c
这次的测试代码比较抽象,我复制粘贴的书上的代码。
// 内核的入口函数
#include "../lib/kernel/print.h"
#include "./init.h"
#include "../thread/thread.h"
#include "../device/console.h"
#include "./interrupt.h"
#include "../userprog/process.h"
// 本章测试头文件
#include "../lib/user/syscall.h"
#include "../userprog/syscall-init.h"
#include "../lib/stdio.h"
#include "./memory.h"
void k_thread_a(void *);
void k_thread_b(void *);
void u_prog_a(void);
void u_prog_b(void);
int main(void)
{
put_str("HongBai's OS kernel\n");
init_all(); // 初始化所有模块
// process_execute(u_prog_a, "user_prog_a");
// process_execute(u_prog_b, "user_prog_b");
intr_enable();
thread_start("k_thread_a", 31, k_thread_a, "argA: ");
thread_start("k_thread_b", 31, k_thread_b, "argB: ");
while (1)
{
};
}
void k_thread_a(void *arg)
{
char *para = arg;
void *addr1, *addr2, *addr3, *addr4, *addr5, *addr6, *addr7;
int max = 1000;
printf(" thread_a test start\n");
while (max-- > 0)
{
int size = 128;
addr1 = sys_malloc(size);
size *= 2;
addr2 = sys_malloc(size);
size *= 2;
addr3 = sys_malloc(size);
sys_free(addr1);
addr4 = sys_malloc(size);
size *= 2;
size *= 2;
size *= 2;
size *= 2;
size *= 2;
size *= 2;
size *= 2;
addr5 = sys_malloc(size);
addr6 = sys_malloc(size);
sys_free(addr5);
size *= 2;
addr7 = sys_malloc(size);
sys_free(addr6);
sys_free(addr7);
sys_free(addr2);
sys_free(addr3);
sys_free(addr4);
}
printf(" thread_a test end\n");
while (1)
{
};
}
void k_thread_b(void *arg)
{
char *para = arg;
void *addr1, *addr2, *addr3, *addr4, *addr5, *addr6, *addr7, *addr8, *addr9;
int max = 1000;
printf(" thread_b test start\n");
while (max-- > 0)
{
int size = 9;
addr1 = sys_malloc(size);
size *= 2;
addr2 = sys_malloc(size);
size *= 2;
sys_free(addr2);
addr3 = sys_malloc(size);
sys_free(addr1);
addr4 = sys_malloc(size);
addr5 = sys_malloc(size);
addr6 = sys_malloc(size);
sys_free(addr5);
size *= 2;
addr7 = sys_malloc(size);
sys_free(addr6);
sys_free(addr7);
sys_free(addr3);
sys_free(addr4);
size *= 2;
size *= 2;
size *= 2;
addr1 = sys_malloc(size);
addr2 = sys_malloc(size);
addr3 = sys_malloc(size);
addr4 = sys_malloc(size);
addr5 = sys_malloc(size);
addr6 = sys_malloc(size);
addr7 = sys_malloc(size);
addr8 = sys_malloc(size);
addr9 = sys_malloc(size);
sys_free(addr1);
sys_free(addr2);
sys_free(addr3);
sys_free(addr4);
sys_free(addr5);
sys_free(addr6);
sys_free(addr7);
sys_free(addr8);
sys_free(addr9);
}
printf(" thread_b test end\n");
while (1)
{
};
}
void u_prog_a(void)
{
printf("%s%d%c", " program_a_pid:", getpid(), '\n');
while (1)
{
};
}
void u_prog_b(void)
{
printf("%s%d%c", " program_b_pid:", getpid(), '\n');
while (1)
{
};
}
这里说明一下,代码是手打的,编译时难免会有warnning和error,大部分放出来的代码都已经是修改好后无报错版本了,但是难免有疏漏,如果无法正常编译运行请大家自行修补。
makefile没有修改,直接运行开始测试。随便贴一下截图吧
那这部分就这样,任意小字节可能还有问题,后续修改。
实现系统调用malloc和free
还是套模板:1.宏枚举体++ 2.调用宏 3.在子程序数组里注册
syscall.h
//用户进程调用本文件
#ifndef __LIB_USER_SYSCALL_H
#define __LIB_USER_SYSCALL_H
#include "../kernel/stdint.h"
enum SYSCALL_NR
{
SYS_GETPID,
SYS_WRITE,
SYS_MALLOC,
SYS_FREE
};
uint32_t getpid(void); // 获取任务pid
uint32_t write(char *str); // 打印字符串并返回字符串长度
void *malloc(uint32_t size);
void free(void *ptr);
#endif
syscall.c
#include "./syscall.h"
/*从上到下,分别是0、1、2、3参数的系统调用,结构基本一致
*eax是子程序号,剩下三个存在ebx、ecx、edx中*/
/*({ ... })是gcc扩展
*将一组语句封装为一个表达式,返回最后一个语句的值*/
#define _syscall0(NUMBER) ({ \
int retval; \
asm volatile( \
"int $0x80" \
: "=a"(retval) \
: "a"(NUMBER) \
: "memory"); \
retval; \
})
#define _syscall1(NUMBER, ARG1) ({ \
int retval; \
asm volatile( \
"int $0x80" \
: "=a"(retval) \
: "a"(NUMBER), "b"(ARG1) \
: "memory"); \
retval; \
})
#define _syscall2(NUMBER, ARG1, ARG2) ({ \
int retval; \
asm volatile( \
"int $0x80" \
: "=a"(retval) \
: "a"(NUMBER), "b"(ARG1), "c"(ARG2) \
: "memory"); \
retval; \
})
#define _syscall3(NUMBER, ARG1, ARG2, ARG3) ({ \
int retval; \
asm volatile( \
"int $0x80" \
: "=a"(retval) \
: "a"(NUMBER), "b"(ARG1), "c"(ARG2), "d"(ARG3) \
: "memory"); \
retval; \
})
/*返回当前任务的pid*/
uint32_t getpid()
{
return _syscall0(SYS_GETPID);
}
/*打印字符串str,返回strlen*/
uint32_t write(char *str)
{
return _syscall1(SYS_WRITE, str);
}
void *malloc(uint32_t size)
{
return (void *)_syscall1(SYS_MALLOC, size);
}
void free(void *ptr)
{
return _syscall1(SYS_FREE, ptr);
}
syscall_init.h
#ifndef __USERPROG_SYSCALL_INIT_H
#define __USERPROG_SYSCALL_INIT_H
#include "../lib/kernel/stdint.h"
uint32_t sys_getpid(void);
void syscall_init(void);
// 以下两个函数声明,实现在memory.c
void *sys_malloc(uint32_t size);
void sys_free(void *ptr);
#endif
syscall-init.c
// 内核调用本文件
#include "./syscall-init.h"
#include "../lib/kernel/stdint.h"
#include "../lib/user/syscall.h"
#include "../thread/thread.h"
#include "../lib/kernel/print.h"
#include "../device/console.h"
#include "../lib/string.h"
#define syscall_nr 32 // 最大支持的子功能个数
typedef void *syscall;
syscall syscall_table[syscall_nr];
/*返回当前任务的pid*/
uint32_t sys_getpid(void)
{
return running_thread()->pid;
}
/*打印字符串str,返回strlen*/
uint32_t sys_wirte(char *str)
{
console_put_str(str);
return strlen(str);
}
/*void *sys_malloc(uint32_t size)
{
return sys_malloc(size);
}
void sys_free(void *ptr)
{
sys_free(ptr);
}*/
/*初始化系统调用*/
void syscall_init(void)
{
put_str("syscall_init start\n");
syscall_table[SYS_GETPID] = sys_getpid;
syscall_table[SYS_WRITE] = sys_wirte;
syscall_table[SYS_MALLOC] = sys_malloc;
syscall_table[SYS_FREE] = sys_free;
put_str("syscall_init done\n");
}
main.c
// 内核的入口函数
#include "../lib/kernel/print.h"
#include "./init.h"
#include "../thread/thread.h"
#include "../device/console.h"
#include "./interrupt.h"
#include "../userprog/process.h"
// 本章测试头文件
#include "../lib/user/syscall.h"
#include "../userprog/syscall-init.h"
#include "../lib/stdio.h"
#include "./memory.h"
void k_thread_a(void *);
void k_thread_b(void *);
void u_prog_a(void);
void u_prog_b(void);
int main(void)
{
put_str("HongBai's OS kernel\n");
init_all(); // 初始化所有模块
intr_enable();
process_execute(u_prog_a, "user_prog_a");
process_execute(u_prog_b, "user_prog_b");
thread_start("k_thread_a", 31, k_thread_a, "argA: ");
thread_start("k_thread_b", 31, k_thread_b, "argB: ");
while (1)
{
};
}
void k_thread_a(void *arg)
{
void *addr1 = sys_malloc(256);
void *addr2 = sys_malloc(255);
void *addr3 = sys_malloc(254);
console_put_str(" thread_a malloc addr:0x");
console_put_int((int)addr1);
console_put_char(',');
console_put_int((int)addr2);
console_put_char(',');
console_put_int((int)addr3);
console_put_char('\n');
int cpu_delay = 100000;
while (cpu_delay-- > 0)
;
sys _free(addr1);
sys _free(addr2);
sys _free(addr3);
while (1)
{
};
}
void k_thread_b(void *arg)
{
void *addr1 = sys_malloc(256);
void *addr2 = sys_malloc(255);
void *addr3 = sys_malloc(254);
console_put_str(" thread_b malloc addr:0x");
console_put_int((int)addr1);
console_put_char(',');
console_put_int((int)addr2);
console_put_char(',');
console_put_int((int)addr3);
console_put_char('\n');
int cpu_delay = 100000;
while (cpu_delay-- > 0)
;
sys _free(addr1);
sys _free(addr2);
sys _free(addr3);
while (1)
{
};
while (1)
{
};
}
void u_prog_a(void)
{
void *addr1 = malloc(256);
void *addr2 = malloc(255);
void *addr3 = malloc(254);
printf(" prog_a malloc addr:0x%x,0x%x,0x%x\n", (int)addr1, (int)addr2, (int)addr3);
int cpu_delay = 100000;
while (cpu_delay-- > 0)
;
free(addr1);
free(addr2);
free(addr3);
while (1)
{
};
}
void u_prog_b(void)
{
void *addr1 = malloc(256);
void *addr2 = malloc(255);
void *addr3 = malloc(254);
printf(" prog_b malloc addr:0x%x,0x%x,0x%x\n", (int)addr1, (int)addr2, (int)addr3);
int cpu_delay = 100000;
while (cpu_delay-- > 0)
;
free(addr1);
free(addr2);
free(addr3);
while (1)
{
};
}
结果
运行结果正常,malloc-free初步完成
结语
5.3我写好了这篇博客70%的内容,5.45.5需要复习考试,就没收尾,5.5晚上sys_free部分,今天早上整个完成。
sys_free可能还有bug,表现为b_thread done没有打印出来,留待后续调试吧。