rt-thread内核补充

本文详细介绍了RT-thread中的内存管理算法,如slab和memheap,以及内存分配和释放过程。同时涵盖了内存池的控制块、内存块分配机制和应用实例。此外,文章还深入讨论了中断管理,包括中断处理流程、中断嵌套、中断栈和底半处理,以及中断的挂载、屏蔽和通知等操作。
摘要由CSDN通过智能技术生成

内存管理

小内存管理算法

步骤:

  1. 在空闲链表头寻找空闲内存块
  2. 满足需求空闲内存块做分割
    • 满足需求指内存块大于需求12字节
    • 其中的12个字节用于存储链表的节点信息
  3. 将分割后剩余的区域放回空闲链表

slab管理算法

slab管理算法

内存分配

假设分配一个 32 字节的内存,slab 内存分配器会先按照 32 字节的值,从 zone array 链表表头数组中找到相应的 zone 链表。如果这个链表是空的,则向页分配器分配一个新的 zone,然后从 zone 中返回第一个空闲内存块。如果链表非空,则这个 zone 链表中的第一个 zone 节点必然有空闲块存在,那么就取相应的空闲块。如果分配完成后,zone 中所有空闲内存块都使用完毕,那么分配器需要把这个 zone 节点从链表中删除。

内存释放

空闲的区(zone)会被释放

memheap管理算法(RT-thread主要)

memheap管理算法

内存堆管理

示例程序

#include <rtthread.h>

#define THREAD_PRIORITY      25
#define THREAD_STACK_SIZE    512
#define THREAD_TIMESLICE     5

/* 线程入口 */
void thread1_entry(void *parameter)
{
    int i;
    char *ptr = RT_NULL; /* 内存块的指针 */

    for (i = 0; ; i++)
    {
        /* 每次分配 (1 << i) 大小字节数的内存空间 */
        // 保证了每次申请的内存空间呈指数增加
        ptr = rt_malloc(1 << i);

        /* 如果分配成功 */
        if (ptr != RT_NULL)
        {
            rt_kprintf("get memory :%d byte\n", (1 << i));
            /* 释放内存块 */
            rt_free(ptr);
            rt_kprintf("free memory :%d byte\n", (1 << i));
            ptr = RT_NULL;
        }
        else
        {
            rt_kprintf("try to get %d byte memory failed!\n", (1 << i));
            return;
        }
    }
}

