《操作系统真象还原》第十二章(二) —— 完善堆内存管理
章节任务介绍
任务简介
上一节,我们完成了用户程序和操作系统之间的接口——系统调用,本节我们在此基础上完善堆内存管理——实现malloc和free
之前我们虽然已经实现了内存管理,但之前实现分配的内存都是以 4KB 大小的页框为单位的,当我们仅需要几十字节、几百字节这样的小内存块时,显然无法满足这样的需求了,为此必须实现一种小内存块的管理,可以满足任意内存大小的分配,这就是我们为实现malloc要做的基础工作。
本节的主要任务有:
实现sys_malloc
实现sys_free
实现malloc
实现free
内存分配
底层初始化
划分和管理内存块
为了实现对小内存块的管理和分配
我们将一页内存按照块大小平均划分
假如我们规定一块的内存大小是512B,则这页内存可以被分为4k/512B=8个块
为了适应不同块大小的分配,我们分别定义7种类型大小的内存块
接下来我们定义一些必要的数据结构,以下是这些数据结构之间的关系
`/kernel/memory.h`
首先是空闲内存块的定义
/*
内存块描述符个数
对应16、32、64、128、256、512、1024这7种小内存块
*/
#define DESC_CNT 7
/*内存块*/
struct mem_block
{
// 内存块使用双链表进行管理
struct list_elem free_elem;
};
为了对内存块进行管理,我们还需要定义内存块的信息管理数据结构
/*内存块描述符*/
struct mem_block_desc
{
// 每个块的大小
uint32_t block_size;
// 块的总数量
uint32_t blocks_per_arena;
// 用于管理组织块的双链表结构的头节点
struct list free_list;
};
同时为了分配内存,我们需要记录空闲内存块的数量和位置
/kernel/memory.c
/*
内存仓库元信息
每个页面有7种类型大小的内存块
一个arena管理一种类型大小的内存块
*/
struct arena
{
/*管理该种类型大小的内存块数组索引*/
struct mem_block_desc *desc;
/*
标记是分配的页框还是内存块
如果是页框,则cnt表示页框的数量
如果是内存块,则cnt表示空闲内存块的数量
(注意,是空闲块的数量,mem_block_desc中表示的是块的总数)
*/
bool large;
uint32_t cnt;
};
从哪申请内存块
内存块的分配和释放本质上都是从内存池中进行申请和归还的,而我们有两种内存池——内核内存池和用户内存池
其中内核内存池供内核程序使用,用户内存池供用户程序使用
因此针对不同类型的内存池需要各自分别添加内存管理结构
内核内存池
/kernel/memory.c
/*不同类型大小的内存块管理数组*/
struct mem_block_desc k_block_descs[DESC_CNT];
用户内存池
/thread/thread.h
/* 进程或线程的pcb,程序控制块, 此结构体用于存储线程的管理信息*/
struct task_struct
{
uint32_t *self_kstack; // 用于存储线程的栈顶位置,栈顶放着线程要用到的运行信息
pid_t pid; // 定义线程或者进程的pid
enum task_status status;
uint8_t priority; // 线程优先级
char name[16]; // 用于存储自己的线程的名字
uint8_t ticks; // 线程允许上处理器运行还剩下的滴答值,因为priority不能改变,所以要在其之外另行定义一个值来倒计时
uint32_t elapsed_ticks; // 此任务自上cpu运行后至今占用了多少cpu嘀嗒数, 也就是此任务执行了多久*/
struct list_elem general_tag; // general_tag的作用是用于线程在一般的队列(如就绪队列或者等待队列)中的结点
struct list_elem all_list_tag; // all_list_tag的作用是用于线程队列thread_all_list(这个队列用于管理所有线程)中的结点
uint32_t *pgdir; // 进程自己页目录表的虚拟地址
struct virtual_addr userprog_vaddr; // 每个用户进程自己的虚拟地址池
struct mem_block_desc u_block_desc[DESC_CNT]; // 用户进程内存块描述符
uint32_t stack_magic; // 如果线程的栈无限生长,总会覆盖地pcb的信息,那么需要定义个边界数来检测是否栈已经到了PCB的边界
};
上述主要为PCB数据结构添加内存块管理成员
struct mem_block_desc u_block_desc[DESC_CNT]; // 用户进程内存块描述符
内存块管理初始化
内核内存块管理初始化
/kernel/memory.c
/*
初始化内存块管理数组
分别对应16、32、64、128、256、512、1024这7种大小内存块类型
*/
void block_desc_init(struct mem_block_desc *desc_array)
{
uint16_t desc_idx, block_size = 16;
for (desc_idx = 0; desc_idx < DESC_CNT; desc_idx++)
{
// 块的大小
desc_array[desc_idx].block_size = block_size;
// 块的总数
desc_array[desc_idx].blocks_per_arena = (PG_SIZE - sizeof(struct arena)) / block_size;
// 管理组织块的双链表结构
list_init(&desc_array[desc_idx].free_list);
block_size *= 2;
}
}
添加内存块管理结构的初始化入口
/* 内存管理部分初始化入口 */
void mem_init()
{
put_str("mem_init start\n");
uint32_t mem_bytes_total = (*(uint32_t *)(0xb00));
mem_pool_init(mem_bytes_total); // 初始化内存池
block_desc_init(k_block_descs);
put_str("mem_init done\n");
}
用户内存块管理初始化
/userprog/process.c
// 用于创建进程,参数是进程要执行的函数与他的名字
void process_execute(void *filename, char *name)
{
/* pcb内核的数据结构,由内核来维护进程信息,因此要在内核内存池中申请 */
struct task_struct *thread = get_kernel_pages(1);
/*初始化pcb*/
init_thread(thread, name, default_prio);
// 初始化进程pcb中特有的页目录表
thread->pgdir = create_page_dir();
// 初始化进程虚拟内存池,进程有自己的虚拟内存
create_user_vaddr_bitmap(thread);
// 初始化进程空闲链表管理数组
block_desc_init(thread->u_block_desc);
// 首先初始化线程栈运行环境,执行start_process
// 然后初始化中断栈,此时的中断栈也是进程的运行栈,接着执行真正的执行函数filename
thread_create(thread, start_process, filename);
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);
}
用户内存块的初始化在创建用户进程时进行,而用户进程的创建也是内核负责完成的,因此只需在用户创建函数中调用block_desc_init完成对pcb中内存块管理结构的初始化即可,如下
// 初始化进程空闲链表管理数组
block_desc_init(thread->u_block_desc);
实现sys_malloc
/kernel/memory.c
首先是一些必要的辅助函数
/*返回arena中第idx个内存块的地址*/
static struct mem_block *arena2block(struct arena *a, uint32_t idx)
{
/*跨过arana元信息,然后按照块大小和索引值寻找对应内存块地址*/
return (struct mem_block *)((uint32_t)a + sizeof(struct arena) + idx * a->desc->block_size);
}
/*返回内存块block所在的arena地址*/
static struct arena *block2arena(struct mem_block *block)
{
return (struct arena *)((uint32_t)block & 0xfffff000);
}
接下来正式实现sys_malloc
/*在堆中申请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_thread = running_thread();
// 判断从哪个内存池中申请内存
if (cur_thread->pgdir == NULL)
{
// 如果是内存线程,就从内核内存池中申请内存
PF = PF_KERNEL;
pool_size = kernel_pool.pool_size;
mem_pool = &kernel_pool;
descs = k_block_descs;
}
else
{
// 从用户内存池中申请内存
PF = PF_USER;
pool_size = user_pool.pool_size;
mem_pool = &user_pool;
descs = cur_thread->u_block_desc;
}
/* 若申请的内存不在内存池容量范围内则直接返回NULL */
if (!(size > 0 && size < pool_size))
return NULL;
struct arena *a;
struct mem_block *block;
lock_acquire(&mem_pool->lock);
/* 超过最大内存块1024, 就分配页框 */
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;
lock_release(&mem_pool->lock);
// 跨过arena大小,把剩下的内存返回
return (void *)(a + 1);
}
else
{
lock_release(&mem_pool->lock);
return NULL;
}
}
// 若申请的内存小于等于1024,可在各种规格的mem_block_desc中去适配
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))
{
// 如果链表为空,说明此时没有空闲块,就创建新块分配之
// 分配1页框做为arena
a = malloc_page(PF, 1);
if (a == NULL)
{
lock_release(&mem_pool->lock);
return NULL;
}
memset(a, 0, PG_SIZE);
/* 对于分配的小块内存,将desc置为相应内存块描述符,
* cnt置为此arena可用的内存块数,large置为false */
a->desc = &descs[desc_idx];
a->large = false;
a->cnt = descs[desc_idx].blocks_per_arena;
uint32_t block_idx;
enum intr_status old_status = intr_disable();
/* 开始将arena拆分成内存块,并添加到内存块描述符的free_list中 */
for (block_idx = 0; block_idx < descs[desc_idx].blocks_per_arena; block_idx++)
{
block = arena2block(a, block_idx);
ASSERT(!elem_find(&a->desc->free_list, &block->free_elem));
list_append(&a->desc->free_list, &block->free_elem);
}
intr_set_status(old_status);
}
/* 开始分配内存块 */
// block = elem2entry(struct mem_block, free_elem, list_pop(&(descs[desc_idx].free_list)));
block = list_pop(&(descs[desc_idx].free_list));
memset(block, 0, descs[desc_idx].block_size);
a = block2arena(block);
a->cnt--;
lock_release(&mem_pool->lock);
return (void *)block;
}
}
其代码逻辑为
判断在哪种类型的内存池中申请内存块——内核内存池 or 用户内存池?
判断申请的内存大小是否大于最大可分配类型大小的内存1024B
如果申请内存的块大小,大于最大可分配类型大小的内存1024B,则说明无法分配小的内存块,直接分配一页页框给申请者
否则转向步骤3
遍历各种类型大小的内存块,找到第一个满足申请内存大小的类型内存(首次适配算法)。如申请者申请500B的内存,遍历后发现256B<500B<512B,说明此时可以尝试去找一块空闲的512B的内存块分配给申请者
查看空闲链表是否为空,即查看是否有没有被分配的512B内存块
如果空闲链表为空,说明512B大小的内存块还没有创建或者已经被分配完了
则此时重新从内核中申请一页内存
然后将该页内存划分成7个(由于有arena的存在,可用的块只有7个)512B大小的内存块
然后将划分好的内存块插入到空闲链表中
如果空闲链表不为空,则转向5
空闲链表不为空,说明有可用的内存块使用,则从空闲链表中弹出然后分配给申请者
测试
/kernel/main.c
#include "print.h"
#include "init.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall-init.h"
#include "syscall.h"
#include "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("I am kernel\n");
init_all();
intr_enable();
thread_start("k_thread_a", 31, k_thread_a, "I am thread_a");
thread_start("k_thread_b", 31, k_thread_b, "I am thread_b ");
while (1)
;
return 0;
}
/* 在线程中运行的函数 */
void k_thread_a(void *arg)
{
char *para = arg;
void *addr = sys_malloc(33);
console_put_str(" I am thread_a, sys_malloc(33), addr is 0x");
console_put_int((int)addr);
console_put_char('\n');
while (1)
;
}
/* 在线程中运行的函数 */
void k_thread_b(void *arg)
{
char *para = arg;
void *addr = sys_malloc(63);
console_put_str(" I am thread_b, sys_malloc(63), addr is 0x");
console_put_int((int)addr);
console_put_char('\n');
while (1)
;
}
/* 测试用户进程 */
void u_prog_a(void)
{
char *name = "prog_a";
printf(" I am %s, my pid:%d%c", name, getpid(), '\n');
while (1)
;
}
/* 测试用户进程 */
void u_prog_b(void)
{
char *name = "prog_b";
printf(" I am %s, my pid:%d%c", name, getpid(), '\n');
while (1)
;
}
编译运行
内存释放
内存块的释放是基于页面的——假如所有内存块都空闲,则直接将该页面释放,否则就只是将该内存块插入到空闲链表中
因此我们首先需要构建内存页的释放,内存页的释放是内存页分配的逆过程
在物理内存池中释放物理内存页(只需将位图置为0即可)
清除页表中的页表项,即清除掉虚拟内存和物理内存的映射关系
在虚拟内存池中释放虚拟内存页(只需将位图置为0即可)
内存页的释放
释放物理内存页
/kernel/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;
}
// 将位图中该位清0
bitmap_set(&mem_pool->pool_bitmap, bit_idx, 0);
}
删除映射关系
/kernel/memory.c
/* 去掉页表中虚拟地址vaddr的映射,只去掉vaddr对应的pte */
static void page_table_pte_remove(uint32_t vaddr)
{
uint32_t *pte = pte_ptr(vaddr);
// 将页表项pte的P位置0
*pte &= ~PG_P_1;
// 更新tlb
asm volatile("invlpg %0" ::"m"(vaddr) : "memory");
}
释放虚拟内存页
/kernel/memory.c
/*在虚拟地址池中释放以_vaddr起始的连续pg_cnt个虚拟页地址,实质就是清除虚拟内存池位图的位*/
static void vaddr_remove(enum pool_flags 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);
}
else
{ // 用户虚拟内存池
struct task_struct *cur_thread = running_thread();
bit_idx_start = (vaddr - cur_thread->userprog_vaddr.vaddr_start) / PG_SIZE;
while (cnt < pg_cnt)
bitmap_set(&cur_thread->userprog_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 0);
}
}
内存页释放过程封装
/* 释放以虚拟地址vaddr为起始的cnt个物理页框 */
void mfree_page(enum pool_flags pf, void *_vaddr, uint32_t pg_cnt)
{
uint32_t vaddr = (uint32_t)_vaddr, page_cnt = 0;
ASSERT(pg_cnt >= 1 && vaddr % PG_SIZE == 0);
uint32_t pg_phy_addr = addr_v2p(vaddr);
/* 确保待释放的物理内存在低端1M+1k大小的页目录+1k大小的页表地址范围外 */
ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= 0x102000);
/* 判断pg_phy_addr属于用户物理内存池还是内核物理内存池 */
if (pg_phy_addr >= user_pool.phy_addr_start)
{ // 位于user_pool内存池
vaddr -= PG_SIZE;
while (page_cnt < pg_cnt)
{
vaddr += PG_SIZE;
pg_phy_addr = addr_v2p(vaddr);
/* 确保物理地址属于用户物理内存池 */
ASSERT((pg_phy_addr % PG_SIZE) == 0 && pg_phy_addr >= user_pool.phy_addr_start);
/* 先将对应的物理页框归还到内存池 */
pfree(pg_phy_addr);
/* 再从页表中清除此虚拟地址所在的页表项pte */
page_table_pte_remove(vaddr);
page_cnt++;
}
/* 清空虚拟地址的位图中的相应位 */
vaddr_remove(pf, _vaddr, pg_cnt);
}
else
{ // 位于kernel_pool内存池
vaddr -= PG_SIZE;
while (page_cnt < pg_cnt)
{
vaddr += PG_SIZE;
pg_phy_addr = addr_v2p(vaddr);
/* 确保待释放的物理内存只属于内核物理内存池 */
ASSERT((pg_phy_addr % PG_SIZE) == 0 &&
pg_phy_addr >= kernel_pool.phy_addr_start &&
pg_phy_addr < user_pool.phy_addr_start);
/* 先将对应的物理页框归还到内存池 */
pfree(pg_phy_addr);
/* 再从页表中清除此虚拟地址所在的页表项pte */
page_table_pte_remove(vaddr);
page_cnt++;
}
/* 清空虚拟地址的位图中的相应位 */
vaddr_remove(pf, _vaddr, pg_cnt);
}
}
sys_free
有了内存页的释放过程之后,内存块的释放就可以实现了,其逻辑同上所述
判断释放的内存块大小是否大于可分配的最大大小1024B,如果是就直接释放这个页面,否则转步骤2
判断当前内存块所在的arena是否所有块都空闲,如果是就直接释放这个页面,否则转步骤3
仅仅释放该内存块,将其插入空闲链表即可
/* 回收内存块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 == 0 || a->large == 1);
if (a->desc == NULL && a->large == true)
{
// 大于1024的内存
mfree_page(PF, a, a->cnt);
}
else
{
// 小于等于1024的内存块先将内存块回收到free_list
list_append(&a->desc->free_list, &b->free_elem);
/* 再判断此arena中的内存块是否都是空闲,如果是就释放arena */
if (++a->cnt == a->desc->blocks_per_arena)
{
uint32_t block_idx;
for (block_idx = 0; block_idx < a->desc->blocks_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);
}
}
测试
/kernel/main.c
#include "print.h"
#include "init.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall-init.h"
#include "syscall.h"
#include "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("I am kernel\n");
init_all();
intr_enable();
thread_start("k_thread_a", 31, k_thread_a, "I am thread_a");
thread_start("k_thread_b", 31, k_thread_b, "I am thread_b ");
while (1)
;
return 0;
}
/* 在线程中运行的函数 */
void k_thread_a(void *arg)
{
char *para = arg;
void *addr1;
void *addr2;
void *addr3;
void *addr4;
void *addr5;
void *addr6;
void *addr7;
console_put_str(" thread_a start\n");
int max = 1000;
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);
}
console_put_str(" thread_a end\n");
while (1)
;
}
/* 在线程中运行的函数 */
void k_thread_b(void *arg)
{
char *para = arg;
void *addr1;
void *addr2;
void *addr3;
void *addr4;
void *addr5;
void *addr6;
void *addr7;
void *addr8;
void *addr9;
int max = 1000;
console_put_str(" thread_b 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);
}
console_put_str(" thread_b end\n");
while (1)
;
}
/* 测试用户进程 */
void u_prog_a(void)
{
char *name = "prog_a";
printf(" I am %s, my pid:%d%c", name, getpid(), '\n');
while (1)
;
}
/* 测试用户进程 */
void u_prog_b(void)
{
char *name = "prog_b";
printf(" I am %s, my pid:%d%c", name, getpid(), '\n');
while (1)
;
}
编译运行
实现系统调用malloc和free
malloc和free的实现其实是对sys_malloc和sys_free的用户实现
添加内核系统调用例程
/userprog/syscall-init.c
/* 初始化系统调用 */
void syscall_init(void)
{
put_str("syscall_init start\n");
syscall_table[SYS_GETPID] = sys_getpid;
syscall_table[SYS_WRITE] = sys_write;
syscall_table[SYS_MALLOC] = sys_malloc;
syscall_table[SYS_FREE] = sys_free;
put_str("syscall_init done\n");
}
添加用户系统调用函数malloc和free
/lib/user/syscall.h
#ifndef __LIB_USER_SYSCALL_H
#define __LIB_USER_SYSCALL_H
#include "stdint.h"
/*定义系统调用号*/
enum SYSCALL_NR
{
SYS_GETPID,
SYS_WRITE,
SYS_MALLOC,
SYS_FREE
};
uint32_t getpid(void);
uint32_t write(char *str);
void *malloc(uint32_t size);
void free(void *ptr);
#endif
/lib/user/syscall.c
/* 申请size字节大小的内存,并返回结果 */
void *malloc(uint32_t size)
{
return (void *)_syscall1(SYS_MALLOC, size);
}
/* 释放ptr指向的内存 */
void free(void *ptr)
{
_syscall1(SYS_FREE, ptr);
}
测试
/kernel/main.c
#include "print.h"
#include "init.h"
#include "thread.h"
#include "interrupt.h"
#include "console.h"
#include "process.h"
#include "syscall-init.h"
#include "syscall.h"
#include "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("I am kernel\n");
init_all();
intr_enable();
process_execute(u_prog_a, "u_prog_a");
process_execute(u_prog_b, "u_prog_b");
thread_start("k_thread_a", 31, k_thread_a, "I am thread_a");
thread_start("k_thread_b", 31, k_thread_b, "I am thread_b");
while (1)
;
return 0;
}
/* 在线程中运行的函数 */
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 = 9999999;
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 = 999999;
while (cpu_delay-- > 0)
;
sys_free(addr1);
sys_free(addr2);
sys_free(addr3);
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)
;
}
编译运行
原文地址:https://blog.csdn.net/qq_58158950/article/details/145018041
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!