type
status
slug
date
summary
tags
category
password
icon
概述
µC/OS-II provides an alternative to
malloc()
and free()
by allowing your application to obtain fixed-sized memory blocks from a partition made of a contiguous memory area, as illustrated in Figure 12.1. All memory blocks are the same size and the partition contains an integral number of blocks. Allocation and deallocation of these memory blocks is done in constant time and is deterministic(确定的).As shown in Figure 12.2, more than one memory partition can exist, so your application can obtain memory blocks of different sizes. However, a specific memory block must be returned to the partition from which it came. This type of memory management is not subject to fragmentation.
同样的,肯定需要对这些partition有管理,对应着一个结构体:
OS_MEM结构体
Memory Control Blocks
µC/OS-II keeps track of memory partitions through the use of a data structure called a memory control block (Listing 12.1). Each memory partition requires its own memory control block.
.OSMemAddr
is a pointer to the beginning (base) of the memory partition from which memory blocks will be allocated. This field is initialized when you create a partition [see section 12.01, Creating a Partition,
OSMemCreate()
] and is not used thereafter..OSMemFreeList
is a pointer used by µC/OS-II to point to either the next free memory control block or to the next free memory block. The use depends on whether the memory partition has been created or not (see section 12.01).
.OSMemBlkSize
determines the size of each memory block in the partition and is a parameter you specify when the memory partition is created (see section 12.01).
.OSMemNBlks
establishes the total number of memory blocks available from the partition. This parameter is specified when the partition is created (see section 12.01).
.OSMemNFree
is used to determine how many memory blocks are available from the partition.
µC/OS-II initializes the memory manager if you configure OS_MEM_EN to 1 in
OS_CFG.H
. Initialization is done by OS_MemInit()
[called by OSInit()
] and consists of creating a linked list of memory control blocks, as shown in Figure 12.3. You specify the maximum number of memory partitions with the configuration constant OS_MAX_MEM_PART (see OS_CFG.H
), which must be set at least to 2.As you can see, the OSMemFreeList field of the control block is used to chain the free control blocks.
既然提到了
OS_MemInit
这个函数那就得讲讲:OS_MemInit
函数,筛选版
实际就是完成了空闲链表的创建,具象化为这张图(show again)👇
OSMemTbl和OSMemFreeList的定义如下,这个MemTbl就是定义有几个partition的
ok,可以来create了:
Creating a Partition, OSMemCreate()
Your application must create each partition before it can be used and is this done by calling
OSMemCreate()
. Listing 12.2 shows how you could create a memory partition containing 100 blocks of 32 bytes each. Some processors like to have memory aligned on either 16 or 32-bit boundaries. To accommodate these processors, you could declare the memory partitions as:INT16U CommTxPart[100][16];
or,
INT32U CommTxPart[100][8];
The code to create a memory partition is shown below.
OSMemCreate()
requires four arguments: the beginning address of the memory partition, the number of blocks to be allocated from this partition, the size (in bytes) of each block, and a pointer to a variable that contains an error code. OSMemCreate()
returns a NULL pointer if OSMemCreate()
fails. On success, OSMemCreate()
returns a pointer to the allocated memory control block. This pointer must be used in subsequent calls to memory management services [see OSMemGet()
, OSMemPut()
, and OSMemQuery()
in the following part].
OSMemCreate代码筛选版
addr参数是the beginning address of the memory partition
nblks参数是the number of blocks to be allocated from this partition
blksize参数是the size (in bytes) of each block
举个例子:
要很好地理解这个部分,对于c里面,栈分配的多维数组的理解需要深入一些:
栈结构的比如 int8u A[3][2]就是分配一个连续的6字节空间,而A是指针的指针,*A指向空间的首地址,**A指向第一个元素
上面说的不太准确,我们直接跑代码看看:
二维数组的类型很奇怪,它是INT8U (*)[32]
这里给个解释的帖子:
If a memory control block is available and all the previous conditions are satisfied, the memory blocks within the partition are linked together in a singly linked list. A singly linked list is used because insertion and removal of elements in the list is always done from the head of the list.
这样看下来,我觉得这个真可以改,对于plink的类型:
不过原来的可读性更好,因为plink的定义就是它存储的是地址,是指向下一个block的地址,所以最好把它写成指针的指针。
Obtaining a Memory Block, OSMemGet()
Your application can get a memory block from one of the created memory partitions by calling
OSMemGet()
. You must use the pointer returned by OSMemCreate()
in the call to OSMemGet()
to specify which partition the memory block will come from. Obviously, your application needs to know how big the memory block obtained is so that it doesn’t exceed its storage capacity. In other words, you must not use more memory than is available from the memory block. For example, if a partition contains 32-byte blocks, then your application can use up to 32 bytes. When you are done using the block, you must return it to the proper memory partition [see Returning a Memory Block, OSMemPut()
].OSMemGet代码筛选版
核心代码处的解释:
更改OSMemFreeList的指向为当前的FreeList指向的第一个元素,然后减少剩下的空余Mem数量,这样就把block分配出去了
看图来有个具象化认识:
来自官方tutorial的一些trick:
(1) The pointer passed to
OSMemGet()
specifies the partition from which you want to get a memory block.
(7) The pointer to the allocated block is finally returned to your application.这里就能填前面关于OS_MEM结构体里面个别元素的含义的坑了:
.OSMemFreeList
is a pointer used by µC/OS-II to point to either the next free memory control block or to the next free memory block. The use depends on whether the memory partition has been created or not (see section 12.01).
在Memory partition控制块没有被分配用来管理一个具体的partition的时候,OSMemFreelist就是用来连接各个控制块的指针,让他们连成如下图所示的链表:
而当他被分配用于管理一个具体的partition的时候,这个指针用于指向空闲的block链表的首地址,而这个空闲block链表接下来怎么连成一片呢?简单,还记得为什么要在partition建立的时候进行这几个检测吗?:
这里我按照自己的顺序解释:
你首先要保证你的block是能放下一个指针的,因为block的头部是用于存放指针的,比如你32字节的block,那么在空闲block链表里面放着的时候,前4字节用于存放了链表的指针。而因为空闲block的链接完全是按照他们起始的地址所存放的链表指针来连接的,我不能确定这样做有什么必要性,但是保证让整个空闲链表的首地址按照地址指针的大小进行对齐感觉起来很有道理,这里埋个问题,以后解决。最后是对于block数量的要求,要求每个分区至少得有2个block,只有一个的话都没法搞一个链表,不过更根本的原因应该是只有一个blcok没有啥意义,这个分区就是这个block的大小,分配了这个block这个分区也就不会再分配了,没有啥用。
Returning a Memory Block, OSMemPut()
When your application is done with a memory block, it must be returned to the appropriate partition. This is accomplished by calling
OSMemPut()
. You should note that OSMemPut()
has no way of knowing whether the memory block returned to the partition belongs to that partition. In other words, if you allocate a memory block from a partition containing blocks of 32 bytes, then you should not return this block to a memory partition containing blocks of 120 bytes. The next time an application requests a block from the 120-byte partition, it will only get 32 valid bytes; the remaining 88 bytes may belong to some other task(s). This could certainly make your system crash.OSMemPut代码筛选版
没啥好说的,不过有个判定是否这个partition已经满了:
再提一下这个:Obtaining Status of a Memory Partition, OSMemQuery()
因为比较简单,所以我这里就把原文的参考手册部分复制过来了
这里有个消息接收的结构体:
(1) As usual, we start off by checking the arguments passed to the function.
(2) All the fields found in OS_MEM are copied to the OS_MEM_DATA data structure with interrupts disabled. This ensures that the fields will not be altered until they are all copied.
(3) You should also notice that computation of the number of blocks used is performed outside of the critical section because it’s done using the local copy of the data.
ok,我们最关心的其实是,什么时候需要使用get、put来得到内存使用
Using Memory Partitions
Figure 12.5 shows an example of how you can use the dynamic memory allocation feature of µC/OS-II, as well as its message-passing capability (第1部分讲解消息队列的时候有说). Also, refer to Listing 12.8 for the pseudocode(伪代码) of the two tasks shown. The numbers in parenthesis in Figure 12.5 correspond to the appropriate action in Listing 12.8.
The first task reads and checks the value of analog inputs (pressures, temperatures, voltages) and sends a message to the second task if any of the analog inputs exceed a threshold(阀值). The message sent contains a time stamp, information about which channel had the error, an error code, an indication of the severity of the error, and any other information you can think of.
Error handling in this example is centralized(集中的). This means that other tasks, or even ISRs, can post error messages to the error-handling task. The error-handling task could be responsible for displaying error messages on a monitor (a display), logging errors to a disk, or dispatching other tasks that could take corrective actions based on the error.
看看伪代码:
- 作者:liamY
- 链接:https://liamy.clovy.top/article/school/ucosII/memory
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。