嵌入式专栏 发表于 2025-3-31 11:45:00

嵌入式中高效管理调度和动态内存

关注+星标公众号,不错过精彩内容
转自| 嵌入式情报局

搞嵌入式开发,内存管理比较重要,我们遇到的bug,很多时候都有因为内存导致的。今天就跟大家聊聊这红黑树到底干啥的~
首先两个典型的问题:
实时任务调度:如何从百万级定时器中快速找到最先超时的事件?
内存分配:如何在海量碎片中高效搜索≥10KB的可用内存块?
红黑树正是解决这些问题的好办法:
? 通常使用链表管理1000个定时器?插入操作可能从1ms暴增到100ms!
? 用数组实现内存分配器?查找空闲块的时间会随着碎片增加直线上升!
时间复杂度如下数据结构插入删除查找链表/数组O(n)O(1)~O(n)O(n)红黑树O(log n)O(log n)O(log n)红黑树能够保证所有操作在 ( O(log n) ) 内完成,一、案例1:时间戳搜索(实时任务调度)1. 场景需求? 问题:实时系统需管理数千个定时器,普通链表或数组的插入/删除效率无法满足实时性要求。
? 目标:
? 插入新定时器(如50ms后触发的任务) → O(log n)
? 查找最早超时的任务 → O(1)(直接取最左节点)
? 删除已触发的定时器 → O(log n)
2. 红黑树实现方案键值设计:
? 用32位时间戳(如uint32_t timeout = systick + 50)表示超时时间。
? 时间戳溢出处理:(伪代码,只为说明应用)
// 判断时间戳a是否早于b(考虑32位溢出)
bool is_earlier(uint32_t a, uint32_t b) {
    return (int32_t)(a - b) 0; // 利用有符号数溢出特性
}
数据结构:
typedef struct TimerEvent {
    uint32_t timeout;// 超时时间戳
    void (*callback)(void); // 回调函数指针
} TimerEvent;
typedef struct RBNode {
    uint32_t key;      // 存储timeout
    TimerEvent event;
    Color color;
    struct RBNode *left, *right, *parent;
} RBNode;
3. 核心操作代码插入定时器:
void timer_insert(RBNode **root, uint32_t timeout, TimerEvent *evt) {
    RBNode *z = alloc_node(timeout); // 从预分配内存池申请节点
    z->event = *evt;
   
    // 标准BST插入逻辑
    RBNode *y = NULL;
    RBNode *x = *root;
    while (x) {
      y = x;
      x = (is_earlier(timeout, x->key)) ? x->left : x->right;
    }
    z->parent = y;
    if (!y)
      *root = z;
    elseif (is_earlier(timeout, y->key))
      y->left = z;
    else
      y->right = z;
   
    fix_insert(root, z); // 红黑树平衡修复
}
查找最早超时事件:
// 直接取最左节点(最小值)
RBNode* find_earliest_timeout(RBNode *root) {
    if (!root) return NULL;
    while (root->left) root = root->left;
    return root;
}
删除定时器:
// 先调用bst_delete删除节点,再修复红黑树
void timer_delete(RBNode **root, RBNode *z) {
    RBNode *y = z;
    Color y_original_color = y->color;
    RBNode *x = NULL;
   
    if (!z->left) {
      x = z->right;
      transplant(root, z, z->right);
    } elseif (!z->right) {
      x = z->left;
      transplant(root, z, z->left);
    } else {
      y = minimum(z->right); // 找后继节点
      y_original_color = y->color;
      x = y->right;
      if (y->parent == z)
            x->parent = y;
      else {
            transplant(root, y, y->right);
            y->right = z->right;
            y->right->parent = y;
      }
      transplant(root, z, y);
      y->left = z->left;
      y->left->parent = y;
      y->color = z->color;
    }
   
    if (y_original_color == BLACK)
      fix_delete(root, x); // 红黑树删除修复
}

二、案例2:内存块大小搜索(动态内存分配)1. 场景需求? 问题:动态内存分配需快速查找≥请求大小的最小空闲块,首次适应算法效率低下。
? 目标:
? 插入释放的内存块 → O(log n)
? 查找合适块 → O(log n)
? 分割块后插入剩余空间 → O(log n)
2. 红黑树实现方案键值设计:
? 用内存块大小(uint32_t block_size)作为键值,树节点按块大小升序排列。
数据结构:
typedef struct MemBlock {
    uint32_t start_addr; // 内存块起始地址
    uint32_t size;       // 块大小
} MemBlock;
typedef struct RBNode {
    uint32_t key;      // 存储block_size
    MemBlock block;
    Color color;
    struct RBNode *left, *right, *parent;
} RBNode;
3. 核心操作代码查找≥size的最小内存块:
RBNode* find_suitable_block(RBNode *root, uint32_t size) {
    RBNode *current = root;
    RBNode *result = NULL;
   
    while (current) {
      if (current->key >= size) {
            result = current;       // 记录候选块
            current = current->left; // 继续向左找更小的块
      } else {
            current = current->right; // 当前块太小,向右找更大的
      }
    }
    return result; // 返回NULL或合适块
}
内存分配逻辑:
MemBlock* mem_alloc(RBNode **root, uint32_t size) {
    RBNode *node = find_suitable_block(*root, size);
    if (!node) returnNULL; // 内存不足
   
    MemBlock *alloc_block = &node->block;
   
    // 分割块(例如:找到12KB的块,申请10KB)
    if (alloc_block->size > size) {
      MemBlock remain = {
            .start_addr = alloc_block->start_addr + size,
            .size = alloc_block->size - size
      };
      mem_insert(root, remain.size, &remain); // 插入剩余块
    }
   
    mem_delete(root, node); // 从树中删除原节点
    return alloc_block;
}
插入释放的内存块:
void mem_insert(RBNode **root, uint32_t size, MemBlock *blk) {
    RBNode *z = alloc_node(size);
    z->block = *blk;
   
    // 标准BST插入
    RBNode *y = NULL;
    RBNode *x = *root;
    while (x) {
      y = x;
      x = (size key) ? x->left : x->right;
    }
    z->parent = y;
    if (!y)
      *root = z;
    elseif (size key)
      y->left = z;
    else
      y->right = z;
   
    fix_insert(root, z); // 红黑树平衡修复
}
所以红黑树的本质:通过颜色规则和旋转策略,在动态操作中维持近似平衡,以极低开销(每个节点仅1bit颜色标记)实现高效搜索。
不过传统的链表数组也有他们的优势,比如:
极小型数据(节点数 只追加不删除:数组的尾插操作 ( O(1) ) 更快严格顺序访问:如循环缓冲区,数组效率更高


如何更换JLink的Flashloader

MCU触摸按键噪声处理方法

AI设计PCB靠谱吗?
页: [1]
查看完整版本: 嵌入式中高效管理调度和动态内存