在之前已经学习了 cpu 的上电启动流程和裸机下的链接和启动文件,现在参考 FreeRTOS ,我们自底向上手动重新实现该系统,并确保其能够成功应用。

1. 环形双向链表

1.1 代码

源码中为 list.h 及 list.c 文件,其中使用了大量的宏定义,我们暂时不考虑 FreeRTOS.h 、FreeRTOSConfig.h 中的配置以及代码中的宏定义,在之后需要时考虑,为求易用,采用与源码不同的命名风格。

// list.h
#ifndef LIST_H
#define LIST_H

#include <stdint.h>
#include <stddef.h>
#include <assert.h>
#include <limits.h>

#define INTEGRITY_CHECK_VALUE  0x12345678U    // 完整性校验值
#define MAX_NODE_VAL           UINT32_MAX     // 节点最大值

// 前向声明
struct List;
typedef struct Node Node;

// 链表节点
struct Node {
    uint32_t integrity1;     // 完整性校验字段 1
    uint32_t val;            // 值
    Node* nxt;
    Node* pre;
    void* owner;             // 指向拥有此节点的对象(如任务控制块)
    struct List* container;  // 所属链表
    uint32_t integrity2;     // 完整性校验字段 2
};

// 链表结构
typedef struct List {
    uint32_t listIntegrity1;
    size_t nodeCount;        // 链表中实际节点数量(不含哨兵)
    Node dummy;              // 哨兵节点
    uint32_t listIntegrity2;
} List;

// ———————— 公共 API ————————

void list_init(List* list);
void list_initNode(Node* node);

void list_insertEnd(List* list, Node* newNode);
void list_insertSorted(List* list, Node* newNode);

size_t list_removeNode(Node* node);


// 其他
static inline int list_isEmpty(const List* list) {
    return (list->nodeCount == 0) ? 1 : 0;
}

static inline size_t list_length(const List* list) {
    return list->nodeCount;
}

static inline void* list_getOwnerOfHeadEntry(const List* list) {
    return ((Node* )&(list->dummy))->nxt->owner;
}

static inline uint32_t list_getHeadValue(const List* list) {
    return ((Node* )&(list->dummy))->nxt->val;
}

static inline int list_isNodeInList(const List* list, const Node* node) {
    return (node->container == list) ? 1 : 0;
}

static inline List* list_nodeGetContainer(const Node* node) {
    return node->container;
}

#endif
// list.c
#include "list.h"
#include <string.h>
#include <stdio.h>


// ———————— 内联辅助函数————————

// 只在 list.c 中使用 ,如果在其他文件中使用应放在头文件
// 1. inline 只是建议,编译器可以选择是否内联
// 2. C++中 inline 全局变量/函数为外部链接性,且允许重定义
//    static inline 不推荐,函数地址在不同 .cpp 文件中不相等
// 3. C 中 inline 全局变量/函数为外部链接性,但不允许重定义
//    使用static 后为内部链接,每个包含该头文件的 .c 文件都会生成一个本地副本
static inline void setNodeIntegrity(Node* node) {
    node->integrity1 = INTEGRITY_CHECK_VALUE;
    node->integrity2 = INTEGRITY_CHECK_VALUE;
}

static inline void setListIntegrity(List* list) {
    list->listIntegrity1 = INTEGRITY_CHECK_VALUE;
    list->listIntegrity2 = INTEGRITY_CHECK_VALUE;
}

static inline void checkNodeIntegrity(const Node* node) {
    assert(node->integrity1 == INTEGRITY_CHECK_VALUE);
    assert(node->integrity2 == INTEGRITY_CHECK_VALUE);
}

static inline void checkListIntegrity(const List* list) {
    assert(list->listIntegrity1 == INTEGRITY_CHECK_VALUE);
    assert(list->listIntegrity2 == INTEGRITY_CHECK_VALUE);
}

void list_init(List* list) {
    // 初始化哨兵节点,自环
    Node* dummy = &(list->dummy);
    dummy->val = MAX_NODE_VAL;
    dummy->nxt = (Node* )dummy;
    dummy->pre = (Node* )dummy;
    setNodeIntegrity(dummy);
    
    list->nodeCount = 0;

    setListIntegrity(list);
}

