ThreadX内核源码分析 - 动态内存管理

Posted arm7star

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadX内核源码分析 - 动态内存管理相关的知识,希望对你有一定的参考价值。

ThreadX内核与μC/OS-II、NucleusPlus内核一样,都支持固定大小的内存块管理,也支持不固定大小的内存块管理。

固定大小的内存块管理是把一片内存分割成大小相同的多个内存块,以整块内存分配和释放,例如内存块大小为1024字节,那么每次只能申请释放1024字节的内存,一般只适合大小固定的内存块申请释放场景,类似linux的2的n次方的内存页申请,mmu只能管理页,不能把页分成更小的内存管理,因此要分配小的内存前,先按页分配,再在分配的页里面按byte分配内存。

1、block内存管理

1.1、block内存块介绍

ThreadX内核的内存块大致如下所示,将一块连续内存分割成n块连续大小相等的块,然后将这些块组成一个空闲链表,空闲内存块的前面部分信息就用于链表指针:

1.2、block内存申请_tx_block_allocate

申请内存主要是获取可用内存块,主要过程是:

  • 有可用内存块,在可用内存块前面空间存储pool地址,返回存储pool地址后的下一个可用内存地址(并不把整个内存块返回给应用程序,释放内存块函数不带 pool参数,因此需要通过可用内存往前的一段固定偏移地址来获取pool信息;有的系统释放是明确指定内存块的pool,那么就可以不预留指向pool的内存);
  • 没有可用内存块,那么就可能要阻塞当前线程,阻塞时主要是将线程挂载到pool的挂起链表,然后将可用内存块地址(应用程序获取内存的指针及其他信息保存到线程控制块里面,释放内存的线程就可以直接修改等待内存线程的内存返回地址、返回状态,而不需要先把内存放回空闲链表,再唤醒等待线程,让唤醒线程重新去申请内存,这样效率比较低)。

_tx_block_allocate申请内存代码如下:

