12.1位视图的创建和对内存的分配
对于内存管理要处理下列问题
位视图(页为单位)解决的是内存的分配问题
位视图的数据结构(结构体)
//D:\code\x86\code\start\start\source\kernel\include\tools\bitmap.h
#ifndef BITMAP_H
#define BITMAP_H
#include "comm/types.h"
/**
* @brief 位图数据结构
*/
typedef struct _bitmap_g {
int bit_count; // 位的数据,位数量
uint8_t * bits; // 位空间,字节数组,靠外面传进来
}bitmap_t;
void bitmap_init (bitmap_t * bitmap, uint8_t * bits, int count, int init_bit); //初始化,init_bit表示初始化是全0还是全1
int bitmap_byte_count (int bit_count); //将bit转换位byte,向上取整
int bitmap_get_bit (bitmap_t * bitmap, int index);//位图中某一个位的状态值
void bitmap_set_bit (bitmap_t * bitmap, int index, int count, int bit); //对连续count个内存页进行设置
int bitmap_is_set (bitmap_t * bitmap, int index); //判断某一位是否置0或者置1了
int bitmap_alloc_nbits (bitmap_t * bitmap, int bit, int count);//进程想要多少个页,bit表是想要1还是想要0和init_bit功能类似
#endif // BITMAP_H
//D:\code\x86\code\start\start\source\kernel\tools\bitmap.c
#include "tools/bitmap.h"
#include "tools/klib.h"
/**
* @brief 获取所需要的字节数量
*/
int bitmap_byte_count (int bit_count) {
return (bit_count + 8 - 1) / 8; // 向上取整,前面算task sleep时间也用到了这个取整方法
}
/**
* @brief 位图初始化
*/
void bitmap_init (bitmap_t * bitmap, uint8_t * bits, int count, int init_bit) {
bitmap->bit_count = count;//位图需要多少位
bitmap->bits = bits; //字节数组
int bytes = bitmap_byte_count(bitmap->bit_count); //位图需要多少byte
kernel_memset(bitmap->bits, init_bit ? 0xFF: 0, bytes); //利用uint8数组起始地址,给数组赋值,内存赋值函数是按字节为单位
}
/**
* @brief 连续设置N个位
*/
void bitmap_set_bit (bitmap_t * bitmap, int index, int count, int bit) {
for (int i = 0; (i < count) && (index < bitmap->bit_count); i++, index++) {
// 可以考虑进行一定程序的优化!!
if (bit) {
bitmap->bits[index / 8] |= 1 << (index % 8);
} else {
bitmap->bits[index / 8] &= ~(1 << (index % 8));
}
}
}
/**
* @brief 获取指定位的状态
*/
int bitmap_get_bit (bitmap_t * bitmap, int index) {
//return bitmap->bits[index / 8] & (1 << (index % 8));
// 2023-3-9 这里应该返回0或者1
return (bitmap->bits[index / 8] & (1 << (index % 8))) ? 1 : 0;
}
/**
* @brief 检查指定位是否置1
*/
int bitmap_is_set (bitmap_t * bitmap, int index) {
return bitmap_get_bit(bitmap, index) ? 1 : 0;
}
/**
* @brief 连续分配若干指定比特位,返回起始索引
*/
int bitmap_alloc_nbits (bitmap_t * bitmap, int bit, int count) {
int search_idx = 0;
int ok_idx = -1;
while (search_idx < bitmap->bit_count) {
// 定位到第一个相同的索引处
if (bitmap_get_bit(bitmap, search_idx) != bit) {
// 不同,继续寻找起始的bit
search_idx++;
continue;
}
// 记录起始索引
ok_idx = search_idx;
// 继续计算下一部分
int i;
for (i = 1; (i < count) && (search_idx < bitmap->bit_count); i++) {
if (bitmap_get_bit(bitmap, search_idx++) != bit) {
// 不足count个,退出,重新进行最外层的比较
ok_idx = -1;
break;
}
}
// 找到,设置各位,然后退出
if (i >= count) {
bitmap_set_bit(bitmap, ok_idx, count, ~bit); //将这些位设置为1,表示自己占用了这些空间
return ok_idx;
}
}
return -1;
}
利用位视图分配内存
需要一个结构体来囊括
//D:\code\x86\code\start\start\source\kernel\include\core\memory.h
#ifndef MEMORY_H
#define MEMORY_H
#include "tools/bitmap.h"
#include "comm/boot_info.h"
#include "ipc/mutex.h"
/**
* @brief 地址分配结构
*/
typedef struct _addr_alloc_t { //专门涌来内存的分配与释放
mutex_t mutex; // 内存分配的互斥信号量
bitmap_t bitmap; // 辅助分配用的位图
uint32_t page_size; // 页(块)大小
uint32_t start; // 内存起始地址
uint32_t size; // 地址大小
}addr_alloc_t;
void memory_init (boot_info_t * boot_info); //初始化需要知道整个内存的分配信息,之前章节传出来的
#endif // MEMORY_H
//D:\code\x86\code\start\start\source\kernel\core\memory.c
/**
* 内存管理
*
* 作者:李述铜
* 联系邮箱: 527676163@qq.com
*/
#include "tools/klib.h"
#include "tools/log.h"
#include "core/memory.h"
/**
* @brief 初始化地址分配结构
* 以下不检查start和size的页边界,由上层调用者检查
*/
static void addr_alloc_init (addr_alloc_t * alloc, uint8_t * bits, uint32_t start, uint32_t size, uint32_t page_size) {
mutex_init(&alloc->mutex);
alloc->start = start;
alloc->size = size; //KB一共大小
alloc->page_size = page_size; //每一页大小
bitmap_init(&alloc->bitmap, bits, alloc->size / page_size, 0);
}
/**
* @brief 分配多页内存
*/
static uint32_t addr_alloc_page (addr_alloc_t * alloc, int page_count) {
uint32_t addr = 0;
mutex_lock(&alloc->mutex);
int page_index = bitmap_alloc_nbits(&alloc->bitmap, 0, page_count); //连续分配page_count个页
if (page_index >= 0) {
addr = alloc->start + page_index * alloc->page_size;//这是分配出来内存的起始地址
}
mutex_unlock(&alloc->mutex);
return addr;
}
/**
* @brief 释放多页内存
*/
static void addr_free_page (addr_alloc_t * alloc, uint32_t addr, int page_count) {
mutex_lock(&alloc->mutex);
uint32_t pg_idx = (addr - alloc->start) / alloc->page_size;
bitmap_set_bit(&alloc->bitmap, pg_idx, page_count, 0);
mutex_unlock(&alloc->mutex);
}
/**
* @brief 初始化内存管理系统
* 该函数的主要任务:
* 1、初始化物理内存分配器:将所有物理内存管理起来. 在1MB内存中分配物理位图
* 2、重新创建内核页表:原loader中创建的页表已经不再合适
*/
void memory_init (boot_info_t * boot_info) {
addr_alloc_t addr_alloc; //创建一个内存分配器
uint8_t bits[8]; //位图
addr_alloc_init(&addr_alloc, bits, 0x1000, 64*4096, 4096); //从4KB位置开始.页大小为4KB,分配256KB的内存大小
for (int i = 0; i < 32; i++) {
uint32_t addr = addr_alloc_page(&addr_alloc, 2);
log_printf("alloc addr: 0x%x", addr);
} //一共分配64页
uint32_t addr = 0;
for (int i = 0; i < 32; i++) {
addr_free_page(&addr_alloc, addr, 2);
addr += 8192;
}
}