Nuttx之mm_extend

声明:此处代码分析,来源与 nuttx 12.8.0版本。

/****************************************************************************
 * Name: mm_extend
 *
 * Description:
 *   Extend a heap region by add a block of (virtually) contiguous memory
 *   to the end of the heap.
 *
 ****************************************************************************/

void mm_extend(FAR struct mm_heap_s *heap, FAR void *mem, size_t size,
               int region)
{
  FAR struct mm_allocnode_s *oldnode;
  FAR struct mm_allocnode_s *newnode;
  uintptr_t blockstart;
  uintptr_t blockend;

  /* Make sure that we were passed valid parameters */

  DEBUGASSERT(heap && mem);
#if CONFIG_MM_REGIONS > 1
  DEBUGASSERT(size >= MIN_EXTEND && region >= 0 &&
              region < heap->mm_nregions);
#else
  DEBUGASSERT(size >= MIN_EXTEND && region == 0);
#endif

  /* Make sure that the memory region are properly aligned */

  blockstart = (uintptr_t)mem;
  blockend   = blockstart + size;

  DEBUGASSERT(MM_ALIGN_UP(blockstart) == blockstart);
  DEBUGASSERT(MM_ALIGN_DOWN(blockend) == blockend);

  /* Take the memory manager mutex */

  DEBUGVERIFY(mm_lock(heap));

  /* Get the terminal node in the old heap.  The block to extend must
   * immediately follow this node.
   */

  oldnode = heap->mm_heapend[region];
  DEBUGASSERT((uintptr_t)oldnode + MM_SIZEOF_ALLOCNODE == blockstart);

  /* The size of the old node now extends to the new terminal node.
   * This is the old size (MM_SIZEOF_ALLOCNODE) plus the size of
   * the block (size) minus the size of the new terminal node
   * (MM_SIZEOF_ALLOCNODE) or simply:
   */

  oldnode->size = size | (oldnode->size & MM_MASK_BIT);

  /* The old node should already be marked as allocated */

  DEBUGASSERT(MM_NODE_IS_ALLOC(oldnode));

  /* Get and initialize the new terminal node in the heap */

  newnode       = (FAR struct mm_allocnode_s *)
                  (blockend - MM_SIZEOF_ALLOCNODE);
  newnode->size = MM_SIZEOF_ALLOCNODE | MM_ALLOC_BIT;

  heap->mm_heapend[region] = newnode;

  /* Finally, increase the total heap size accordingly */

  heap->mm_heapsize += size;
  mm_unlock(heap);

  /* Finally "free" the new block of memory where the old terminal node was
   * located.
   */

  mm_free(heap, mem);
}

显然,此函数用于扩展堆空间。首先对函数的参数进行合理性检验自然属于基本操作。对于size的检验标准为size>=MIN_EXTEND,那么,问题来了,为何这样的size就是合理的呢?首先我们先来看看MIN_EXTEND的定义。

#define MIN_EXTEND (2 * MM_SIZEOF_ALLOCNODE)

可以看的出来,size大小的空间至少能容纳得下两个struct mm_allocnode_s。众所周知,堆是由struct mm_heap_s来进行管理的。mm_heap_s的成员mm_heapend成员用来标记堆空间的结束,这就解释了MIN_EXTEND中的一个struct mm_allocnode_s结构的来源。我们的函数功能是扩展堆空间,那么,扩展的空间必然是空闲的。对堆空间的扩展实质上就是增加struct mm_heap_s 的mm_nodelist的成员。struct mm_freenode_s用于空闲空间的管理。且,struct mm_freenode_s结构是存在于每个空闲空间的开头。这就解释了MIN_EXTEND中的第二个struct mm_allocnode_s结构的来源。有人说了,struct mm_freenode_s与struct mm_allocnode_s相差两个struct mm_freenode_s这指针的大小,确实,按照严格意义来说,应该修改MIN_EXTEND的定义如下。

#define MIN_EXTEND (sizeof(struct mm_freenode_s) + MM_SIZEOF_ALLOCNODE)

接下来的一个问题是,为什么会有如下代码。

  oldnode->size = size | (oldnode->size & MM_MASK_BIT);

我的意思是,oldnode是属于struct mm_allocnode_s这个类型的,根据mm_addregion我们知道,对于新加入堆的空间struct mm_allocnode_s的size成员变量表示大小的部分,其值一般是MM_SIZEOF_ALLOCNODE,如下。

void mm_addregion(FAR struct mm_heap_s *heap, FAR void *heapstart,
                  size_t heapsize)
{
  ......

  heap->mm_heapstart[idx]->size    = MM_SIZEOF_ALLOCNODE | MM_ALLOC_BIT;
  
  ......

  heap->mm_heapend[idx]->size      = MM_SIZEOF_ALLOCNODE | MM_ALLOC_BIT |
                                     MM_PREVFREE_BIT;

  ......
}

对于此处oldnode->size,其原因是,此时的oldnode,在mm_free间接调用mm_malloc_size时,mm_malloc_size做了强制类型转换。这也印证了我们之前说的,struct mm_freenode_s结构是存在于每个空闲空间的开头。

size_t mm_malloc_size(FAR struct mm_heap_s *heap, FAR void *mem)
{
  ......

  node = (FAR struct mm_freenode_s *)((FAR char *)mem - MM_SIZEOF_ALLOCNODE);
  
  ......
}