080 UINT  _tx_block_allocate(TX_BLOCK_POOL *pool_ptr, VOID **block_ptr, ULONG wait_option)
081 
082 
083 TX_INTERRUPT_SAVE_AREA
084 
085 UINT                        status;
086 TX_THREAD                   *thread_ptr;
087 UCHAR                       *work_ptr;
088 UCHAR                       *temp_ptr;
089 UCHAR                       **next_block_ptr;
090 UCHAR                       **return_ptr;
091 UINT                        suspended_count;
092 TX_THREAD                   *next_thread;
093 TX_THREAD                   *previous_thread;
094 #ifdef TX_ENABLE_EVENT_TRACE
095 TX_TRACE_BUFFER_ENTRY       *entry_ptr;
096 ULONG                       time_stamp =  ((ULONG) 0);
097 #endif
098 #ifdef TX_ENABLE_EVENT_LOGGING
099 UCHAR                       *log_entry_ptr;
100 ULONG                       upper_tbu;
101 ULONG                       lower_tbu;
102 #endif
103 
104 
105     /* Disable interrupts to get a block from the pool.  */
106     TX_DISABLE
107 
108 #ifdef TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
109 
110     /* Increment the total allocations counter.  */
111     _tx_block_pool_performance_allocate_count++;
112 
113     /* Increment the number of allocations on this pool.  */
114     pool_ptr -> tx_block_pool_performance_allocate_count++;
115 #endif
116 
117 #ifdef TX_ENABLE_EVENT_TRACE
118 
119     /* If trace is enabled, save the current event pointer.  */
120     entry_ptr =  _tx_trace_buffer_current_ptr;
121 
122     /* If trace is enabled, insert this event into the trace buffer.  */
123     TX_TRACE_IN_LINE_INSERT(TX_TRACE_BLOCK_ALLOCATE, pool_ptr, 0, wait_option, pool_ptr -> tx_block_pool_available, TX_TRACE_BLOCK_POOL_EVENTS)
124 
125     /* Save the time stamp for later comparison to verify that
126        the event hasn't been overwritten by the time the allocate
127        call succeeds.  */
128     if (entry_ptr != TX_NULL)
129     
130 
131         time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
132     
133 #endif
134 
135 #ifdef TX_ENABLE_EVENT_LOGGING
136     log_entry_ptr =  *(UCHAR **) _tx_el_current_event;
137 
138     /* Log this kernel call.  */
139     TX_EL_BLOCK_ALLOCATE_INSERT
140 
141     /* Store -1 in the third event slot.  */
142     *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =  (ULONG) -1;
143 
144     /* Save the time stamp for later comparison to verify that
145        the event hasn't been overwritten by the time the allocate
146        call succeeds.  */
147     lower_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET));
148     upper_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET));
149 #endif
150 
151     /* Determine if there is an available block.  */
152     if (pool_ptr -> tx_block_pool_available != ((UINT) 0)) // 可用空闲内存块数量tx_block_pool_available不为0,有可用空闲内存块,那么获取内存块即可
153     
154 
155         /* Yes, a block is available.  Decrement the available count.  */
156         pool_ptr -> tx_block_pool_available--; // 可用空闲内存块数量减1
157 
158         /* Pickup the current block pointer.  */
159         work_ptr =  pool_ptr -> tx_block_pool_available_list; // 获取第一可用空闲内存块(所有内存大小都一样,只需要获取可用空闲内存块链表表头的第一个内存块即可)
160 
161         /* Return the first available block to the caller.  */
162         temp_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *))); // temp_ptr等于work_ptr加上一个指针(内存块的前面一个指针大小用与指向内存块所在的pool,ThreadX的内存块释放函数没有指定pool,所以要在内存块的前面保存pool)
163         return_ptr =  TX_INDIRECT_VOID_TO_UCHAR_POINTER_CONVERT(block_ptr); // block_ptr强制转换为UCHAR **类型(类似malloc,内存指针都是void *)
164         *return_ptr =  temp_ptr; // 申请到的内存地址
165 
166         /* Modify the available list to point at the next block in the pool. */
167         next_block_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr); // 下一个可用空闲内存块
168         pool_ptr -> tx_block_pool_available_list =  *next_block_ptr; // pool的可用空闲内存块链表指向下一个空闲内存块
169 
170         /* Save the pool's address in the block for when it is released!  */
171         temp_ptr =  TX_BLOCK_POOL_TO_UCHAR_POINTER_CONVERT(pool_ptr); // pool的地址
172         *next_block_ptr =  temp_ptr; // pool地址写入申请的内存块的前面(释放内存块时,找到对应的pool,放回到对应的pool)
173     
174 #ifdef TX_ENABLE_EVENT_TRACE
175 
176         /* Check that the event time stamp is unchanged.  A different
177            timestamp means that a later event wrote over the byte
178            allocate event.  In that case, do nothing here.  */
179         if (entry_ptr != TX_NULL)
180         
181 
182             /* Is the time stamp the same?  */
183             if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
184             
185             
186                 /* Timestamp is the same, update the entry with the address.  */
187 #ifdef TX_MISRA_ENABLE
188                 entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*block_ptr);
189 #else
190                 entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*block_ptr);
191 #endif
192             
193         
194 #endif
195 
196 #ifdef TX_ENABLE_EVENT_LOGGING
197         /* Store the address of the allocated block.  */
198         *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =  (ULONG) *block_ptr;
199 #endif
200 
201         /* Set status to success.  */
202         status =  TX_SUCCESS;
203         
204         /* Restore interrupts.  */
205         TX_RESTORE // 申请到了内存,开启中断
206     
207     else // 没有可用空闲内存块的清空下
208     
209 
210         /* Default the return pointer to NULL.  */
211         return_ptr =   TX_INDIRECT_VOID_TO_UCHAR_POINTER_CONVERT(block_ptr);
212         *return_ptr =  TX_NULL; // 设置返回内存的地址为null
213 
214         /* Determine if the request specifies suspension.  */
215         if (wait_option != TX_NO_WAIT) // 非TX_NO_WAIT,也就是申请内存的线程在没申请到内存时要等待有可用内存或者等待超时
216         
217 
218             /* Determine if the preempt disable flag is non-zero.  */
219             if (_tx_thread_preempt_disable != ((UINT) 0)) // 如果禁用了抢占,那么线程不能阻塞,否则其他线程也得不到调度,暂用内存块的线程得不到调度内存就不可能释放,内核会进入死循环
220             
221 
222                 /* Suspension is not allowed if the preempt disable flag is non-zero at this point, return error completion.  */
223                 status =  TX_NO_MEMORY; // 申请不到内存又不能进入阻塞状态,设置没又内存可用,后面返回上一级函数
224 
225                 /* Restore interrupts.  */
226                 TX_RESTORE
227             
228             else // 允许抢占,线程可以进入阻塞状态
229             
230 
231                 /* Prepare for suspension of this thread.  */
232             
233 #ifdef TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
234 
235                 /* Increment the total suspensions counter.  */
236                 _tx_block_pool_performance_suspension_count++;
237 
238                 /* Increment the number of suspensions on this pool.  */
239                 pool_ptr -> tx_block_pool_performance_suspension_count++;
240 #endif
241 
242                 /* Pickup thread pointer.  */
243                 TX_THREAD_GET_CURRENT(thread_ptr) // 获取当前线程指针
244 
245                 /* Setup cleanup routine pointer.  */
246                 thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_block_pool_cleanup); // 设置挂起清理函数指针(阻塞被唤醒后执行的函数)
247 
248                 /* Setup cleanup information, i.e. this pool control
249                    block.  */
250                 thread_ptr -> tx_thread_suspend_control_block =  (VOID *) pool_ptr; // pool信息
251 
252                 /* Save the return block pointer address as well.  */
253                 thread_ptr -> tx_thread_additional_suspend_info =  (VOID *) block_ptr; // 内存地址
254 
255 #ifndef TX_NOT_INTERRUPTABLE
256 
257                 /* Increment the suspension sequence number, which is used to identify
258                    this suspension event.  */
259                 thread_ptr -> tx_thread_suspension_sequence++;
260 #endif
261 
262                 /* Pickup the number of suspended threads.  */
263                 suspended_count =  (pool_ptr -> tx_block_pool_suspended_count);
264             
265                 /* Increment the number of suspended threads.  */
266                 (pool_ptr -> tx_block_pool_suspended_count)++; // 因等待内存而挂起的线程数加1
267 
268                 /* Setup suspension list.  */
269                 if (suspended_count == TX_NO_SUSPENSIONS) // 如果当前线程是第一个等待内存的线程,把当前线程挂载到pool的挂起链表tx_block_pool_suspension_list即可
270                 
271 
272                     /* No other threads are suspended.  Setup the head pointer and
273                        just setup this threads pointers to itself.  */
274                     pool_ptr -> tx_block_pool_suspension_list =     thread_ptr;
275                     thread_ptr -> tx_thread_suspended_next =        thread_ptr;
276                     thread_ptr -> tx_thread_suspended_previous =    thread_ptr;
277                 
278                 else // 还有其他线程也在等待内存块,当前线程添加到tx_block_pool_suspension_list末尾即可
279                 
280 
281                     /* This list is not NULL, add current thread to the end. */
282                     next_thread =                                   pool_ptr -> tx_block_pool_suspension_list;
283                     thread_ptr -> tx_thread_suspended_next =        next_thread;
284                     previous_thread =                               next_thread -> tx_thread_suspended_previous;
285                     thread_ptr -> tx_thread_suspended_previous =    previous_thread;
286                     previous_thread -> tx_thread_suspended_next =   thread_ptr;
287                     next_thread -> tx_thread_suspended_previous =   thread_ptr;
288                 
289 
290                 /* Set the state to suspended.  */
291                 thread_ptr -> tx_thread_state =       TX_BLOCK_MEMORY; // 设置线程阻塞状态(之前有介绍挂起线程操作,如果线程处于阻塞状态,那么需要延迟挂起,否则需要将线程从tx_block_pool_suspension_list删除,当有可用内存块的时候,可用内存就不会分配给不在等待链表里面的线程)
292 
293 #ifdef TX_NOT_INTERRUPTABLE
294 
295                 /* Call actual non-interruptable thread suspension routine.  */
296                 _tx_thread_system_ni_suspend(thread_ptr, wait_option);
297 
298                 /* Restore interrupts.  */
299                 TX_RESTORE
300 #else
301 
302                 /* Set the suspending flag.  */
303                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
304 
305                 /* Setup the timeout period.  */
306                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option; // 超时时间(_tx_thread_system_suspend会根据该值决定是否启动定时器,线程如果在超时时间内没有获取到内存,那么超时定时器将被调用,线程将被唤醒)
307 
308                 /* Temporarily disable preemption.  */
309                 _tx_thread_preempt_disable++;
310 
311                 /* Restore interrupts.  */
312                 TX_RESTORE
313 
314                 /* Call actual thread suspension routine.  */
315                 _tx_thread_system_suspend(thread_ptr); // 挂起当前线程
316 #endif
317 
318 #ifdef TX_ENABLE_EVENT_TRACE
319 
320                 /* Check that the event time stamp is unchanged.  A different
321                    timestamp means that a later event wrote over the byte
322                    allocate event.  In that case, do nothing here.  */
323                 if (entry_ptr != TX_NULL)
324                 
325             
326                     /* Is the time-stamp the same?  */
327                     if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
328                     
329                 
330                         /* Timestamp is the same, update the entry with the address.  */
331 #ifdef TX_MISRA_ENABLE
332                         entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*block_ptr);
333 #else
334                         entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*block_ptr);
335 #endif
336                     
337                 
338 #endif
339 
340 #ifdef TX_ENABLE_EVENT_LOGGING
341                 /* Check that the event time stamp is unchanged and the call is about
342                    to return success.  A different timestamp means that a later event
343                    wrote over the block allocate event.  A return value other than
344                    TX_SUCCESS indicates that no block was available. In those cases,
345                    do nothing here.  */
346                 if (lower_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) &&
347                     upper_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)) &&
348                     ((thread_ptr -> tx_thread_suspend_status) == TX_SUCCESS))
349                 
350 
351                     /* Store the address of the allocated block.  */
352                     *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_3_OFFSET)) =  (ULONG) *block_ptr;
353                 
354 #endif
355 
356                 /* Return the completion status.  */
357                 status =  thread_ptr -> tx_thread_suspend_status; // 线程挂起状态(释放内存块的线程会把内存块直接给等待内存的线程,也就是tx_thread_additional_suspend_info(block_ptr))
358             
359         
360         else
361         
362 
363             /* Immediate return, return error completion.  */
364             status =  TX_NO_MEMORY;
365 
366             /* Restore interrupts.  */
367             TX_RESTORE
368         
369     
370 
371     /* Return completion status.  */
372     return(status);
373 