void list_initNode(Node* node) {
    node->container = NULL;
    setNodeIntegrity(node);
}

void list_insertEnd(List* list, Node* newNode) {
    checkListIntegrity(list);
    checkNodeIntegrity(newNode);

    Node* dummy = &(list->dummy);

    newNode->nxt = dummy;
    newNode->pre = dummy->pre;

    dummy->pre->nxt = newNode;
    dummy->pre = newNode;

    newNode->container = list;
    list->nodeCount++;
}

void list_insertSorted(List* list, Node* newNode) {
    checkListIntegrity(list);
    checkNodeIntegrity(newNode);

    uint32_t value = newNode->val;
    Node* iter;

    if (value == MAX_NODE_VAL) {
        // 插入到哨兵前(即逻辑末尾)
        iter = (Node* )&(list->dummy);
        iter = iter->pre;
    } else {
        // 从哨兵开始向前找第一个 val > value 的位置
        iter = (Node* )&(list->dummy);
        while (iter->nxt->val <= value) {
            iter = iter->nxt;
        }
    }

    // 插入在 iter 之后
    newNode->nxt = iter->nxt;
    newNode->pre = iter;
    iter->nxt->pre = newNode;
    iter->nxt = newNode;

    newNode->container = list;
    list->nodeCount++;
}

size_t list_removeNode(Node* node) {
    List* list = node->container;
    assert(node != &(list->dummy));

    node->nxt->pre = node->pre;
    node->pre->nxt = node->nxt;

    node->container = NULL;
    list->nodeCount--;

    return list->nodeCount;
}

#if 0

// 辅助函数:打印链表内容(仅用于调试)
void printList(const List* list) {
    printf("List length: %zu\n", list_length(list));
    if (list_isEmpty(list)) {
        printf("(empty)\n");
        return;
    }
    Node* dummy = (Node*)&(list->dummy);
    Node* cur = dummy->nxt;
    while (cur != dummy) {
        printf("%u -> ", cur->val);
        cur = cur->nxt;
    }
    printf("END\n");
}