那么,struct mm_freenode_s的size成员是表示的同oldnode->size = size 相同的意思吗?即 struct mm_freenode_s的size成员表示的是sizeof(struct mm_freenode_s)+用户可用空间大小吗?显然是的。同样,可以从mm_addregion中就能看出来。

void mm_addregion(FAR struct mm_heap_s *heap, FAR void *heapstart,
                  size_t heapsize)
{
  FAR struct mm_freenode_s *node;

  ......

  node                             = (FAR struct mm_freenode_s *)
                                     (heapbase + MM_SIZEOF_ALLOCNODE);
  DEBUGASSERT((((uintptr_t)node + MM_SIZEOF_ALLOCNODE) % MM_ALIGN) == 0);
  node->size                       = heapsize - 2 * MM_SIZEOF_ALLOCNODE;

  ......
}

mm_addregion对struct mm_allocnode_s的size成员的赋值有一定的特殊性,那么,此size表示的是那段空间呢?更一般的来说,此size表示的是sizeof(struct mm_allocnode_s)+用户实际使用空间大小,何以见得呢?我们可以通过函数mm_malloc就能看得出来。

FAR void *mm_malloc(FAR struct mm_heap_s *heap, size_t size)
{
      FAR struct mm_freenode_s *node;

      ......

      alignsize = MM_ALIGN_UP(size + MM_ALLOCNODE_OVERHEAD);

      ......

      node->size = alignsize | (node->size & MM_MASK_BIT);

      ......


      ret = (FAR void *)((FAR char *)node + MM_SIZEOF_ALLOCNODE);

      ......

}

已经博主授权,源码转载自 https://pan.quark.cn/s/a4b39357ea24 QueueForMcu 基于单片机实现的队列功能模块,主要用于8位、16位、32位非运行RTOS的单片机应用,兼容大多数单片机平台。 开源代码:https://.com/xiaoxinpro/QueueForMcu 一、特性 动态创建队列对象 动态设置队列数据缓冲区 静态指定队列元素数据长度 采用值传递的方式保存队列数据 二、快速使用 三、配置说明 目前QueueForMcu只有一个静态配置项,具体如下: 在文件 中有一个宏定义 用于指定队列元素的数据长度,默认是 ,可以根据需要更改为其他数据类型。 四、数据结构 队列的数据结构为 用于保存队列的状态,源码如下: 其中 为配置项中自定义的数据类型。 五、创建队列 1、创建队列缓存 由于我们采用值传递的方式保存队列数据,因此我们在创建队列前要手动创建一个队列缓存区,用于存放队列数据。 以上代码即创建一个大小为 的队列缓存区。 2、创建队列结构 接下来使用 创建队列结构,用于保存队列的状态: 3、初始化队列 准备好队列缓存和队列结构后调用 函数来创建队列,该函数原型如下: 参数说明: 参考代码: 六、压入队列 1、单数据压入 将数据压入队列尾部使用 函数,该函数原型如下: 参数说明: 返回值说明: 该函数会返回一个 枚举数据类型,返回值会根据队列状态返回以下几个值: 参考代码: 2、多数据压入 若需要将多个数据(数组)压入队列可以使用 函数,原理上循环调用 函数来实现的,函数原型如下: 参数说明: 当数组长度大于队列剩余长度时,数组多余的数据将被忽略。 返回值说明: 该函数将返回实际被压入到队列中的数据长度。 当队列中的剩余长度富余...
### NuttX 中 `nuttx_add_application` 的用法 在 NuttX 操作系统中,`nuttx_add_application` 是一个非常重要的函数,它允许开发者注册自定义应用程序到系统的初始化流程中。此功能通常用于将特定的任务或服务集成到 NuttX 启动过程中。 以下是关于该函数的一些关键点以及如何使用的示例: #### 函数原型 `void nuttx_add_application(struct app_init_s *app);` - 参数说明: - `struct app_init_s *app`: 这是一个指向结构体的指针,其中包含了应用的相关配置信息。 #### 结构体定义 `struct app_init_s` 的定义如下[^3]: ```c struct app_init_s { FAR const char *name; /* 应用程序名称 */ main_t entry; /* 主入口函数 (int (*)(int argc, char *argv[])) */ }; ``` - 字段解释: - `FAR const char *name`: 表示应用程序的名字,主要用于调试和日志打印。 - `main_t entry`: 这是应用程序的主要入口函数,其签名应与标准 C 程序的 `main()` 类似,即接受命令行参数并返回整数值。 #### 使用示例 以下是如何使用 `nuttx_add_application` 注册一个简单应用程序的例子: ```c #include <stdio.h> #include <apps/init_app.h> /* 自定义应用程序的主函数 */ static int my_custom_app(int argc, char *argv[]) { printf("My Custom Application is running!\n"); return 0; } /* 初始化结构体 */ static struct app_init_s my_app = { .name = "my_custom_app", .entry = my_custom_app, }; /* 调用 nuttx_add_application 来注册应用程序 */ void register_my_custom_app(void) { nuttx_add_application(&my_app); } ``` 在此代码片段中,我们创建了一个名为 `my_custom_app` 的新应用程序,并通过调用 `register_my_custom_app` 将其添加到 NuttX 的启动序列中[^4]。 #### 集成到 NuttX 构建系统 为了使上述代码生效,还需要将其适配至 NuttX 的构建环境中。具体操作包括但不限于修改 Makefile 或 Kconfig 文件以确保编译器能够找到源文件并将目标链接入最终镜像。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值