1.3、申请内存块超时

线程申请内存块超时,主要是超时定时器处理,超时定时器处理线程_tx_timer_thread_entry调用线程超时函数_tx_thread_timeout处理线程超时事件,_tx_thread_timeout调用线程超时的处理函数tx_thread_suspend_cleanup,对应等待内存的线程回调函数就指向_tx_block_pool_cleanup。

等待内存超时的清理函数,主要从等待链表删除超时线程,设置线程挂起返回状态(没有内存),唤醒阻塞的线程,_tx_block_pool_cleanup代码实现如下:

078 VOID  _tx_block_pool_cleanup(TX_THREAD *thread_ptr, ULONG suspension_sequence)
079 
080 
081 #ifndef TX_NOT_INTERRUPTABLE
082 TX_INTERRUPT_SAVE_AREA
083 #endif
084 
085 TX_BLOCK_POOL       *pool_ptr;
086 UINT                suspended_count;
087 TX_THREAD           *next_thread;
088 TX_THREAD           *previous_thread;
089 
090     
091 #ifndef TX_NOT_INTERRUPTABLE
092 
093     /* Disable interrupts to remove the suspended thread from the block pool.  */
094     TX_DISABLE
095 
096     /* Determine if the cleanup is still required.  */
097     if (thread_ptr -> tx_thread_suspend_cleanup == &(_tx_block_pool_cleanup)) // 再次检查tx_thread_suspend_cleanup函数,有可能超时处理过程,释放内存的线程已经把内存分配给了超时的线程(超时和内存释放同时发生,保证不了谁先执行)
098     
099     
100         /* Check for valid suspension sequence.  */
101         if (suspension_sequence == thread_ptr -> tx_thread_suspension_sequence)
102         
103 
104             /* Setup pointer to block pool control block.  */
105             pool_ptr =  TX_VOID_TO_BLOCK_POOL_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block); // 获取等待的pool
106 
107             /* Check for a NULL byte pool pointer.  */
108             if (pool_ptr != TX_NULL) // pool不为空(再次核对pool相关信息)
109             
110             
111                 /* Check for valid pool ID.  */
112                 if (pool_ptr -> tx_block_pool_id == TX_BLOCK_POOL_ID) // pool类型为块类型
113                 
114 
115                     /* Determine if there are any thread suspensions.  */
116                     if (pool_ptr -> tx_block_pool_suspended_count != TX_NO_SUSPENSIONS) // 是否有等待内存的线程(如果没有的话,那么需要唤醒的线程应该就不再等待内存块了;ThreadX很多开关中断操作,有些关联操作不保证原子操作,所以有多重检查)
117                     
118 #else
119 
120                         /* Setup pointer to block pool control block.  */
121                         pool_ptr =  TX_VOID_TO_BLOCK_POOL_POINTER_CONVERT(thread_ptr -> tx_thread_suspend_control_block); // 获取等待的pool
122 #endif
123 
124                         /* Yes, we still have thread suspension!  */
125 
126                         /* Clear the suspension cleanup flag.  */
127                         thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL; // 清空线程挂起清理函数指针
128 
129                         /* Decrement the suspended count.  */
130                         pool_ptr -> tx_block_pool_suspended_count--; // 线程超时,不再等待内存块,pool等待挂起计数器减1
131 
132                         /* Pickup the suspended count.  */
133                         suspended_count =  pool_ptr -> tx_block_pool_suspended_count;
134 
135                         /* Remove the suspended thread from the list.  */
136     
137                         /* See if this is the only suspended thread on the list.  */
138                         if (suspended_count == TX_NO_SUSPENSIONS) // 当前线程是最后一个等待内存块的线程,等待链表清空即可
139                         
140 
141                             /* Yes, the only suspended thread.  */
142     
143                             /* Update the head pointer.  */
144                             pool_ptr -> tx_block_pool_suspension_list =  TX_NULL;
145                         
146                         else // 还有其他线程等待内存块,将当前线程从等待链表删除
147                         
148 
149                             /* At least one more thread is on the same suspension list.  */
150 
151                             /* Update the links of the adjacent threads.  */
152                             next_thread =                                   thread_ptr -> tx_thread_suspended_next;
153                             previous_thread =                               thread_ptr -> tx_thread_suspended_previous;
154                             next_thread -> tx_thread_suspended_previous =   previous_thread;
155                             previous_thread -> tx_thread_suspended_next =   next_thread;
156 
157                             /* Determine if we need to update the head pointer.  */
158                             if (pool_ptr -> tx_block_pool_suspension_list == thread_ptr)
159                             
160             
161                                 /* Update the list head pointer.  */
162                                 pool_ptr -> tx_block_pool_suspension_list =     next_thread;
163                             
164                         
165 
166                         /* Now we need to determine if this cleanup is from a terminate, timeout,
167                            or from a wait abort.  */
168                         if (thread_ptr -> tx_thread_state == TX_BLOCK_MEMORY) // 检查是等待超时调用清理函数还是函数线程终止调用清理函数,如果是线程终止,那么不需要做其他处理
169                         
170 
171                             /* Timeout condition and the thread still suspended on the block pool.  
172                                Setup return error status and resume the thread.  */
173 
174 #ifdef TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
175 
176                             /* Increment the total timeouts counter.  */
177                             _tx_block_pool_performance_timeout_count++;
178 
179                             /* Increment the number of timeouts on this block pool.  */
180                             pool_ptr -> tx_block_pool_performance_timeout_count++;
181 #endif
182 
183                             /* Setup return status.  */
184                             thread_ptr -> tx_thread_suspend_status =  TX_NO_MEMORY; // 等待内存超时,设置返回状态为TX_NO_MEMORY,没有等到内存
185 
186 #ifdef TX_NOT_INTERRUPTABLE
187 
188                             /* Resume the thread!  */
189                             _tx_thread_system_ni_resume(thread_ptr);
190 #else
191                             /* Temporarily disable preemption.  */
192                             _tx_thread_preempt_disable++;
193 
194                             /* Restore interrupts.  */
195                             TX_RESTORE
196 
197                             /* Resume the thread!  */
198                             _tx_thread_system_resume(thread_ptr); // 唤醒阻塞的等待内存的线程
199 
200                             /* Disable interrupts.  */
201                             TX_DISABLE
202 #endif
203                         
204 #ifndef TX_NOT_INTERRUPTABLE
205                     
206                 
207             
208         
209     
210 
211     /* Restore interrupts.  */
212     TX_RESTORE
213 #endif
214 