int main(void) {
    printf("=== Starting List Test Suite ===\n");

    // Test 1: 初始化链表
    List list;
    list_init(&list);
    assert(list_isEmpty(&list));
    assert(list_length(&list) == 0);
    printf("✅ Test 1 passed: list initialized correctly.\n");

    // Test 2: 初始化节点并插入末尾
    Node node1, node2, node3;
    list_initNode(&node1); node1.val = 10; node1.owner = (void*)0x1;
    list_initNode(&node2); node2.val = 20; node2.owner = (void*)0x2;
    list_initNode(&node3); node3.val = 30; node3.owner = (void*)0x3;

    list_insertEnd(&list, &node1);
    assert(!list_isEmpty(&list));
    assert(list_length(&list) == 1);
    assert(list_getHeadValue(&list) == 10);
    assert(list_getOwnerOfHeadEntry(&list) == (void*)0x1);
    assert(list_isNodeInList(&list, &node1));
    assert(list_nodeGetContainer(&node1) == &list);
    printf("✅ Test 2 passed: insertEnd and basic accessors.\n");

    list_insertEnd(&list, &node2);
    assert(list_length(&list) == 2);
    // 验证顺序:10 -> 20
    Node* dummy = (Node*)&(list.dummy);
    assert(dummy->nxt->val == 10);
    assert(dummy->nxt->nxt->val == 20);
    printf("✅ Test 3 passed: multiple insertEnd.\n");

    // Test 4: 有序插入
    List sortedList;
    list_init(&sortedList);

    Node s1, s2, s3, s4;
    list_initNode(&s1); s1.val = 50;
    list_initNode(&s2); s2.val = 10;
    list_initNode(&s3); s3.val = 30;
    list_initNode(&s4); s4.val = 70;

    list_insertSorted(&sortedList, &s2); // 10
    list_insertSorted(&sortedList, &s3); // 10,30
    list_insertSorted(&sortedList, &s1); // 10,30,50
    list_insertSorted(&sortedList, &s4); // 10,30,50,70

    assert(list_length(&sortedList) == 4);
    dummy = (Node*)&(sortedList.dummy);
    assert(dummy->nxt->val == 10);
    assert(dummy->nxt->nxt->val == 30);
    assert(dummy->nxt->nxt->nxt->val == 50);
    assert(dummy->nxt->nxt->nxt->nxt->val == 70);
    printf("✅ Test 4 passed: insertSorted maintains order.\n");

    // Test 5: 插入 MAX_NODE_VAL(应插在末尾)
    Node maxNode;
    list_initNode(&maxNode);
    maxNode.val = MAX_NODE_VAL;
    list_insertSorted(&sortedList, &maxNode);
    assert(list_length(&sortedList) == 5);
    assert(((Node*)&sortedList.dummy)->pre->val == MAX_NODE_VAL);
    printf("✅ Test 5 passed: MAX_NODE_VAL inserted at end.\n");

    // Test 6: 删除节点
    size_t newLen = list_removeNode(&s3); // remove 30
    assert(newLen == 4);
    assert(!list_isNodeInList(&sortedList, &s3));
    assert(s3.container == NULL);

    // 验证剩余顺序:10,50,70,MAX
    dummy = (Node*)&(sortedList.dummy);
    assert(dummy->nxt->val == 10);
    assert(dummy->nxt->nxt->val == 50);
    assert(dummy->nxt->nxt->nxt->val == 70);
    assert(dummy->pre->val == MAX_NODE_VAL);
    printf("✅ Test 6 passed: node removal works.\n");

    // Test 7: 删除头节点
    list_removeNode(&s2); // remove 10
    assert(list_getHeadValue(&sortedList) == 50);
    printf("✅ Test 7 passed: head node removal.\n");

    // Test 8: 删除尾节点(非 MAX)
    list_removeNode(&s4); // remove 70
    assert(((Node*)&sortedList.dummy)->pre->val == MAX_NODE_VAL);
    assert(list_length(&sortedList) == 2); // 50, MAX
    printf("✅ Test 8 passed: tail node removal.\n");

    // Test 9: 完整性校验触发(可选:手动破坏校验值)
    // 注意:这会触发 assert
    // list.listIntegrity1 = 0;
    // list_insertEnd(&list, &node3); // should crash
    

    printf("\n✅ All tests passed!\n");
    return 0;
}
#endif

// gcc -Wall -Wextra -std=c99 -g list.c -o test_list
// ./test_list

1.2 为何 static 不冲突?

1️⃣ 每个编译单元(.c 文件)独立编译
  • a.c 和 b.c 分别编译成 a.o 和 b.o。
  • 在 a.o 中,static int x; 是一个 局部符号(local symbol),绑定到 a.o 的 .bss 段偏移 0。
  • 在 b.o 中,static int x; 也是一个 局部符号,绑定到 b.o 的 .bss 段偏移 0。
  • 它们在各自的 .o 文件中同名,但彼此完全不知道对方的存在
2️⃣ 链接时:局部符号不参与全局符号解析
  • 链接器(如 ld)只关心 全局符号(global symbols) 是否重定义。
  • static 变量是 局部符号(local / internal linkage),链接器:
    • 不会检查它们是否重名;
    • 不会合并它们;
    • 直接把每个 .o 的 .bss 段拼接起来。
  • 结果:可执行文件的 .bss 段 = a.o 的 .bss + b.o 的 .bss + ...

       所以 a.c 中的 x 和 b.c 中的 x 实际上位于 .bss 段中不同的内存地址

3️⃣ 运行时:每个函数访问的是自己编译单元内的 x
  • 编译器在生成代码时,已经为 a.c 中的 x 绑定了一个固定的偏移(比如 .bss + 0)。
  • 同样,b.c 中的 x 绑定的是另一个偏移(比如 .bss + 4,假设 int 占 4 字节)。
  • 运行时,a.c 的函数只会读写它自己的那个地址,根本不会碰到 b.c 的 x

1.3 测试

将 list.c 中的 #if 0 改为 1,之后执行:

gcc -Wall -Wextra -std=c99 -g list.c -o test_list
 ./test_list

其中 Test 9 需要取消注释后再重新编译测试一次。

如图则测试成功:

测试完毕后把 #if 1 改为 0,否则之后的应用会出错。

