Real-Time Embedded Multithreading--Using ThreadX & ARM-MEMORY MANAGEMENT: Block Pools

Summary of Memory Block Pools
Allocating memory in a fast and deterministic manner is essential in real-time applications.
This is made possible by creating and managing multiple pools of fixed-size memory
blocks called memory block pools.
Because memory block pools consist of fixed-size blocks, using them involves no
fragmentation problems. This is crucial because fragmentation causes behavior that is
inherently nondeterministic. In addition, allocating and freeing fixed-size blocks is fast—
the time required is comparable to that of simple linked-list manipulation. Furthermore,
the allocation service does not have to search through a list of blocks when it allocates and
deallocates from a memory block pool—it always allocates and deallocates at the head of
the available list. This provides the fastest possible linked list processing and might help
keep the currently used memory block in cache.
Lack of flexibility is the main drawback of fixed-size memory pools. The block size
of a pool must be large enough to handle the worst-case memory requirements of its
users. Making many different-sized memory requests from the same pool may cause
memory waste. One possible solution is to create several different memory block pools
that contain different sized memory blocks.
Each memory block pool is a public resource. ThreadX imposes no constraints as to
how pools may be used. Applications may create memory block pools either during initialization
or during run-time from within application threads. There is no limit to the
number of memory block pools an application may use.
As noted earlier, memory block pools contain a number of fixed-size blocks. The
block size, in bytes, is specified during creation of the pool. Each memory block in the
pool imposes a small amount of overhead—the size of a C pointer. In addition, ThreadX
may pad the block size in order to keep the beginning of each memory block on proper
alignment.
The number of memory blocks in a pool depends on the block size and the total
number of bytes in the memory area supplied during creation. To calculate the capacity
of a pool (number of blocks that will be available), divide the block size (including
padding and the pointer overhead bytes) into the total number of bytes in the supplied
memory area.
The memory area for the block pool is specified during creation, and can be located
anywhere in the target’s address space. This is an important feature because of the considerable
flexibility it gives the application. For example, suppose that a communication
product has a high-speed memory area for I/O. You can easily manage this memory area
by making it a memory block pool.
Application threads can suspend while waiting for a memory block from an empty
pool. When a block is returned to the pool, ThreadX gives this block to the suspended
thread and resumes the thread. If multiple threads are suspended on the same memory
block pool, ThreadX resumes them in the order that they occur on the suspend thread
list (usually FIFO).
However, an application can also cause the highest-priority thread to be resumed. To
accomplish this, the application calls tx_block_pool_prioritize prior to the block
release call that lifts thread suspension. The block pool prioritize service places the highest priority
thread at the front of the suspension list, while leaving all other suspended
threads in the same FIFO order.

Memory Block Pool Control Block
The characteristics of each memory block pool are found in its Control Block. It contains
information such as block size, and the number of memory blocks left. Memory
pool Control Blocks can be located anywhere in memory, but they are commonly
defined as global structures outside the scope of any function. Table Memory Block Pool Control Block lists most members of the memory pool Control Block.
在这里插入图片描述
The user of an allocated memory block must not write outside its boundaries. If this
happens, corruption occurs in an adjacent (usually subsequent) memory area. The results
are unpredictable and quite often catastrophic.
In most cases, the developer can ignore the contents of the Memory Block Pool Control
Block. However, there are several fields that may be useful during debugging, such
as the number of available blocks, the initial number of blocks, the actual block size, the
total number of bytes in the block pool, and the number of threads suspended on this
memory block pool.

Summary of Memory Block Pool Services
Appendix A contains detailed information about memory block pool services. This
appendix contains information about each service, such as the prototype, a brief description
of the service, required parameters, return values, notes and warnings, allowable
invocation, and an example showing how the service can be used. Table Services of the memory block pool contains a list of all available memory block pool services. In the succeeding sections of this chapter, we will investigate each of these services.
在这里插入图片描述
We will first consider the tx_block_pool_create service because it must be invoked
before any of the other services.