1.4、block内存释放_tx_block_release

与申请内存超时一样,_tx_block_release释放内存也是检查等待内存的线程的各种参数状态,最后把线程控制块里面记录的返回内存的地址取出来,把当前释放的内存地址赋值给等待线程的内存返回地址即可,然后设置获取内存成功,唤醒等待内存块的线程(唤醒线程函数会把超时定时器去激活);如果没有线程等待内存,那么将释放的内存返回空闲链表,插入空闲链表表头(内存块大小一样,下次应该比较块申请到该内存块,对cache性能应该有改善,如果分配一个没有使用过的cache,那么cache需要重新从内存加载)。

_tx_block_release代码比较简单,实现如下:

075 UINT  _tx_block_release(VOID *block_ptr)
076 
077 
078 TX_INTERRUPT_SAVE_AREA
079 
080 TX_BLOCK_POOL       *pool_ptr;
081 TX_THREAD           *thread_ptr;
082 UCHAR               *work_ptr;
083 UCHAR               **return_block_ptr;
084 UCHAR               **next_block_ptr;
085 UINT                suspended_count;
086 TX_THREAD           *next_thread;
087 TX_THREAD           *previous_thread;
088 
089 
090     /* Disable interrupts to put this block back in the pool.  */
091     TX_DISABLE
092 
093     /* Pickup the pool pointer which is just previous to the starting 
094        address of the block that the caller sees.  */
095     work_ptr =        TX_VOID_TO_UCHAR_POINTER_CONVERT(block_ptr);
096     work_ptr =        TX_UCHAR_POINTER_SUB(work_ptr, (sizeof(UCHAR *)));
097     next_block_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
098     pool_ptr =        TX_UCHAR_TO_BLOCK_POOL_POINTER_CONVERT((*next_block_ptr));
099 
100 #ifdef TX_BLOCK_POOL_ENABLE_PERFORMANCE_INFO
101 
102     /* Increment the total releases counter.  */
103     _tx_block_pool_performance_release_count++;
104 
105     /* Increment the number of releases on this pool.  */
106     pool_ptr -> tx_block_pool_performance_release_count++;
107 #endif
108 
109     /* If trace is enabled, insert this event into the trace buffer.  */
110     TX_TRACE_IN_LINE_INSERT(TX_TRACE_BLOCK_RELEASE, pool_ptr, TX_POINTER_TO_ULONG_CONVERT(block_ptr), pool_ptr -> tx_block_pool_suspended_count, TX_POINTER_TO_ULONG_CONVERT(&work_ptr), TX_TRACE_BLOCK_POOL_EVENTS)
111 
112     /* Log this kernel call.  */
113     TX_EL_BLOCK_RELEASE_INSERT
114 
115     /* Determine if there are any threads suspended on the block pool.  */
116     thread_ptr =  pool_ptr -> tx_block_pool_suspension_list;
117     if (thread_ptr != TX_NULL)
118     
119 
120         /* Remove the suspended thread from the list.  */
121 
122         /* Decrement the number of threads suspended.  */
123         (pool_ptr -> tx_block_pool_suspended_count)--;
124         
125         /* Pickup the suspended count.  */
126         suspended_count =  (pool_ptr -> tx_block_pool_suspended_count);
127 
128         /* See if this is the only suspended thread on the list.  */
129         if (suspended_count == TX_NO_SUSPENSIONS)
130         
131 
132             /* Yes, the only suspended thread.  */
133 
134             /* Update the head pointer.  */
135             pool_ptr -> tx_block_pool_suspension_list =  TX_NULL;
136         
137         else
138         
139 
140             /* At least one more thread is on the same expiration list.  */
141 
142             /* Update the list head pointer.  */
143             next_thread =                                thread_ptr -> tx_thread_suspended_next;
144             pool_ptr -> tx_block_pool_suspension_list =  next_thread;
145 
146             /* Update the links of the adjacent threads.  */
147             previous_thread =                              thread_ptr -> tx_thread_suspended_previous;
148             next_thread -> tx_thread_suspended_previous =  previous_thread;
149             previous_thread -> tx_thread_suspended_next =  next_thread;
150         
151  
152         /* Prepare for resumption of the first thread.  */
153 
154         /* Clear cleanup routine to avoid timeout.  */
155         thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL;
156 
157         /* Return this block pointer to the suspended thread waiting for
158            a block.  */
159         return_block_ptr =  TX_VOID_TO_INDIRECT_UCHAR_POINTER_CONVERT(thread_ptr -> tx_thread_additional_suspend_info);
160         work_ptr =          TX_VOID_TO_UCHAR_POINTER_CONVERT(block_ptr);
161         *return_block_ptr =  work_ptr;
162 
163         /* Put return status into the thread control block.  */
164         thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS;
165 
166 #ifdef TX_NOT_INTERRUPTABLE
167 
168         /* Resume the thread!  */
169         _tx_thread_system_ni_resume(thread_ptr);
170 
171         /* Restore interrupts.  */
172         TX_RESTORE
173 #else
174 
175         /* Temporarily disable preemption.  */
176         _tx_thread_preempt_disable++;
177 
178         /* Restore interrupts.  */
179         TX_RESTORE
180 
181         /* Resume thread.  */
182         _tx_thread_system_resume(thread_ptr);
183 #endif
184     
185     else
186     
187 
188         /* No thread is suspended for a memory block.  */
189 
190         /* Put the block back in the available list.  */
191         *next_block_ptr =  pool_ptr -> tx_block_pool_available_list;
192 
193         /* Adjust the head pointer.  */
194         pool_ptr -> tx_block_pool_available_list =  work_ptr;
195 
196         /* Increment the count of available blocks.  */
197         pool_ptr -> tx_block_pool_available++;
198 
199         /* Restore interrupts.  */
200         TX_RESTORE
201     
202 
203     /* Return successful completion status.  */
204     return(TX_SUCCESS);
205 

2、byte内存管理

2.1、byte内存管理介绍

byte内存管理就是分配内存的大小不固定,内存块大小不固定,byte内存管理将内存初始化两个节点,最后一个节点为不可用内存,所有的内存块组成一个循环链表,相邻地址的内存块在链表中紧挨着;与block块内存管理不同的是,非空闲内存块也在链表中,只是多空闲与否的标记以及下一个内存块地址的指针,byte内存块申请过程会把一个大的空闲内存块一分为二,产生较多小的不连续的空闲内存块,为了避免太多碎片,内存块不够的时候需要合并相邻的空闲内存块,因此所有内存内存块是按地址链接在一个链表里面的;内存申请的时候只标记内存块非空闲,并不会改变链表结构。

 初始化的内存链表大致如下(下面的pool_ptr在内存块空闲的时候,里面的值是空闲标记,内存被申请的时候,里面是指向pool的指针,空闲标记的值比较特殊,不会与内存地址冲突,因此可以有两种用处;释放内存的时候需要获取pool指针,这样才能更新pool的信息):