2. 内存管理(heap)

2.1 为何要自己实现堆管理?

为什么不能使用标准 C 的 malloc/free?

  • 没有堆管理器 :malloc 的实现依赖于操作系统的 sbrk 或 mmap 系统调用。
  • 如果你强行调用 malloc:
    • 链接时可能失败(找不到 malloc 实现)
    • 或者链接了 libc 的 stub(空实现),运行时直接崩溃或返回 NULL

2.2 代码

// heap.h
#ifndef HEAP_H
#define HEAP_H

#include <stddef.h>
#include <stdint.h>

// 配置宏
#define HEAP_TOTAL_SIZE     (16 * 1024)   // 堆总大小:16KB
#define HEAP_ALIGNMENT      8             // 内存对齐:8 字节
#define HEAP_CLEAR_ON_FREE  0             // free 时是否清零内存
#define HEAP_CHECK_VALUE    0xABCDEF99U   // 完整性校验魔数

// 前向声明
typedef struct BlockHead BlockHead;

// ———————— 内部类型定义 ————————

struct BlockHead {
    uint32_t integrity1;     // 完整性校验字段 1
    BlockHead* nxtFree;      // 指向下一个空闲块(仅空闲时有效)
    size_t blockSize;        // 物理块大小(含dummyHead),最高位为分配标志,不是实际可用大小
    uint32_t integrity2;     // 完整性校验字段 2
};

// ———————— 辅助宏 ————————
// 对齐宏:将 x 对齐到最近的 align 的倍数
// C 标准规定:malloc() 返回的指针必须适合任何基本类型的对齐要求。
// 在 64 位系统中,最大基本类型通常是 8 字节(如 double、void*),所以 8 字节对齐是安全且通用的选择
// 且许多 32 位系统的 malloc 实现仍然会返回 8 字节对齐的地址
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))  // 向上对齐
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))                // 向下对齐

// 将数字 1 左移到最高位,使用 size_t 最高位作为已分配标志
#define BLOCK_ALLOCATED_BIT  ((size_t)1 << (sizeof(size_t) * 8 - 1))         // 32 位系统上,BLOCK_ALLOCATED_BIT是 0x80000000
#define IS_ALLOCATED(blk)    (((blk)->blockSize & BLOCK_ALLOCATED_BIT) != 0)
#define MARK_ALLOCATED(blk)  ((blk)->blockSize |= BLOCK_ALLOCATED_BIT)
#define MARK_FREE(blk)       ((blk)->blockSize &= ~BLOCK_ALLOCATED_BIT)
#define GET_USABLE_SIZE(blk) ((blk)->blockSize & ~BLOCK_ALLOCATED_BIT)       // 清除最高位,只保留实际大小(用户可用 + 头部大小)

// 公共 API 声明
void* heap_malloc(size_t size);
void  heap_free(void* ptr);
void* heap_calloc(size_t num, size_t size);
size_t heap_getFreeBytes(void);
size_t heap_getMinFreeBytes(void);
void  heap_reset(void);

#endif // HEAP_H
// heap.c
#include "heap.h"
#include <string.h>
#include <limits.h>
#include <assert.h>

// ———————— 静态全局数据 ————————

// 在 数据段(通常是 .bss 或 .data)中预留一块连续的静态内存,作为自定义堆分配器(如 malloc/free)的底层存储空间。
// 它不是动态分配的(因为堆还没初始化),而是编译时就确定大小的全局数组。
// 所有后续的 malloc 请求,都会从这块 ucHeap 中切分出子块返回,所以不能放在头文件中。
static uint8_t ucHeap[HEAP_TOTAL_SIZE];     // 静态堆内存池

// 只在 heap.c 中使用,故放在 heap.c 中
static BlockHead dummyHead;                 // 起始哨兵节点,不在堆内存池中
static BlockHead* dummyTail = NULL;         // 结束哨兵指针

static size_t FreeBytesRemaining = 0;       // 当前空闲字节数
static size_t MinEverFreeBytes = SIZE_MAX;  // 历史最小空闲字节数

// 对齐后的块头大小
static const size_t blockHeadSize = ALIGN_UP(sizeof(BlockHead), HEAP_ALIGNMENT);


