内存管理的背景知识
数据对齐
内存管理提到了对数据对齐的概念。简单介绍一下,STM32F4是32位CPU,因而可以得知它的数据总线是32位的也就是说一次可以读取4个字节的内存数据。这就有一个问题了,每一次读取是从哪里开始读4个字节呢?硬件的底层明确了是从4的倍数处读取4个字节。
如果我要读取一个字节的数据,就会一次性读取它所在的地址为x至x+3这4个字节(x是4的倍数,如0,4,8…)。然后剔除不要的部分得到我想要的数据。
如果我要读取两个字节的数据呢?问题来了,假如我正好一个字节在x至x+3,一个字节在x+4至x+7,这就意味着硬件要进行2次读取操作,以及剔除、拼接操作,这些都是很费时的;内存对齐简单理解,就是数据的地址会放在4的倍数处,这样对于2个字节或者4个字节的数据都只需要一次读取就可以完成。,大大提高了效率。
ucos的内存管理要求在申请一段空间并将其初始化为内存分区时要保证数据对齐,也就是说你的数组首地址一定是4的倍数。
内存块大小
另外就是对内存块的大小的要求。对于32位的CPU,它支持的地址空间为4GB,原因就是支持32位的寻址操作,也就意味着指针类型void*的默认类型等价于32位无符号整型。内存块在没有使用的时候会和ucos的消息池比较相似,将未使用的内存块链接成单链表,这样方便获取和回收。
因此内存块在未使用时,它的空间一定是能够放下一个指向下一个内存块的指针,也就是4个字节。综合数据对齐的概念,内存块的大小不得小于指针大小并且应当是指针大小的整数倍。
疑问点
主要疑问就是下面这一段将内存块串成单链表的过程。以下内容均是基于STM32F4系列芯片。
p_link = (void **)p_addr;
p_blk = p_addr;
loops = n_blks - 1u;
p_blk = (INT8U *)p_addr + blksize;
for (i = 0; i < loops; i++) {
p_blk += blk_size;
*p_link = (void *)p_blk;
p_link = (void **)(void *)p_blk;
}
*p_link = (void *)0;
变量含义
假设自己创建的内存分区的数组为:uint8_t temp_Buf[a][b];
p_addr:内存分区的首地址,也就是事先声明的二维数组的首地址,调用时也可以看到传递的实参是数组名temp_Buf。
n_blks:内存分区中内存块的个数,也就是a。
blk_size:每一个内存块的大小,也就是b。
分析
p_addr作为参数是一个无符号指针类型,它指向的数据类型不确定,但是这个指针变量本身一定是32位的。
p_blk是声明称了CPU_INT08U类型的指针,并且在代码第2行将p_addr赋值给了p_blk。这意味着什么?意味着p_blk最初指向的是第一个内存块的首地址,并且p_blk++后,它指向的地址只会加1。为什么要这么声明呢?当然是为了代码第6行,我可以通过 p_blk += blk_size;准确的找到下一个内存块的首地址。因为我是要将内存块串连成一个单链表的,所以肯定要提取出内存块的首地址并且放到前一个内存块中。这就是下面要分析的:
我先明确的说明一下:回想一下消息池,指向下一个消息的指针是放在一个nextPtr结构体成员中的。而ucos中的内存块它只是一个个一维数组,那下一个内存块的地址放在哪里呢?**答案是放在前一个内存块的首地址开始的4个字节。**这话可能不好理解,实际上的意思就是,下一个内存块的4字节的地址信息放在当前内存块的数组中,索引0到3这四个数组成员中。
作为类型为void的p_addr来说,它的值是32位的地址,的确是指向了内存块的首地址,但是指向首地址后的几个字节?这个是void无法指出的,如果是CPU_INT08U *就能够知道是指向某一个地址所在的一个字节的数据。由前面知道,已经通过 p_blk += blk_size提取到了下一个内存块的地址,它是一个32位的数据,我怎么放到前一个数组中呢?先类比一下,如果我想把一个CPU_INT08U类型的数据写入某一个地址中去,那肯定是通过一个指向CPU_INT08U的指针来实现赋值。那写入一个4字节的地址数据呢?那当然是通过一个指向指针类型的指针来实现赋值。
所以代码第1行的p_link = (void **)p_addr的意思是,将p_link赋值为一个4字节的地址数据的指针,那这样就可以通过*p_link实现地址数据的读取和写入了。所以就有了代码第7行的 *p_link = (void *)p_blk;。成功的将后一个内存块的地址放入了前一个内存块数组中。
剩余的代码就是用于循环了,没什么大的难度。原理都是一样的。
End
指针,指向指针的指针…其实也可以换个角度看,void它和CPU_INT08U都是数据类型的一种。所以void**可以看成是指向void的指针。
本文介绍了uCOS-III内存管理中关于数据对齐和内存块大小的要求。在内存管理中,数据对齐对于提高效率至关重要,而内存块大小至少为指针大小并为指针大小的整数倍。文章还探讨了如何通过单链表将内存块串联起来,特别是如何将下一个内存块的地址存储在前一个内存块的首4个字节中,以及如何利用指针和指向指针的指针来实现这一过程。

912

被折叠的 条评论
为什么被折叠?



