2、内存管理

        在这一章中引入了ld链接脚本如何指示程序的入口地址,如何指导.text、.data、.bss、.stack以及heap在内存中的管理。以及如何在start.S启动文件中,将位于flash中的数据搬移到sram中。并实现了一个简单的动态内存申请功能。

一、ld链接脚本

        链接脚本用于指定链接器如何将程序的各个部分(如代码段、数据段、栈、堆等)安排到内存中的特定位置。它可以精确的控制程序如何被加载到内存中,尤其是在资源有限的嵌入式设备上。

定义内存区域。首先定义程序将使用的内存区域。每个区域都可以指定起始地址、大小和访问权限。通常会定义FLASH和RAM两个区域。其中ENTRY( _start )即在指示程序入口地址。

ENTRY( _start )
MEMORY{
    RAM (xrw) : ORIGIN = 0x80000000, LENGTH = 32K
    FLASH (rx) : ORIGIN = 0x20000000, LENGTH = 128K
}

设置段(sections)。链接脚本中会指定如何将代码和数据等分配到内存中定义的区域。每个段(如.text、.data、.bss)都可以指定存放位置和对齐方式。

SECTIONS {

    .text : {
        PROVIDE(_text_start = .);
        *(.text .text.*)
        PROVIDE(_text_end = .);
    } >FLASH AT>FLASH

    .rodata : {
        PROVIDE(_rodata_start = .);
        *(.rodata .rodata.*)
        PROVIDE(_rodata_end = .);
    } >FLASH AT>FLASH

    .dalign : {
        . = ALIGN(4);
        PROVIDE(_data_vma = .);
    } >RAM AT>FLASH
    .dlalign : {
        . = ALIGN(4);
        PROVIDE(_data_lma = .);
    } >FLASH AT>FLASH

    .data : {
        PROVIDE(_data_start = .);
        *(.data .data.*)
        *(.sdata .sdata.*)
        . = ALIGN(4);
        PROVIDE(_data_end = .);
    } >RAM AT>FLASH

    .bss : {
        . = ALIGN(4);
        PROVIDE(_start_bss = .);
        *(.bss .bss.*)
        *(.sbss .sbss.*)
        . = ALIGN(4);
        PROVIDE(_end_bss = .);
    } >RAM AT>FLASH

    .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size : {
        PROVIDE( _heap_end = . );
        . = ALIGN(4);
        PROVIDE(_start_stack = .);
        . = . + __stack_size;
        PROVIDE(_end_stack = .);
    } >RAM

    PROVIDE(_memory_start = ORIGIN(RAM));
    PROVIDE(_memory_end = ORIGIN(RAM) + LENGTH(RAM));

    PROVIDE(_heap_start = _end_bss);
    PROVIDE(_heap_size = _start_stack - _heap_start);
}

        .text段用于存储程序代码,放在flash中。

        .rodata段用于存储静态变量或常量,放在flash中。

        .data段用于存储已初始化的全局变量,放在RAM中。

        .bss段存储未初始化的全局变量,放在RAM中。

符号和重定位。 链接脚本可以用PROVIDE来定义符号,其值是当前内存位置。

对齐方式和内存填充。链接脚本可以通过ALIGN来确保内存段的对齐,以优化内存访问效率和避免内存碎片。 ALIGN(4)表示地址会被调整为4字节对齐。

加载地址和运行地址。 加载地址是程序在内存中被加载的位置。运行地址是程序在运行时的实际地址。例如,存储时会被加载到物理FLASH中但在运行时会被加载到内存RAM区域中。

.bss : {
        . = ALIGN(4);
        PROVIDE(_start_bss = .);
        *(.bss .bss.*)
        *(.sbss .sbss.*)
        . = ALIGN(4);
        PROVIDE(_end_bss = .);
    } >RAM AT>FLASH

二 、mem.S

        在mem.S中指示出了,ld链接文件里定义的不同区域的起始和结束地址。包括代码段(.text)、数据段(.data)、只读数据段(.rodata)、bss段、堆区和栈区。这些符号通过.word 指令存储了对应的地址,用于程序在运行时访问和管理内存区域,确保内存的正确初始化和使用。

.section .rodata

.global TEXT_START
TEXT_START: .word _text_start

.global TEXT_END
TEXT_END: .word _text_end

.global DATA_START
DATA_START: .word _data_start

.globaL DATA_END
DATA_END: .word _data_end

.global RODATA_START
RODATA_START: .word _rodata_start

.global RODATA_END
RODATA_END: .word _rodata_end

.global BSS_START
BSS_START: .word _start_bss

.global BSS_END
BSS_END: .word _end_bss

.global HEAP_START
HEAP_START: .word _heap_start

.global HEAP_SIZE
HEAP_SIZE: .word _heap_size

.global STACK_START
STACK_START: .word _start_stack