//     |------------------|----------------------|-----------------|
// ucHeap  对齐的偏移    first               dummyTail 尾部对齐偏移  
//     |-----------------------------------------------------------|
//                     HEAP_TOTAL_SIZE

// ———————— 内部辅助函数 ————————

// 只在 heap.c 中使用,故放在 heap.c 中
// 1. inline 只是建议,编译器可以选择是否内联
// 2. C++中 inline 全局变量/函数为外部链接性,且允许重定义
//    static inline 不推荐,函数地址在不同 .cpp 文件中不相等
// 3. C 中 inline 全局变量/函数为外部链接性,但不允许重定义
//    使用static 后为内部链接,每个包含该头文件的 .c 文件都会生成一个本地副本
static inline void setBlockIntegrity(BlockHead* blk) {
    blk->integrity1 = HEAP_CHECK_VALUE;
    blk->integrity2 = HEAP_CHECK_VALUE;
}

static inline void checkBlockIntegrity(const BlockHead* blk) {
    assert(blk != NULL);
    assert(blk->integrity1 == HEAP_CHECK_VALUE);
    assert(blk->integrity2 == HEAP_CHECK_VALUE);
}

static void heap_init(void) {
    // 计算对齐后的堆起始地址
    uintptr_t startAddr = (uintptr_t)ucHeap;         // 获取堆起始地址
    startAddr = ALIGN_UP(startAddr, HEAP_ALIGNMENT); // 对齐起始地址
    size_t offset = startAddr - (uintptr_t)ucHeap;   // 起始地址与堆起始地址的偏移量(对齐损失)
    size_t usableSize = HEAP_TOTAL_SIZE - offset;    // 可用字节数(不含对齐损失)

    // 计算结束块地址(预留一个块头)
    uintptr_t endAddr = startAddr + usableSize;
    endAddr = ALIGN_DOWN(endAddr - blockHeadSize, HEAP_ALIGNMENT);
    dummyTail = (BlockHead*)endAddr;

    // 初始化结束哨兵
    dummyTail->blockSize = 0;
    dummyTail->nxtFree = NULL;
    setBlockIntegrity(dummyTail);

    // 创建初始空闲块:从 startAddr 到 dummyTail
    BlockHead* first = (BlockHead*)startAddr;
    first->blockSize = (uint8_t*)dummyTail - (uint8_t*)first;
    first->nxtFree = dummyTail;
    setBlockIntegrity(first);

    // 初始化起始哨兵
    dummyHead.nxtFree = first;
    dummyHead.blockSize = 0;
    setBlockIntegrity(&dummyHead);

    // 初始化统计信息
    FreeBytesRemaining = first->blockSize;
    MinEverFreeBytes = first->blockSize;
}

// 将一个刚释放的块插入空闲链表,并自动向前/向后合并相邻空闲块
static void insertBlockIntoFreeList(BlockHead* toInsert) {
    checkBlockIntegrity(toInsert);

    // 从起始哨兵开始,按地址升序查找插入位置
    // 遍历链表,找到 iter 使得:iter < toInsert <= iter->nxtFree
    BlockHead* iter = &dummyHead;
    while (iter->nxtFree < toInsert && iter->nxtFree != dummyTail) {
        iter = iter->nxtFree;
    }

    // 向前合并:若前一块紧邻当前块,[ iter 块 ][ toInsert 块 ]  ← 中间没有间隔
    // 如果是,扩展 iter 的大小,并让 toInsert 指向 iter
    if (iter != &dummyHead) {
        uint8_t* prevEnd = (uint8_t*)iter + GET_USABLE_SIZE(iter); // iter 块的结束地址
        if (prevEnd == (uint8_t*)toInsert) {    // 如果中间没有间隔,iter 的结束地址 == toInsert 的起始地址时
            // 把 toInsert 的大小加到 iter->blockSize 上。
            // 之后不再需要 toInsert 这个独立块了,让 toInsert 指针指向 iter,后续操作基于合并后的大块
            iter->blockSize += GET_USABLE_SIZE(toInsert);
            toInsert = iter;
        }
    }

    // 向后合并:若当前块紧邻下一块
    BlockHead* next = iter->nxtFree;
    if (next != dummyTail) {
        uint8_t* thisEnd = (uint8_t*)toInsert + GET_USABLE_SIZE(toInsert); // toInsert 块的结束地址
        if (thisEnd == (uint8_t*)next) {  // 只有当 toInsert 的结束地址 == next 的起始地址时,才能合并
            toInsert->blockSize += GET_USABLE_SIZE(next);
            toInsert->nxtFree = next->nxtFree;
        } else {
            toInsert->nxtFree = next;
        }
    } else {
        toInsert->nxtFree = dummyTail;
    }

    // 更新前驱指针(除非已合并到前一块)
    if (iter != toInsert) {
        iter->nxtFree = toInsert;
    }

    setBlockIntegrity(toInsert);
}