int dynmem_sample(void)
{
    rt_thread_t tid = RT_NULL;

    /* 创建线程 1 */
    tid = rt_thread_create("thread1",
                           thread1_entry, RT_NULL,
                           THREAD_STACK_SIZE,
                           THREAD_PRIORITY,
                           THREAD_TIMESLICE);
    if (tid != RT_NULL)
        rt_thread_startup(tid);

    return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(dynmem_sample, dynmem sample);

对内存块的相关操作

分配内存块

从内存堆上分配用户指定大小的内存块

void *rt_malloc(rt_size_t nbytes);

注意:参数为二进制数据

释放内存块

必要性:应用程序使用完从内存分配器中申请的内存后,必须及时释放,否则会造成内存泄漏

void rt_free (void *ptr);

补充:官方文档

上面官方文档中定义了一些常见的内存管理错误

内存池管理

内存堆管理的缺点:每次分配内存都需要在空闲内存块中寻找,分配完内存容易出现内存碎片(许多小的内存被浪费)。

内存池管理

内存池控制块
struct rt_mempool
{
    struct rt_object parent;

    void        *start_address;  /* 内存池数据区域开始地址 */
    rt_size_t     size;           /* 内存池数据区域大小 */

    rt_size_t     block_size;    /* 内存块大小  */
    rt_uint8_t    *block_list;   /* 内存块列表  */

    /* 内存池数据区域中能够容纳的最大内存块数  */
    rt_size_t     block_total_count;
    /* 内存池中空闲的内存块数  */
    rt_size_t     block_free_count;
    /* 因为内存块不可用而挂起的线程列表 */
    rt_list_t     suspend_thread;
    /* 因为内存块不可用而挂起的线程数 */
    rt_size_t     suspend_thread_count;
};
typedef struct rt_mempool* rt_mp_t;
内存块分配机制

内存池创建的时候先向系统申请一个大的空间,然后将内存块分成相同大小的小内存块。内存控制块的参数包括内存池名,内存缓冲区,内存块大小,块数以及一个等待线程队列

内存块分配机制

内存池应用实例
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-04-11     霄寒       the first version
 */
#include <rtthread.h>

static rt_uint8_t *ptr[50];
static rt_uint8_t mempool[4096];
static struct rt_mempool mp;

#define THREAD_PRIORITY      25
#define THREAD_STACK_SIZE    512
#define THREAD_TIMESLICE     5

/* 指向线程控制块的指针 */
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;

/* 线程 1 入口 */
static void thread1_mp_alloc(void *parameter)
{
    int i;
    for (i = 0 ; i < 50 ; i++)
    {
        if (ptr[i] == RT_NULL)
        {
            /* 试图申请内存块 50 次,当申请不到内存块时,
               线程 1 挂起,转至线程 2 运行 */
            ptr[i] = rt_mp_alloc(&mp, RT_WAITING_FOREVER);
            if (ptr[i] != RT_NULL)
                rt_kprintf("allocate No.%d\n", i);
        }
    }
}

/* 线程 2 入口,线程 2 的优先级比线程 1 低,应该线程 1 先获得执行。*/
static void thread2_mp_release(void *parameter)
{
    int i;

    rt_kprintf("thread2 try to release block\n");
    for (i = 0; i < 50 ; i++)
    {
        /* 释放所有分配成功的内存块 */
        if (ptr[i] != RT_NULL)
        {
            rt_kprintf("release block %d\n", i);
            rt_mp_free(ptr[i]);
            ptr[i] = RT_NULL;
        }
    }
}

int mempool_sample(void)
{
    int i;
    for (i = 0; i < 50; i ++) ptr[i] = RT_NULL;

    /* 初始化内存池对象 */
    rt_mp_init(&mp, "mp1", &mempool[0], sizeof(mempool), 80); // 此处申请的内存块数量为 (mempool == 4096 ) / (80 + 4) == 48 块
                                                              // 所以程序申请到 47 号(i从0起)时申请不到,开始释放

    /* 创建线程 1:申请内存池 */
    tid1 = rt_thread_create("thread1", thread1_mp_alloc, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid1 != RT_NULL)
        rt_thread_startup(tid1);


    /* 创建线程 2:释放内存池 */
    tid2 = rt_thread_create("thread2", thread2_mp_release, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY + 1, THREAD_TIMESLICE);
    if (tid2 != RT_NULL)
        rt_thread_startup(tid2);

    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mempool_sample, mempool sample);

运行过程:

本例程在初始化内存池对象时,初始化了 4096 /(80+4) = 48 个内存块。

①线程 1 申请了 48 个内存块之后,此时内存块已经被用完,需要其他地方释放才能再次申请;但此时,线程 1 以一直等待的方式又申请了 1 个,由于无法分配,所以线程 1 挂起;

②线程 2 开始执行释放内存的操作;当线程 2 释放一个内存块的时候,就有一个内存块空闲出来,唤醒线程 1 申请内存,申请成功后再申请,线程 1 又挂起,再循环一次②;

③线程 2 继续释放剩余的内存块,释放完毕。

中断管理

中断处理过程

RT-Thread 中断管理中,将中断处理程序分为中断前导程序、用户中断服务程序、中断后续程序三部分,如下图

注:即为保存现场、中断处理函数、回复现场

中断前导程序
void rt_interrupt_enter(void)
{
    rt_base_t level;

    level = rt_hw_interrupt_disable();
    rt_interrupt_nest ++;
    rt_hw_interrupt_enable(level);
}
用户中断服务程序

一般情况:运行中断处理函数、中断后续程序 – 》退出

特殊情况:需要进行线程切换

中断后续程序
void rt_interrupt_leave(void)
{
    rt_base_t level;

    level = rt_hw_interrupt_disable();
    rt_interrupt_nest --;
    rt_hw_interrupt_enable(level);
}

进行线程切换的contex-M架构

中断嵌套

译为:中断的中断,内部中断要优先处理

中断栈

中断的栈空间,一般是位于线程的栈空间中,但是也可以独立开辟。嵌套中断的栈空间如果没有独立开辟,就在被嵌套的栈空间里开辟。

中断底半处理

在上半部分中,取得硬件状态和数据后,打开被屏蔽的中断,给相关线程发送一条通知,然后结束中断服务程序。

而接下来,相关的线程在接收到通知,接着对状态或数据进行进一步的处理

由于以上两个部分的处理是异步过程,所以需要用户考虑两个过程系统的处理时间,从而选择是否使用底半处理的方式。

中断的一些操作
中断的挂载
rt_isr_handler_t rt_hw_interrupt_install(int vector,
                                        rt_isr_handler_t  handler,
                                        void *param,
                                        char *name);
参数描述
vectorvector 是挂载的中断号
handler新挂载的中断服务程序
paramparam 会作为参数传递给中断服务程序
name中断的名称
返回——
return挂载这个中断服务程序之前挂载的中断服务程序的句柄
屏蔽中断源
void rt_hw_interrupt_mask(int vector);
参数描述
vector要屏蔽的中断号
打开屏蔽中断源
void rt_hw_interrupt_umask(int vector);
参数描述
vector要打开屏蔽的中断号
全局中断开关
rt_base_t rt_hw_interrupt_disable(void);// 关
void rt_hw_interrupt_enable(rt_base_t level); // 开
中断通知
void rt_interrupt_enter(void); // 通知内核已经进入了中断状态
void rt_interrupt_leave(void); // 通知内核已经离开了中断状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值