10-1RT-Thread动态内存管理
在嵌入式系统中,变量和中间数据一般存放在系统存储空间中。只有在实际使用时,才将它们从存储空间读取到CPU进行运算。存储空间可分为两种,内部存储空间rem和外部存储空间rome或flash。其中ram或称之为内存,访问速度比较快,可以按照变量地址随机访问,但断电后数据会丢失。而room或flash的内容比较固定,主要用来保存程序本身的数据内容,保存的内容断电后不会丢失。
对于嵌入式的内存又有堆和栈之分栈不需要程序员手动去申请。函数的局部变量入参都占用空间,当函数执行结束时,占空间会自动释放,而堆需要程序员手动申请和释放。
在RT thread中有两种堆内存管理方式,动态内存堆管理和静态内存池管理。
1.一般内存管理算法是根据需要存储数据的长度,在内存中寻找一个与这段数据相适应的空闲内存块,然后存储数据。而寻找空闲内存块耗费的时间是不确定的这对实时操作系统来说是不可接受的。因此实时操作系统必须保证内存块的分配过程是在可预测的确定时间内完成。
2.随着内存不断被分配和释放,整个内存区域会产生越来越多的碎片。系统中虽有足够的空间块,但他们的地址并非连续,导致无法申请到大的内存。对于通用系统而言,可以通过重启系统解决,但对于嵌入系统来说可能无法接受。
3.嵌入式系统的资源不同,内存在KB到MB之间,如何为系统选择高效率的内存分配算法比较复杂。
在RT thread中将ZI段结尾处到内存尾部的空间作为内存堆,主要用于系统动态分配内存的场合。比如我们使用动态方式创建某些内核对象,如消息队列、邮箱、信号量等的时候,所使用的内存空间就是动态内存堆。
动态内存管理是在内存资源充足的情况下,根据用户的需求,从系统配置的一块比较大的连续内存中分配任意大小的内存块,当用户不需要该内存时,又可以释放供系统再进行统一的管理。其优点是按需分配,在设备中使用灵活。缺点是内存容易碎片化。
RT thread 关于动态内存堆的管理主要有三种算法
我们只能通过RT thread settings来配置系统内核,选择其中一种内存管理方法。对于用户而言,这三种算法是透明的,也就是说提供给用户的内存管理接口是相同的,只是算法的实现原理不同。
当需要分配内存时,将从这个大的内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还给堆管理系统。
每个内存块不管是已分配的内存块还是空闲的内存块,都包含一个数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来。
Magic不仅仅用于标识这个数据块,是一个内存管理用的内存数据块,实质也是一个内存保护字,如果这个区域被改写,那么也就意味着这个内存块被非法改写,在正常情况下,只有内存管理器才会去碰这块内存
那么小内存管理是如何进行内存分配的呢?
我们假设此时空闲链表指针初始指向32字节的内存块,当用户线程要分配一个64字节的内存块时,由于当前指针内存块只有32个字节,并不满足要求,内存管理器会继续寻找下一个内存块。在此例中,当找到128字节时,它是满足分配要求的。但因为这个内存块比实际需求大,分配器会把内存块进行拆分,剩余的内存块继续留在列表中。
每次分配内存块前都会留出12个字节的数据头,用于magic use的信息及链表节点使用。返回给应用的地址实际上是这块内存块12字节以后的地址,而数据头部分是用户永远不应该改变的部分。
Slab分配器会根据对象的大小分成多个区,也可以看成每类对象有一个内存池,一个zone的大小在32K到128K字节之间。分配器会在堆初始化的时候,根据堆的大小自动调整系统中的作用。最多包含72种对象,一次最大能够分配16K的内存空间。如果超出16K那么直接从一页分配器中分配。
每个作用上分配的内存块大小是固定的,能够分配相同大小内存块的作用会链接在一个链表中,而72种对象的zoo列表则放在一个数组zone arry中统一管理flag管理算法主要有内存分配和内存释放两个过程。
对于内存分配,我们假设分配一个32字节的内存slab,内存分配器会先按照32个字节的值,从zoo的arry链表表头数组中找到相应的zone链表。如果这个链表是空的,则向液分配器分配一个新的rom,然后从rom中返回第一个空闲块。如果链表非空,则这个作用链表中第一个zone节点必然有空闲块存在,那么就取相应的空闲块。如果分配完成后,zone中所有的空闲内存块都使用完毕,那么分配器需要把这个zone节点从列表中删除,而内存的释放是分配器需要找到内存块中所在的zone节点,然后把内存块链接到zone的空闲内存块链表中。如果此时zone的空闲列表指示出zone的所有内存块都已释放,即zone是完全空闲的,那么当作文列表中全空闲作用达到一定数目后,系统就会把这个全空闲的作用释放到页面分配器
memheap管理算法适用于系统中包含多个地址不连续的内存堆,使用memheap内存管理可以简化系统存在多个内存堆时的使用。当系统中存在多个内存堆的时候,用户只需要在系统初始化的时候,将多个所需的memheap初始化,并开启memheap功能,就可以很方便的把多个memheap粘合起来,用于系统的的P分配。memheap工作机制如图所示,首先将多块内存加入memheap_item链表进行粘合。当分配内存时会先从默认内存堆中分配内存,当分配不到时会查找memheap_item列表,尝试从其他的内存堆上分配内存块。应用程序不用关心当前分配的内存块位于哪个内存堆上,就像是操作一个内存堆一样。
内存堆的初始化函数是在系统启动的时候自动初始化的,而不是我们用户在main函数中再初始化。
我们可以使用RT free进行内存块的释放。同时在内存块的分配和内存块的释放时还设置了相应的钩子函数。