// ———————— 公共 API 定义 ————————

// 分配一块至少 size 字节的内存,返回用户指针(不包含头部)
void* heap_malloc(size_t size) {
    if (size == 0) {
        return NULL;
    }

    // 计算所需总大小(含头部 + 对齐)
    size_t totalSize = size + blockHeadSize;
    if (totalSize < size) { // 溢出检查
        return NULL;
    }
    totalSize = ALIGN_UP(totalSize, HEAP_ALIGNMENT);

    // 检查是否会覆盖分配标志位
    // 如果 totalSize >= BLOCK_ALLOCATED_BIT,那么当我们将它存入 blockSize 并设置分配位时,会覆盖掉实际大小信息,导致后续计算错误
    if (totalSize >= BLOCK_ALLOCATED_BIT) {
        return NULL;
    }

    // 首次调用 malloc 时才初始化堆(lazy initialization)。
    // 避免在程序启动时就占用 CPU 时间。
    if (dummyTail == NULL) {
        heap_init();
    }

    // 遍历空闲链表,寻找足够大的块
    BlockHead* prev = &dummyHead;
    BlockHead* cur = dummyHead.nxtFree;

    while (cur != dummyTail && GET_USABLE_SIZE(cur) < totalSize) {
        checkBlockIntegrity(cur);
        prev = cur;
        cur = cur->nxtFree;
    }

    if (cur == dummyTail) {
        return NULL; // 无足够空间
    }

    // 从空闲链表中移除该块
    // 此时 cur 不再属于链表,即将被分配给用户
    prev->nxtFree = cur->nxtFree;

    // 尝试分割剩余空间
    size_t remaining = GET_USABLE_SIZE(cur) - totalSize;
    // 剩余空间 remaining 必须 ≥ 一个完整新块的最小尺寸
    // 至少能容纳一个 BlockHead(blockHeaderSize), 加上 HEAP_ALIGNMENT 是为了保证新块地址对齐
    if (remaining >= blockHeadSize + HEAP_ALIGNMENT) {
        BlockHead* newBlock = (BlockHead*)((uint8_t*)cur + totalSize);  // 计算新块地址
        newBlock->blockSize = remaining;    // 设置新块大小:remaining
        setBlockIntegrity(newBlock);
        insertBlockIntoFreeList(newBlock);  // 将新块插入空闲链表(会自动合并邻居)
        cur->blockSize = totalSize;         // 缩小原块大小到 totalSize
    }

    // 标记为已分配
    MARK_ALLOCATED(cur);   // 设置 blockSize 最高位为 1
    cur->nxtFree = NULL;
    setBlockIntegrity(cur);

    // 更新统计信息
    FreeBytesRemaining -= GET_USABLE_SIZE(cur);  // 当前空闲字节数
    if (FreeBytesRemaining < MinEverFreeBytes) { // 历史最小值(反映最严重内存压力)
        MinEverFreeBytes = FreeBytesRemaining;
    }

    // 返回用户可用地址(跳过头部)
    return (void*)((uint8_t*)cur + blockHeadSize);
}