.global STACK_END
STACK_END: .word _end_stack

三、动态内存分配的实现

        随后使用分配出来的堆区,按照一定字节划分成若干page,供用户程序申请使用。

#include "os.h"

extern ptr_t TEXT_START;
extern ptr_t TEXT_END;
extern ptr_t DATA_START;
extern ptr_t DATA_END;
extern ptr_t RODATA_START;
extern ptr_t RODATA_END;
extern ptr_t BSS_START;
extern ptr_t BSS_END;
extern ptr_t HEAP_START;
extern ptr_t HEAP_SIZE;
extern ptr_t STACK_START;
extern ptr_t STACK_END;

/*
 * _alloc_start points to the actual start address of heap pool
 * _alloc_end points to the actual end address of heap pool
 * _num_pages holds the actual max number of pages we can allocate.
 */
static uint32_t _alloc_start = 0;
static uint32_t _alloc_end = 0;
static uint32_t _num_pages = 0;

#define PAGE_SIZE  4096
#define PAGE_MASK   (~(PAGE_SIZE-1))

#define PAGE_RESERVED 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;
};

static inline void _clear_page(struct page *page){
    page->flags = 0;
}

static inline int _is_page_free(struct page *page){
    return !(page->flags & PAGE_TAKEN);
}

static inline void _set_page_flag(struct page *page,uint8_t flag)
{
    page->flags |= flag;
}

static inline int _is_page_last(struct page *page){
    return page->flags & PAGE_LAST;
}

/*
 * align the address to the border of page(4K).
 */
static inline ptr_t _align_page(ptr_t addr)
{
    /* 
     * Add PAGE_SIZE - 1 to the address. This increases the address
     * to the next page boundary if it's not already aligned.
     * Then perform a bitwise AND operation with PAGE_MASK to
     * "rounds down" the address to the nearest page boundary.
     */
    return (addr + PAGE_SIZE - 1) & PAGE_MASK;
}

void page_init()
{
    /* 
     * We reserved PAGE_RESERVED pages to hold the Page structures.
     * Sub more 1 to avoid overlapping heap and stack.
    */ 
    _num_pages = (HEAP_SIZE / PAGE_SIZE) - PAGE_RESERVED - 1;    
    printf("HEAP_START = %x, HEAP_SIZE = %x, num of pages = %d\n",HEAP_START,HEAP_SIZE,_num_pages);

    struct page *page = (struct page *)HEAP_START;
    for (int i=0; i<_num_pages; i++){
        _clear_page(page);
        page++;
    }
    
    _alloc_start = _align_page(HEAP_START + PAGE_RESERVED * PAGE_SIZE);
    _alloc_end = _alloc_start + _num_pages * PAGE_SIZE;
    printf("TEXT:   0x%x -> 0x%x\n", TEXT_START, TEXT_END);
    printf("RODATA: 0x%x -> 0x%x\n", RODATA_START, RODATA_END);
    printf("DATA:   0x%x -> 0x%x\n", DATA_START, DATA_END);
    printf("BSS:    0x%x -> 0x%x\n", BSS_START, BSS_END);
    printf("HEAP:   0x%x -> 0x%x\n", _alloc_start, _alloc_end);
    printf("STACK:  0x%x -> 0x%x\n", STACK_START, STACK_END);
}

/*
 * Allocate a memory block which is composed of contiguous physical pages
 * - npages: the number of PAGE_SIZE pages to allocate
 */
void *page_alloc(int npages){
    if(npages <= 0 || _num_pages < npages){
        return NULL;
    }

    /* 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_page_free(page_i)){
            found=1;

            struct page *page_j = page_i + 1;
            for(int j=i+1; j<(i + npages); j++){
                if(!_is_page_free(page_j)){
                    found = 0;
                    break;
                }
                page_j++;
            }

            if(found){
                struct page *page_k = page_i;
                for(int k = i; k < (i + npages); k++){
                    _set_page_flag(page_k, PAGE_TAKEN);
                    page_k++;
                }
                page_k--;
                _set_page_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 || (ptr_t)p >= _alloc_end){
        return;
    }
    /* get the first page descriptor of this memory block */
    struct page *page = (struct page *)HEAP_START;
    page += ((ptr_t)p - _alloc_start)/ PAGE_SIZE;

    /* loop and clear all the page descriptors of the memory block */
    while(!_is_page_free(page)){
        if(_is_page_last(page)){
            _clear_page(page);
            break;
        }else{
            _clear_page(page);
            page++;
        }
    }
}

void page_test()
{
	void *p = page_alloc(2);
	printf("p = %p\n", p);
	//page_free(p);

	void *p2 = page_alloc(1);
	printf("p2 = %p\n", p2);
	page_free(p2);

	void *p3 = page_alloc(4);
	printf("p3 = %p\n", p3);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值