自学内容网 自学内容网

《操作系统真象还原》第十二章(二) —— 完善堆内存管理

章节任务介绍

任务简介

上一节,我们完成了用户程序和操作系统之间的接口——系统调用,本节我们在此基础上完善堆内存管理——实现malloc和free

之前我们虽然已经实现了内存管理,但之前实现分配的内存都是以 4KB 大小的页框为单位的,当我们仅需要几十字节、几百字节这样的小内存块时,显然无法满足这样的需求了,为此必须实现一种小内存块的管理,可以满足任意内存大小的分配,这就是我们为实现malloc要做的基础工作。

本节的主要任务有:

  1. 实现sys_malloc

  2. 实现sys_free

  3. 实现malloc

  4. 实现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;
}
}

其代码逻辑为

  1. 判断在哪种类型的内存池中申请内存块——内核内存池 or 用户内存池?

  2. 判断申请的内存大小是否大于最大可分配类型大小的内存1024B

    • 如果申请内存的块大小,大于最大可分配类型大小的内存1024B,则说明无法分配小的内存块,直接分配一页页框给申请者

    • 否则转向步骤3

  3. 遍历各种类型大小的内存块,找到第一个满足申请内存大小的类型内存(首次适配算法)。如申请者申请500B的内存,遍历后发现256B<500B<512B,说明此时可以尝试去找一块空闲的512B的内存块分配给申请者

  4. 查看空闲链表是否为空,即查看是否有没有被分配的512B内存块

    • 如果空闲链表为空,说明512B大小的内存块还没有创建或者已经被分配完了

      • 则此时重新从内核中申请一页内存

      • 然后将该页内存划分成7个(由于有arena的存在,可用的块只有7个)512B大小的内存块

      • 然后将划分好的内存块插入到空闲链表中

    • 如果空闲链表不为空,则转向5

  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)
      ;
}

 编译运行

内存释放

内存块的释放是基于页面的——假如所有内存块都空闲,则直接将该页面释放,否则就只是将该内存块插入到空闲链表中

因此我们首先需要构建内存页的释放,内存页的释放是内存页分配的逆过程

  1. 在物理内存池中释放物理内存页(只需将位图置为0即可)

  2. 清除页表中的页表项,即清除掉虚拟内存和物理内存的映射关系

  3. 在虚拟内存池中释放虚拟内存页(只需将位图置为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

有了内存页的释放过程之后,内存块的释放就可以实现了,其逻辑同上所述

  1. 判断释放的内存块大小是否大于可分配的最大大小1024B,如果是就直接释放这个页面,否则转步骤2

  2. 判断当前内存块所在的arena是否所有块都空闲,如果是就直接释放这个页面,否则转步骤3

  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)!