2.2、byte内存申请_tx_byte_allocate

_tx_byte_allocate申请内存对内存链表遍历需要较长时间,中间会打开中断,避免阻塞中断;

对于 并发操作,pool的操作增加了一个tx_byte_pool_owner记录最近一次操作pool的线程,开中断期间:

  • 如果有别的线程也申请或者释放内存,那么tx_byte_pool_owner会被改变,当前线程检查到tx_byte_pool_owner不指向自己的线程,那么就可以判断有其他线程操作了pool,pool可能发生了变化,还没查找完pool的话,就需要重新查找pool,没有找到可用内存块的话,也需要重新查找pool(线程还没真正睡眠,修改pool的线程并不知道有线程等待内存,如果线程进入睡眠,即使有可用空闲内存,要是没有其他线程释放内存的话,阻塞的线程是感知不到内存变化的,就不会被唤醒);
  • 如果没有其他线程操作pool,那么线程可以继续之前查找的结果接着查找,没有找到可用空闲内存块的话就阻塞自己,挂载到pool等待链表,有别的线程释放内存的话,就会检查是否有空闲内存满足等待线程的内存,如果有就会给阻塞线程分配内存并唤醒阻塞线程。

_tx_byte_allocate代码实现如下:

082 UINT  _tx_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size,  ULONG wait_option)
083 
084 
085 TX_INTERRUPT_SAVE_AREA
086 
087 UINT                        status;
088 TX_THREAD                   *thread_ptr;
089 UCHAR                       *work_ptr;
090 UINT                        suspended_count;
091 TX_THREAD                   *next_thread;
092 TX_THREAD                   *previous_thread;
093 UINT                        finished;
094 #ifdef TX_ENABLE_EVENT_TRACE
095 TX_TRACE_BUFFER_ENTRY       *entry_ptr;
096 ULONG                       time_stamp =  ((ULONG) 0);
097 #endif
098 #ifdef TX_ENABLE_EVENT_LOGGING
099 UCHAR                       *log_entry_ptr;
100 ULONG                       upper_tbu;
101 ULONG                       lower_tbu;
102 #endif
103 
104 
105     /* Round the memory size up to the next size that is evenly divisible by
106        an ALIGN_TYPE (this is typically a 32-bit ULONG).  This guarantees proper alignment.  */
107     memory_size = (((memory_size + (sizeof(ALIGN_TYPE)))-((ALIGN_TYPE) 1))/(sizeof(ALIGN_TYPE))) * (sizeof(ALIGN_TYPE));
108 
109     /* Disable interrupts.  */
110     TX_DISABLE
111 
112     /* Pickup thread pointer.  */
113     TX_THREAD_GET_CURRENT(thread_ptr)
114 
115 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
116 
117     /* Increment the total allocations counter.  */
118     _tx_byte_pool_performance_allocate_count++;
119 
120     /* Increment the number of allocations on this pool.  */
121     pool_ptr -> tx_byte_pool_performance_allocate_count++;
122 #endif
123 
124 #ifdef TX_ENABLE_EVENT_TRACE
125 
126     /* If trace is enabled, save the current event pointer.  */
127     entry_ptr =  _tx_trace_buffer_current_ptr;
128 
129     /* If trace is enabled, insert this event into the trace buffer.  */
130     TX_TRACE_IN_LINE_INSERT(TX_TRACE_BYTE_ALLOCATE, pool_ptr, 0, memory_size, wait_option, TX_TRACE_BYTE_POOL_EVENTS)
131 
132     /* Save the time stamp for later comparison to verify that
133        the event hasn't been overwritten by the time the allocate
134        call succeeds.  */
135     if (entry_ptr != TX_NULL)
136     
137 
138         time_stamp =  entry_ptr -> tx_trace_buffer_entry_time_stamp;
139     
140 #endif
141 
142 #ifdef TX_ENABLE_EVENT_LOGGING
143     log_entry_ptr =  *(UCHAR **) _tx_el_current_event;
144 
145     /* Log this kernel call.  */
146     TX_EL_BYTE_ALLOCATE_INSERT
147 
148     /* Store -1 in the fourth event slot.  */
149     *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) -1;
150 
151     /* Save the time stamp for later comparison to verify that
152        the event hasn't been overwritten by the time the allocate
153        call succeeds.  */
154     lower_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET));
155     upper_tbu =  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET));
156 #endif
157 
158     /* Set the search finished flag to false.  */
159     finished =  TX_FALSE;
160 
161     /* Loop to handle cases where the owner of the pool changed.  */
162     do // 与block内存分配不同的是,block内存大小固定,释放内存时,肯定可以把释放的内存给另外一个等待内存的线程,但是byte内存释放时,释放的大小并不一定满足等待内存线程需要的内存的大小,因此有更多内存的时候,需要等待内存的线程自己去检查是否有足够空闲内存,所以这里是不断检查是否有足够空闲内存
163     
164 
165         /* Indicate that this thread is the current owner.  */
166         pool_ptr -> tx_byte_pool_owner =  thread_ptr; // 记录当前线程在操作该pool(主要是用于检查是否有别的线程操作了pool,如果没有找到合适大小的可用空闲内存块,在进入阻塞前,如果有其他线程操作了pool,那么可能pool里面可能存在可用空闲内存块,如果不再去检查的话也没有其他线程释放内存的话,当前线程可能就永远不会被唤醒了,只有内存释放的时候会去检查唤醒等待内存的线程)
167 
168         /* Restore interrupts.  */
169         TX_RESTORE // 允许中断(byte内存申请使用频繁,耗时比较长,不能简单的互斥访问,否则并发度很低,影响性能)
170 
171         /* At this point, the executing thread owns the pool and can perform a search
172            for free memory.  */
173         work_ptr =  _tx_byte_pool_search(pool_ptr, memory_size); // 查找memory_size大小的空闲内存(_tx_byte_pool_search的时候会合并拆分空闲链表)
174 
175         /* Optional processing extension.  */
176         TX_BYTE_ALLOCATE_EXTENSION
177 
178         /* Lockout interrupts.  */
179         TX_DISABLE
180 
181         /* Determine if we are finished.  */
182         if (work_ptr != TX_NULL) // 如果找到了可用空闲内存,那么查找结束,work_ptr就是申请到的内存的地址
183         
184         
185             /* Yes, we have found a block the search is finished.  */
186             finished =  TX_TRUE;
187         
188         else // 没有找到可用的内存
189         
190         
191             /* No block was found, does this thread still own the pool?  */
192             if (pool_ptr -> tx_byte_pool_owner == thread_ptr) // 有别的线程修改了tx_byte_pool_owner,对pool进行了操作,如果别的线程释放了内存,那么要再次检查是否有可用内存(没有找到合适内存后会打开中断,如果开中断后有高优先级新城就绪或者其他情况导致有内存被释放,那么这里需要重新查找可用内存),如果pool没有被修改,那么就要进入睡眠,此时中断已经关闭,不会有线程修改pool了
193             
194             
195                 /* Yes, then we have looked through the entire pool and haven't found the memory.  */
196                 finished =  TX_TRUE; // 没有其他线程修改pool,不用再检查释放有可用内存
197             
198         
199 
200      while (finished == TX_FALSE); // finished为TX_FALSE,表明没有获取到内存并且pool被修改了,那么再次检查是否有可用内存
201 
202     /* Copy the pointer into the return destination.  */
203     *memory_ptr =  (VOID *) work_ptr; // 申请到的内存或者null
204 
205     /* Determine if memory was found.  */
206     if (work_ptr != TX_NULL) // 申请到了内存,返回成功,if里面很多代码都是性能统计或者其他trace的代码,不用过多查看
207     
208 
209 #ifdef TX_ENABLE_EVENT_TRACE
210 
211         /* Check that the event time stamp is unchanged.  A different
212            timestamp means that a later event wrote over the byte
213            allocate event.  In that case, do nothing here.  */
214         if (entry_ptr != TX_NULL)
215         
216 
217             /* Is the timestamp the same?  */
218             if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
219             
220             
221                 /* Timestamp is the same, update the entry with the address.  */
222 #ifdef TX_MISRA_ENABLE
223                 entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
224 #else
225                 entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
226 #endif
227             
228         
229 #endif
230 
231 #ifdef TX_ENABLE_EVENT_LOGGING
232         /* Check that the event time stamp is unchanged.  A different
233            timestamp means that a later event wrote over the byte
234            allocate event.  In that case, do nothing here.  */
235         if (lower_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) &&
236             upper_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)))
237         
238             /* Store the address of the allocated fragment.  */
239             *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) *memory_ptr;
240         
241 #endif
242 
243         /* Restore interrupts.  */
244         TX_RESTORE
245         
246         /* Set the status to success.  */
247         status =  TX_SUCCESS;
248     
249     else // 没有找到可用内存,检查是否等待内存还是直接返回失败
250     
251 
252         /* No memory of sufficient size was found...  */
253 
254         /* Determine if the request specifies suspension.  */
255         if (wait_option != TX_NO_WAIT) // 有等待选项
256         
257 
258             /* Determine if the preempt disable flag is non-zero.  */
259             if (_tx_thread_preempt_disable != ((UINT) 0)) // 检查是否禁止抢占,如果禁止抢占的话,线程不能阻塞,否则会禁止其他线程的调度,其他就绪线程都没办法执行了
260             
261 
262                 /* Suspension is not allowed if the preempt disable flag is non-zero at this point - return error completion.  */
263                 status =  TX_NO_MEMORY; // 禁止抢占,不能阻塞,返回申请内存失败即可
264 
265                 /* Restore interrupts.  */
266                 TX_RESTORE
267             
268             else // 阻塞等待内存,超时处理过程与block内存超时一样,别的线程释放内存时,如果有足够内存的话,也是由释放内存的线程把内存给等待内存的线程(合并/拆分空闲内存块等)
269             
270 
271                 /* Prepare for suspension of this thread.  */
272 
273 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
274 
275                 /* Increment the total suspensions counter.  */
276                 _tx_byte_pool_performance_suspension_count++;
277 
278                 /* Increment the number of suspensions on this pool.  */
279                 pool_ptr -> tx_byte_pool_performance_suspension_count++;
280 #endif
281 
282                 /* Setup cleanup routine pointer.  */
283                 thread_ptr -> tx_thread_suspend_cleanup =  &(_tx_byte_pool_cleanup);
284 
285                 /* Setup cleanup information, i.e. this pool control
286                    block.  */
287                 thread_ptr -> tx_thread_suspend_control_block =  (VOID *) pool_ptr;
288 
289                 /* Save the return memory pointer address as well.  */
290                 thread_ptr -> tx_thread_additional_suspend_info =  (VOID *) memory_ptr; // 别的线程释放内存时,如果有可用内存分配给阻塞的线程,那么会把申请到的内存赋值给memory_ptr(被释放的内存可能满足内存大小,或者被释放内存合并相邻空闲块后的空闲内存块可能满足内存大小,所以释放内存时检查是否有合适内存效率应该高一些)
291 
292                 /* Save the byte size requested.  */
293                 thread_ptr -> tx_thread_suspend_info =  memory_size; // 需要申请的内存大小
294 
295 #ifndef TX_NOT_INTERRUPTABLE
296 
297                 /* Increment the suspension sequence number, which is used to identify
298                    this suspension event.  */
299                 thread_ptr -> tx_thread_suspension_sequence++;
300 #endif
301 
302                 /* Pickup the number of suspended threads.  */
303                 suspended_count =  pool_ptr -> tx_byte_pool_suspended_count; // 后续代码跟block阻塞挂起时原理一样
304 
305                 /* Increment the suspension count.  */
306                 (pool_ptr -> tx_byte_pool_suspended_count)++;
307             
308                 /* Setup suspension list.  */
309                 if (suspended_count == TX_NO_SUSPENSIONS)
310                 
311 
312                     /* No other threads are suspended.  Setup the head pointer and
313                        just setup this threads pointers to itself.  */
314                     pool_ptr -> tx_byte_pool_suspension_list =      thread_ptr;
315                     thread_ptr -> tx_thread_suspended_next =        thread_ptr;
316                     thread_ptr -> tx_thread_suspended_previous =    thread_ptr;
317                 
318                 else
319                 
320 
321                     /* This list is not NULL, add current thread to the end. */
322                     next_thread =                                   pool_ptr -> tx_byte_pool_suspension_list;
323                     thread_ptr -> tx_thread_suspended_next =        next_thread;
324                     previous_thread =                               next_thread -> tx_thread_suspended_previous;
325                     thread_ptr -> tx_thread_suspended_previous =    previous_thread;
326                     previous_thread -> tx_thread_suspended_next =   thread_ptr;
327                     next_thread -> tx_thread_suspended_previous =   thread_ptr;
328                 
329 
330                 /* Set the state to suspended.  */
331                 thread_ptr -> tx_thread_state =       TX_BYTE_MEMORY;
332 
333 #ifdef TX_NOT_INTERRUPTABLE
334 
335                 /* Call actual non-interruptable thread suspension routine.  */
336                 _tx_thread_system_ni_suspend(thread_ptr, wait_option);
337 
338                 /* Restore interrupts.  */
339                 TX_RESTORE
340 #else
341 
342                 /* Set the suspending flag.  */
343                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
344 
345                 /* Setup the timeout period.  */
346                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  wait_option;
347 
348                 /* Temporarily disable preemption.  */
349                 _tx_thread_preempt_disable++;
350 
351                 /* Restore interrupts.  */
352                 TX_RESTORE
353 
354                 /* Call actual thread suspension routine.  */
355                 _tx_thread_system_suspend(thread_ptr); // 挂起当前线程
356 #endif
357 
358 #ifdef TX_ENABLE_EVENT_TRACE
359 
360                 /* Check that the event time stamp is unchanged.  A different
361                    timestamp means that a later event wrote over the byte
362                    allocate event.  In that case, do nothing here.  */
363                 if (entry_ptr != TX_NULL)
364                 
365 
366                     /* Is the timestamp the same?  */
367                     if (time_stamp == entry_ptr -> tx_trace_buffer_entry_time_stamp)
368                     
369                 
370                         /* Timestamp is the same, update the entry with the address.  */
371 #ifdef TX_MISRA_ENABLE
372                         entry_ptr -> tx_trace_buffer_entry_info_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
373 #else
374                        entry_ptr -> tx_trace_buffer_entry_information_field_2 =  TX_POINTER_TO_ULONG_CONVERT(*memory_ptr);
375 #endif
376                     
377                 
378 #endif
379 
380 #ifdef TX_ENABLE_EVENT_LOGGING
381                 /* Check that the event time stamp is unchanged.  A different
382                    timestamp means that a later event wrote over the byte
383                    allocate event.  In that case, do nothing here.  */
384                 if (lower_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_LOWER_OFFSET)) &&
385                     upper_tbu ==  *((ULONG *) (log_entry_ptr + TX_EL_EVENT_TIME_UPPER_OFFSET)))
386                 
387 
388                     /* Store the address of the allocated fragment.  */
389                     *((ULONG *) (log_entry_ptr + TX_EL_EVENT_INFO_4_OFFSET)) =  (ULONG) *memory_ptr;
390                 
391 #endif
392 
393                 /* Return the completion status.  */
394                 status =  thread_ptr -> tx_thread_suspend_status; // 设置返回状态
395             
396         
397         else
398         
399     
400             /* Restore interrupts.  */
401             TX_RESTORE
402 
403             /* Immediate return, return error completion.  */
404             status =  TX_NO_MEMORY;
405         
406     
407 
408     /* Return completion status.  */
409     return(status);
410 