Creating a Memory Block Pool
A memory block pool is declared with the TX_BLOCK_POOL data type and is defined
with the tx_block_pool_create service. When defining a memory block pool, you
need to specify its Control Block, the name of the memory block pool, the address of the
memory block pool, and the number of bytes available.
We will develop one example of memory block pool creation to illustrate the use of
this service, and we will name it “my_pool.”

Creating a memory block pool.
TX_BLOCK_POOL my_pool;
UINT status;
/* Create a memory pool whose total size is 1000 bytes
starting at address 0x100000. Each block in this
pool is defined to be 50 bytes long. */
status = tx_block_pool_create(&my_pool, "my_pool_name",
50, (VOID *) 0x100000, 1000);
/* If status equals TX_SUCCESS, my_pool contains about 18
memory blocks of 50 bytes each. The reason there are
not 20 blocks in the pool is because of the one
overhead pointer associated with each block. */

If variable status contains the return value TX_SUCCESS, then we have successfully
created a memory block pool called my_pool that contains a total of 1,000 bytes, with
each block containing 50 bytes. The number of blocks can be calculated as follows:
在这里插入图片描述
Assuming that the value of size of (void*) is four bytes, the total number of blocks
available is calculated thus:
在这里插入图片描述
Use the preceding formula to avoid wasting space in a memory block pool. Be sure
to carefully estimate the needed block size and the amount of memory available to
the pool.

Allocating a Memory Block Pool
After a memory block pool has been declared and defined, we can start using it in a variety
of applications. The tx_block_allocate service is the method that allocates a fixedsize
block of memory from the memory block pool. Because the size of the memory
block pool is determined when it is created, we need to indicate what to do if enough
memory is not available from this block pool. The below code contains an example of allocating
one block from a memory block pool, in which we will “wait forever” if adequate
memory is not available. After memory allocation succeeds, the pointer memory_ptr
contains the starting location of the allocated fixed-size block of memory.

Allocation of a fixed-size block of memory.
TX_BLOCK_POOL my_pool;
unsigned char *memory_ptr;
UINT status;
...
/* Allocate a memory block from my_pool. Assume that the
pool has already been created with a call to
tx_block_pool_create. */
status = tx_block_allocate(&my_pool, (VOID **) &memory_ptr,
TX_WAIT_FOREVER);
/* If status equals TX_SUCCESS, memory_ptr contains the
address of the allocated block of memory. */
If variable status contains the return value TX_SUCCESS, then we have successfully

allocated one fixed-size block of memory. This block is pointed to by memory_ptr.

Deleting a Memory Block Pool
A memory block pool can be deleted with the tx_block_pool_delete service. All threads
that are suspended because they are waiting for memory from this block pool are
resumed and receive a TX_DELETED return status.

Deleting a memory block pool.
TX_BLOCK_POOL my_pool;
UINT status;
...
/* Delete entire memory block pool. Assume that the
pool has already been created with a call to
tx_block_pool_create. */
status = tx_block_pool_delete(&my_pool);
/* If status equals TX_SUCCESS, the memory block pool
has been deleted. */
If variable status contains the return value TX_SUCCESS, then we have successfully

deleted the memory block pool.

Retrieving Memory Block Pool Information
The tx_block_pool_info_get service retrieves a variety of information about a memory
block pool. The information that is retrieved includes the block pool name, the number of
blocks available, the total number of blocks in the pool, the location of the thread that is
first on the suspension list for this block pool, the number of threads currently suspended
on this block pool, and the location of the next created memory block pool. The below code
show how this service can be used to obtain information about a memory block pool.

TX_BLOCK_POOL my_pool;
CHAR *name;
ULONG available;
ULONG total_blocks;
TX_THREAD *first_suspended;
ULONG suspended_count;
TX_BLOCK_POOL *next_pool;
UINT status;
...
/* Retrieve information about the previously created
block pool "my_pool." */
status = tx_block_pool_info_get(&my_pool, &name,
&available,&total_blocks,
&first_suspended,
&suspended_count,
&next_pool);
/* If status equals TX_SUCCESS, the information requested
is valid. */
If variable status contains the return value TX_SUCCESS, then we have successfully obtained valid information 

