循序渐进,学习开发一个RISC-V 上的操作系统

第 8 章 内存管理

对内存进一步的管理,实现动态的分配和释放。

  • 自动管理内存 - 栈(stack)(自动变量,生命周期为函数调用和返回)
  • 静态内存 - 全局变量/静态变量(放在ELF文件加载进内存的数据段里  .data .odata等)
  • 动态管理内存 - 堆(heap)(malloc free 程序员自己申请和释放)

  • .text:已编译程序的机器代码。
  • .rodate:只读数据,比如printf语句中的格式串和switch的跳转表。
  • .data:已经初始化的全局变量和静态C变量。
  • .bss:未初始化的全局变量和静态C变量,以及所有被初始化为0的全局或静态变量,仅是占位符,不占据任何实际磁盘空间。区分初始化和非初始化是为了空间效率。
  • .symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量静态变量的信息,不包括局部变量。

 多个C文件 只是被编译成可重定向文件,每个.o文件代码和数据的地址都是从0开始。 通过链接器脚本获得位置信息,生成最后的可执行文件。

 

 

 

 ENTRY(_start)指明程序从_start指向的那条指令开始运行

OUTPUT_ARCH("riscv")指定输出的文件使用RISCV体系结构,具体的32or64通过调用gcc的编译选项时规定。

 规定一块名为ram的内存 起始地址为0x80000000,长度为128M,权限为可写可执行可分配 可读 进行初始化。”!“是什么意思没搞清楚

 

 PROVIDE(_text_start = .);即表示 把当前地址赋值给_test_start这个符号 “.”表示当前地址

 .text:那一段代码表示 将所有输入文件(可重定位文件)里的.text、.text.*都放在这一段内存里(ram)组成输出elf文件中的.text段。而且定义了这个段的起始地址_text_start和结束地址_text_end

注意.data段首先有一个. = ALIGN(4096) 是因为页大小为4K,使地址对齐。

 通过PROVIDE可以定义很多地址符号:例如内存起始地址_memory_start = ram的起始地址

内存终止地址_memort_end = ram起始地址加上ram的长度。

 链接器脚本里定义的符号就可以直接用在汇编代码里了。

实现 Page 级别的内存分配和释放

数据结构设计

 

 

在堆里面预留一部分内存用来存储页分配信息。注意索引部分内存也要进行4K对齐。

索引数据结构就是一个8位的flags ,里面第0位表示该页是否被分配,第1位表示是否为分配的最后一页。(分配的页是连续的)

#define PAGE_TAKEN (uint8_t)(1 << 0)
#define PAGE_LAST  (uint8_t)(1 << 1)

/*
 * Page Descriptor 
 * flags:
 * - bit 0: flag if this page is taken(allocated)
 * - bit 1: flag if this page is the last page of the memory block allocated
 */
struct Page {
	uint8_t flags;
};

Page 分配和释放接口设计

页分配函数中首先设置标志位found

然后从HEAP_START位置开始搜索第一块没有被分配的页的索引page_i

以page_i开始搜索npages个索引看是否都是没有被分配的页

如果存在符合要求的,那么将最后一页的索引设置为最后一页,并返回page_i所指向页的物理地址

否则,返回第一步。寻找下一块没有被分配的页。

void *page_alloc(int npages)
{
	/* Note we are searching the page descriptor bitmaps. */
	int found = 0;
	struct Page *page_i = (struct Page *)HEAP_START;
	for (int i = 0; i <= (_num_pages - npages); i++) {
		if (_is_free(page_i)) {
			found = 1;
			/* 
			 * meet a free page, continue to check if following
			 * (npages - 1) pages are also unallocated.
			 */
			struct Page *page_j = page_i;
			for (int j = i; j < (i + npages); j++) {
				if (!_is_free(page_j)) {
					found = 0;
					break;
				}
				page_j++;
			}
			/*
			 * get a memory block which is good enough for us,
			 * take housekeeping, then return the actual start
			 * address of the first page of this memory block
			 */
			if (found) {
				struct Page *page_k = page_i;
				for (int k = i; k < (i + npages); k++) {
					_set_flag(page_k, PAGE_TAKEN);
					page_k++;
				}
				page_k--;
				_set_flag(page_k, PAGE_LAST);
				return (void *)(_alloc_start + i * PAGE_SIZE);
			}
		}
		page_i++;
	}
	return NULL;
}

/*
 * Free the memory block
 * - p: start address of the memory block
 */
void page_free(void *p)
{
	/*
	 * Assert (TBD) if p is invalid
	 */
	if (!p || (uint32_t)p >= _alloc_end) {
		return;
	}
	/* get the first page descriptor of this memory block */
	struct Page *page = (struct Page *)HEAP_START;
	page += ((uint32_t)p - _alloc_start)/ PAGE_SIZE;
	/* loop and clear all the page descriptors of the memory block */
	while (!_is_free(page)) {
		if (_is_last(page)) {
			_clear(page);
			break;
		} else {
			_clear(page);
			page++;;
		}
	}
}

页释放函数,若指针为空或者指针地址超出页范围,直接返回

否则,修改该段被分配的页表的索引值,注意最后一页要修改is_last索引值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值