// 释放由 malloc 分配的内存
void heap_free(void* ptr) {
    if (ptr == NULL) {
        return;
    }

    BlockHead* block = (BlockHead*)((uint8_t*)ptr - blockHeadSize);
    checkBlockIntegrity(block);
    assert(IS_ALLOCATED(block));
    assert(block->nxtFree == NULL); // 已分配块不应在空闲链表中

    // 标记为未分配
    MARK_FREE(block);
    block->nxtFree = NULL;

#if HEAP_CLEAR_ON_FREE
    memset(ptr, 0, GET_USABLE_SIZE(block) - blockHeadSize);
#endif

    // 更新统计并插入空闲链表
    FreeBytesRemaining += GET_USABLE_SIZE(block);
    insertBlockIntoFreeList(block);
}

// 分配一块能容纳 num 个 size 字节元素的内存,并将所有字节初始化为 0
void* heap_calloc(size_t num, size_t size) {
    if (num != 0 && size > SIZE_MAX / num) {
        return NULL; // 防止乘法溢出
    }
    size_t total = num * size;
    void* ptr = heap_malloc(total);
    if (ptr != NULL) {
        memset(ptr, 0, total);
    }
    return ptr;
}

size_t heap_getFreeBytes(void) {
    return FreeBytesRemaining;
}

size_t heap_getMinFreeBytes(void) {
    return MinEverFreeBytes;
}

void heap_reset(void) {
    dummyTail = NULL;
    FreeBytesRemaining = 0;
    MinEverFreeBytes = SIZE_MAX;
}

#if 0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "heap.h"

void print_stats(const char* msg) {
    printf("%s: Free=%zu, MinFree=%zu\n", msg,
           heap_getFreeBytes(), heap_getMinFreeBytes());
}

int main(void) {
    printf("=== Heap Allocator Test ===\n");

    // 初始状态
    print_stats("Initial");

    // Test 1: malloc(0) -> NULL
    void* p0 = heap_malloc(0);
    assert(p0 == NULL);
    printf("Test 1 passed: malloc(0) returns NULL\n");

    // Test 2: Basic allocation and free
    void* p1 = heap_malloc(64);
    assert(p1 != NULL);
    printf("Allocated 64 bytes at %p\n", p1);
    print_stats("After malloc(64)");

    // Write some data to check integrity later
    memset(p1, 0xAA, 64);

    heap_free(p1);
    print_stats("After free(p1)");

    // Test 3: calloc
    void* p2 = heap_calloc(10, sizeof(int)); // 40 bytes
    assert(p2 != NULL);
    int* arr = (int*)p2;
    for (int i = 0; i < 10; i++) {
        assert(arr[i] == 0);
    }
    printf("calloc zero-initialized correctly\n");
    heap_free(p2);
    print_stats("After free(calloc)");

    // Test 4: Multiple allocations and fragmentation test
    void* a = heap_malloc(100);
    void* b = heap_malloc(200);
    void* c = heap_malloc(50);
    print_stats("After 3 allocations");

    heap_free(b); // free middle block
    print_stats("After freeing middle block");

    void* d = heap_malloc(190); // should reuse b's space (with slight split)
    assert(d != NULL);
    printf("Reallocated ~190 bytes after freeing 200-byte block\n");
    heap_free(a);
    heap_free(c);
    heap_free(d);
    print_stats("After freeing all");

    // Test 5: Alignment check (should be 8-byte aligned)
    size_t sizes[] = {1, 7, 8, 9, 15, 16, 17, 100, 1000};
    for (int i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) {
        void* ptr = heap_malloc(sizes[i]);
        assert(ptr != NULL);
        uintptr_t addr = (uintptr_t)ptr;
        assert((addr % HEAP_ALIGNMENT) == 0);
        heap_free(ptr);
    }
    printf("All allocations are %d-byte aligned\n", HEAP_ALIGNMENT);

    // Test 6: Overflow protection
    void* bad = heap_malloc(SIZE_MAX - 10);
    assert(bad == NULL);
    printf("Overflow protection works\n");

    // Test 7: Reset heap
    heap_reset();
    print_stats("After heap_reset()");

    void* after_reset = heap_malloc(32);
    assert(after_reset != NULL);
    heap_free(after_reset);
    print_stats("After reset + alloc/free");

    printf("\n✅ All tests passed!\n");
    return 0;
}

#endif

// gcc -Wall -Wextra -std=c99 -g -o test_heap heap.c
// ./test_heap

2.3 测试

同上:

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