about the memory block pool.

Prioritizing a Memory Block Pool Suspension List
When a thread is suspended because it is waiting for a memory block pool, it is placed in
the suspension list in a FIFO manner. When a memory block pool regains a block of
memory, the first thread in the suspension list (regardless of priority) receives an opportunity
to take a block from that memory block pool. The tx_block_pool_prioritize
service places the highest-priority thread suspended for ownership of a specific memory
block pool at the front of the suspension list. All other threads remain in the same FIFO
order in which they were suspended. The follow code contains an example showing how this
service can be used.

Prioritizing the memory block pool suspension list.
TX_BLOCK_POOL my_pool;
UINT status;
...
/* Ensure that the highest priority thread will receive
the next free block in this pool. */
status = tx_block_pool_prioritize(&my_pool);
/* If status equals TX_SUCCESS, the highest priority
suspended thread is at the front of the list. The
next tx_block_release call will wake up this thread. */
If the variable status contains the value TX_SUCCESS, the prioritization request succeeded.

The highest-priority thread in the suspension list that is waiting for the memory
block pool called “my_pool” has moved to the front of the suspension list. The service
call also returns TX_SUCCESS if no thread was waiting for this memory block pool. In
this case, the suspension list remains unchanged.

Releasing a Memory Block
The tx_block_release service releases one previously allocated memory block back to
its associated block pool. If one or more threads are suspended on this pool, each suspended
thread receives a memory block and is resumed until the pool runs out of blocks
or until there are no more suspended threads. This process of allocating memory to suspended
threads always begins with the first thread on the suspended list. The follow code
shows how this service can be used.

Release one block to the memory block pool.
TX_BLOCK_POOL my_pool;
unsigned char *memory_ptr;
UINT status;
...
/* Release a memory block back to my_pool. Assume that the
pool has been created and the memory block has been
allocated. */
status = tx_block_release((VOID *) memory_ptr);
/* If status equals TX_SUCCESS, the block of memory pointed
to by memory_ptr has been returned to the pool. */
If the variable status contains the value TX_SUCCESS, then the memory block pointed

to by memory_ptr has been returned to the memory block pool.

Memory Block Pool Example—Allocating
Thread Stacks
In the previous chapter, we allocated thread stack memory from arrays, and earlier in
this chapter we allocated thread stacks from a byte pool. In this example, we will use a
memory block pool. The first step is to declare the threads and a memory block pool as
follows:

TX_THREAD Speedy_Thread, Slow_Thread;
TX_MUTEX my_mutex;
#DEFINE STACK_SIZE 1024;
TX_BLOCK_POOL my_pool;

Before we define the threads, we need to create the memory block pool and allocate
memory for the thread stack. Following is the definition of the block pool, consisting of
four blocks of 1,024 bytes each and starting at location 0x500000.

UINT status;
status = tx_block_pool_create(&my_pool, "my_pool", 1024,
(VOID *) 0x500000, 4520);
Assuming that the return value was TX_SUCCESS, we have successfully created a
memory block pool. Next, we allocate memory from that block pool for the Speedy_
Thread stack, as follows:
CHAR *stack_ptr;
status = tx_block_allocate(&my_pool,
(VOID **) &stack_ptr,
TX_WAIT_FOREVER);

Assuming that the return value was TX_SUCCESS, we have successfully allocated a
block of memory for the stack, which is pointed to by stack_ptr. Next, we define
Speedy_Thread by using that block of memory for its stack, as follows:

tx_thread_create(&Speedy_Thread, "Speedy_Thread",
Speedy_Thread_entry, 0,
stack_ptr, STACK_SIZE,
5, 5, TX_NO_TIME_SLICE, TX_AUTO_START);

We define the Slow_Thread in a similar fashion. The thread entry functions remain
unchanged.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值