2.3、byte空闲内存块查找_tx_byte_pool_search

ThreadX内核释放byte内存的时候,正常情况下只是将内存块标记为空闲,并不会立即合并空闲内存块,合并空闲内存块是在查找空闲内存块的_tx_byte_pool_search里面合并的,_tx_byte_pool_search在查找空闲内存块的时候,如果空闲内存块大小不满足申请的内存大小,那么检查下一个内存块是否空闲,如果空闲的话就合并下一个空闲内存块,如果满足申请内存的大小,就不检查下一个内存块是否可以合并,否则会浪费时间,影响效率。

_tx_byte_pool_search实现比较简单,基本就是找第一个满足申请内存大小的内存块:

  • 空闲块大小不够的话,检查是否可以合并下一个内存块(如果下一个空闲的话),内存块链表在pool初始化的时候就是非空闲的,所以最后一个内存块不会也不可能和第一个内存块合并,这样就减少了判断最后一个内存块的操作,代码逻辑就认为前一个内存块和后一个内存地址都是连续的;
  • 空闲内存块大小过大的话,拆分成两个内存块,第一个就是申请的内存,第二个就是新的空闲内存块。

_tx_byte_pool_search实现代码如下:

084 UCHAR  *_tx_byte_pool_search(TX_BYTE_POOL *pool_ptr, ULONG memory_size)
085 
086 
087 TX_INTERRUPT_SAVE_AREA
088 
089 UCHAR           *current_ptr;
090 UCHAR           *next_ptr;
091 UCHAR           **this_block_link_ptr;
092 UCHAR           **next_block_link_ptr;
093 ULONG           available_bytes;
094 UINT            examine_blocks;
095 UINT            first_free_block_found =  TX_FALSE;
096 TX_THREAD       *thread_ptr;
097 ALIGN_TYPE      *free_ptr;
098 UCHAR           *work_ptr;
099 
100 
101     /* Disable interrupts.  */
102     TX_DISABLE
103 
104     /* First, determine if there are enough bytes in the pool.  */
105     if (memory_size >= pool_ptr -> tx_byte_pool_available) // 申请的内存大于等于所有可以用的内存大小(还要预留部分内存控制块,所以内存不够,返回null)
106     
107 
108         /* Restore interrupts.  */
109         TX_RESTORE
110 
111         /* Not enough memory, return a NULL pointer.  */
112         current_ptr =  TX_NULL;
113     
114     else // 总的可用内存满足申请的内存大小(可能存在满足大小的连续内存块,需要查找一下)
115     
116 
117         /* Pickup thread pointer.  */
118         TX_THREAD_GET_CURRENT(thread_ptr)
119 
120         /* Setup ownership of the byte pool.  */
121         pool_ptr -> tx_byte_pool_owner =  thread_ptr; // 记录正在操作pool的线程,后面会开中断,如果开中断后,没有线程对pool操作,也就是pool没有被改变,那么可以接着之前的查找继续,否则得重新查找(也就是在多线程同时申请内存的时候效率比较低,查找空闲内存也不能阻塞高优先级线程申请内存,如果有高优先级线程申请内存,那么高优先级线程应该先获取内存)
122 
123         /* Walk through the memory pool in search for a large enough block.  */
124         current_ptr =      pool_ptr -> tx_byte_pool_search; // 从tx_byte_pool_search开始查找空闲内存
125         examine_blocks =   pool_ptr -> tx_byte_pool_fragments + ((UINT) 1); // tx_byte_pool_fragments内存碎片数量,也就是有多少个内存块(加1???多检查一次也不影响,暂时不考虑什么情况存有多检查一次的必要)
126         available_bytes =  ((ULONG) 0);
127         do
128         
129 
130 
131 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
132 
133             /* Increment the total fragment search counter.  */
134             _tx_byte_pool_performance_search_count++;
135 
136             /* Increment the number of fragments searched on this pool.  */
137             pool_ptr -> tx_byte_pool_performance_search_count++;
138 #endif
139 
140             /* Check to see if this block is free.  */
141             work_ptr =  TX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *))); // 内存块加上UCHAR *大小的地址(current_ptr最前面保存的是下一个内存块的地址)
142             free_ptr =  TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr); // work_ptr指针转换为ALIGN_TYPE类型指针
143             if ((*free_ptr) == TX_BYTE_BLOCK_FREE) // 检查是否是空闲内存
144             
145 
146                 /* Determine if this is the first free block.  */
147                 if (first_free_block_found == TX_FALSE) // 没有找到过空闲块时,first_free_block_found为TX_FALSE,如果找到了空闲块,first_free_block_found为TX_TRUE(内核要记录第一个找到的空闲块,避免每次都从非空闲块开始查找空闲内存)
148                 
149 
150                     /* This is the first free block.  */
151                     pool_ptr->tx_byte_pool_search =  current_ptr; // 记录第一次找到的空闲内存块
152                 
153                     /* Set the flag to indicate we have found the first free
154                        block.  */
155                     first_free_block_found =  TX_TRUE; // 已经找到了第一个空闲内存块
156                 
157 
158                 /* Block is free, see if it is large enough.  */
159 
160                 /* Pickup the next block's pointer.  */
161                 this_block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr); // 空闲内存块的最前面记录的是下一个相邻内存块的地址
162                 next_ptr =             *this_block_link_ptr; // 获取下一个内存块的地址
163 
164                 /* Calculate the number of bytes available in this block.  */
165                 available_bytes =   TX_UCHAR_POINTER_DIF(next_ptr, current_ptr); // 下一个内存块的地址减去当前内存块的地址就是当前内存块的大小
166                 available_bytes =   available_bytes - ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))); // 当前内存块大小减去一个指针以及块空闲类型的标志就是当前内存块可以返回给应用程序的可用内存大小
167 
168                 /* If this is large enough, we are done because our first-fit algorithm
169                    has been satisfied!  */
170                 if (available_bytes >= memory_size) // 可用内存大小大于等于申请的内存,那么从当前内存块申请内存给应用程序即可
171                 
172                     /* Get out of the search loop!  */
173                     break;
174                 
175                 else // 内存块可用内存大小不够,检查下一个相邻的内存块是否空闲,是否可以合并
176                 
177 
178                     /* Clear the available bytes variable.  */
179                     available_bytes =  ((ULONG) 0);
180 
181                     /* Not enough memory, check to see if the neighbor is 
182                        free and can be merged.  */
183                     work_ptr =  TX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *))); // 获取下一个相邻的内存块
184                     free_ptr =  TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr); // 内存块空闲类型
185                     if ((*free_ptr) == TX_BYTE_BLOCK_FREE) // 内存块空闲
186                     
187 
188                         /* Yes, neighbor block can be merged!  This is quickly accomplished
189                            by updating the current block with the next blocks pointer.  */
190                         next_block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr); // 获取再下下一个内存块地址(两个内存块地址相减才能知道内存块的大小,内存控制块没有记录内存大小,只记录相邻的下一个内存块的地址)
191                         *this_block_link_ptr =  *next_block_link_ptr; // 当前内存块current_ptr的下一个内存块地址指向下一个内存块next_ptr的下一个内存块(next_ptr内存被合并了)
192 
193                         /* Reduce the fragment total.  We don't need to increase the bytes
194                            available because all free headers are also included in the available
195                            count.  */
196                         pool_ptr -> tx_byte_pool_fragments--; // 当前内存块与相邻的下一个内存块合并,内存碎片数减1
197 
198 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
199 
200                         /* Increment the total merge counter.  */
201                         _tx_byte_pool_performance_merge_count++;
202 
203                         /* Increment the number of blocks merged on this pool.  */
204                         pool_ptr -> tx_byte_pool_performance_merge_count++;
205 #endif
206 
207                         /* See if the search pointer is affected.  */
208                         if (pool_ptr -> tx_byte_pool_search ==  next_ptr) // 如果tx_byte_pool_search指向被合并的下一个空闲内存块,那么更新tx_byte_pool_search指向合并后的空闲内存块(旧的内存块不存在了)
209                         
210                     
211                             /* Yes, update the search pointer.   */
212                             pool_ptr -> tx_byte_pool_search =  current_ptr;
213                         
214                     
215                     else // 下一个内存块不空闲
216                     
217 
218                         /* Neighbor is not free so we can skip over it!  */
219                         next_block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr);
220                         current_ptr =  *next_block_link_ptr; // current_ptr指向下下一个内存块,下一个非空闲内存块
221 
222                         /* Decrement the examined block count to account for this one.  */
223                         if (examine_blocks != ((UINT) 0)) // examine_blocks不为0,要检查的内存块数量减1(next_ptr检查过了,current_ptr的examine_blocks减1在后面)
224                         
225 
226                             examine_blocks--;
227 
228 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
229 
230                             /* Increment the total fragment search counter.  */
231                             _tx_byte_pool_performance_search_count++;
232 
233                             /* Increment the number of fragments searched on this pool.  */
234                             pool_ptr -> tx_byte_pool_performance_search_count++;
235 #endif
236                         
237                     
238                 
239             
240             else // 当前内存块不是空闲内存块,更新current_ptr指向下一个内存块,检查下一个内存块
241             
242 
243                 /* Block is not free, move to next block.  */
244                 this_block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
245                 current_ptr =  *this_block_link_ptr;
246             
247 
248             /* Another block has been searched... decrement counter.  */
249             if (examine_blocks != ((UINT) 0)) // current_ptr检查过了,examine_blocks减1
250             
251 
252                 examine_blocks--;
253             
254 
255             /* Restore interrupts temporarily.  */
256             TX_RESTORE // 允许中断,这个检查空闲内存块耗费了一定时间,不能阻塞中断
257 
258             /* Disable interrupts.  */
259             TX_DISABLE // 再次关闭中断
260 
261             /* Determine if anything has changed in terms of pool ownership.  */
262             if (pool_ptr -> tx_byte_pool_owner != thread_ptr) // 在检查下一个内存块前,检查一下pool的owner是不是当前线程,如果不是当前线程,那么pool的内存可能就被其他线程修改了,current_ptr可能被合并无效了,要检查的内存碎片数量有不一样了,所以又得重新查找空闲内存块
263             
264 
265                 /* Pool changed ownership in the brief period interrupts were
266                    enabled.  Reset the search.  */
267                 current_ptr =      pool_ptr -> tx_byte_pool_search; // 重新获取空闲内存查找的起始内存块(tx_byte_pool_search一般指向一个空闲内存块,内存申请的时候,如果该内存块被本次申请了,那么tx_byte_pool_search就指向下一个内存块(不一定是空闲的);内存释放的时候,如果释放的内存块地址小于tx_byte_pool_search,那么更新tx_byte_pool_search指向刚释放的内存块地址,也就是ThreadX内核尽量从内存块链表前面的内存块分配内存,这样就会优先拆分前面的空闲内存块,后面的内存块可能就比较大,尽量避免过多非连续的小内存碎片的产生)
268                 examine_blo

以上是关于ThreadX内核源码分析 - 动态内存管理的主要内容,如果未能解决你的问题,请参考以下文章

ThreadX内核源码分析 - 线程同步之互斥锁及动态优先级

ThreadX内核源码分析(SMP) - 线程多核映射

ThreadX内核源码分析 - 计数信号量

ThreadX内核源码分析(SMP) - 线程执行核remap

ThreadX内核源码分析 - 优先级及抢占阈值抢占(arm)

ThreadX内核源码分析 - 优先级及抢占阈值抢占(arm)