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_blocks =   pool_ptr -> tx_byte_pool_fragments + ((UINT) 1);
269 
270                 /* Setup our ownership again.  */
271                 pool_ptr -> tx_byte_pool_owner =  thread_ptr;
272             }
273         } while(examine_blocks != ((UINT) 0)); // 如果所有内存块都检查完了还没找到可用空闲内存块,那么退出循环
274 
275         /* Determine if a block was found.  If so, determine if it needs to be
276            split.  */
277         if (available_bytes != ((ULONG) 0)) // available_bytes不为0,表示找到了一个满足申请大小的内存块
278         {
279 
280             /* Determine if we need to split this block.  */
281             if ((available_bytes - memory_size) >= ((ULONG) TX_BYTE_BLOCK_MIN)) // 可用内存减去申请的内存还大于等于TX_BYTE_BLOCK_MIN,也就是将当前内存块拆分后,剩余的内存还够一个空闲内存块(内核不拆分成太小的内存块,太小的内存块分配管理起来效率太低,干脆就把多一点点的内存块给应用程序即可,应用程序也不会访问多给的内存)
282             {
283 
284                 /* Split the block.  */ // 拆分当前内存块
285                 next_ptr =  TX_UCHAR_POINTER_ADD(current_ptr, (memory_size + ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))))); // "下一个内存块地址指针、内存块释放空闲字段 + 申请的内存大小"这么多作为一个新的内存块,里面的可用内存返回给应用程序;接下来的内存划分成一个新的空闲内存块,next_ptr即指向新的空闲内存块的地址
286 
287                 /* Setup the new free block.  */
288                 next_block_link_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(next_ptr); // 指针转换,没有改变值
289                 this_block_link_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr); // 指针转换,没有改变值(current_ptr拆分前的下一个内存块地址,也就是拆分后的next_ptr内存块的下一个内存块地址)
290                 *next_block_link_ptr =  *this_block_link_ptr; // next_ptr的下一个内存块地址,指向拆分前的current_ptr的下一个内存块地址
291                 work_ptr =              TX_UCHAR_POINTER_ADD(next_ptr, (sizeof(UCHAR *))); // next_ptr + sizeof(UCHAR *)指向标记内存块是否空闲的地址
292                 free_ptr =              TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(work_ptr);
293                 *free_ptr =             TX_BYTE_BLOCK_FREE; // next_ptr设置为空闲
294 
295                 /* Increase the total fragment counter.  */
296                 pool_ptr -> tx_byte_pool_fragments++;
297 
298                 /* Update the current pointer to point at the newly created block.  */
299                 *this_block_link_ptr =  next_ptr; // current_ptr的下一个内存块地址指向拆分出来的next_ptr内存块地址
300     
301                 /* Set available equal to memory size for subsequent calculation.  */
302                 available_bytes =  memory_size; // available_bytes等于拆分后申请到的内存块的可用内存大小
303 
304 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
305 
306                 /* Increment the total split counter.  */
307                 _tx_byte_pool_performance_split_count++;
308 
309                 /* Increment the number of blocks split on this pool.  */
310                 pool_ptr -> tx_byte_pool_performance_split_count++;
311 #endif
312             }
313 
314             /* In any case, mark the current block as allocated.  */
315             work_ptr =              TX_UCHAR_POINTER_ADD(current_ptr, (sizeof(UCHAR *)));
316             this_block_link_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
317             *this_block_link_ptr =  TX_BYTE_POOL_TO_UCHAR_POINTER_CONVERT(pool_ptr); // 该地址指向pool地址(与标记内存释放空闲的标志占用同一个内存,pool_ptr不会等于空闲内存块标记,所以可以存pool指针,否则可能造成混乱;复用内存,这样就节省了一个内存空间)
318 
319             /* Reduce the number of available bytes in the pool.  */
320             pool_ptr -> tx_byte_pool_available =  (pool_ptr -> tx_byte_pool_available - available_bytes) - ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))); // pool的可用内存空间减去申请出去的内存块大小
321 
322             /* Determine if the search pointer needs to be updated. This is only done
323                if the search pointer matches the block to be returned.  */
324             if (current_ptr == pool_ptr -> tx_byte_pool_search) // 更新tx_byte_pool_search指向当前内存块的下一个内存块(从tx_byte_pool_search开始查找空闲内存),tx_byte_pool_search指向的内存块已经被申请了,下次不要再从这里开始查找,免得浪费时间(更新的时候不保证下一个内存块可用,所以前面的first_free_block_found也就是在查找过程中更新tx_byte_pool_search指向第一个空闲内存块)
325             {
326 
327                 /* Yes, update the search pointer to the next block.  */
328                 this_block_link_ptr =   TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(current_ptr);
329                 pool_ptr -> tx_byte_pool_search =  *this_block_link_ptr; // 更新tx_byte_pool_search,tx_byte_pool_search指向的内存块已经被申请了,直接指向下一个内存块即可,不管下一个内存块是否空闲,由下次申请内存的线程来更新tx_byte_pool_search
330             }
331 
332             /* Restore interrupts.  */
333             TX_RESTORE
334 
335             /* Adjust the pointer for the application.  */
336             current_ptr =  TX_UCHAR_POINTER_ADD(current_ptr, (((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE))))); // 修正current_ptr,修正前current_ptr包含了pool指针、下一个内存块的地址,跳过这些才是返回给应用程序的可用内存地址,也就是c语言malloc返回的地址
337         }
338         else // 查找完了所以内存块也没找到合适的空闲内存,返回空指针给上一级函数
339         {
340 
341             /* Restore interrupts.  */
342             TX_RESTORE
343 
344             /* Set current pointer to NULL to indicate nothing was found.  */
345             current_ptr =  TX_NULL;
346         }
347     }
348 
349     /* Return the search pointer.  */
350     return(current_ptr);
351 }

2.4、byte内存释放_tx_byte_release

释放内存过程比较简单,与申请内存有很多重复的地方:

  • 检查释放内存是否是byte pool里面的内存,不是的话需要返回错误;
  • 释放内存,将内存块标记为空闲,更新pool的可用内存数量;如果释放的内存地址小于下次查找空闲的内存地址tx_byte_pool_search,更新tx_byte_pool_search指向被释放的空闲内存,使tx_byte_pool_search尽量从低地址空闲内存块开始查找,优先拆分链表前面的空闲内存块;
  • 如果有线程等待内存,给表头线程分配内存,分配不到内存就跳出循环并返回,分配失败就把内存重新变成空闲状态(等待内存的线程链表有更新,分配内存的线程可能不在等待内存了),重新给等待内存的线程链表表头线程分配内存,如果分配成功,则将线程从等待链表删除并唤醒线程,重新给等待内存的线程链表表头线程分配内存,直到没有找到满足表头线程内存大小的空闲内存(不继续给后续线程分配内存,优先给先申请内存的线程分配内存,如果给后续线程分配了内存,空闲内存块变得越来越小越来越少,先申请内存的线程可能更申请不到内存,这样不太公平)

_tx_byte_release释放内存,给等待内存的线程分配内存的实现代码如下:

077 UINT  _tx_byte_release(VOID *memory_ptr)
078 {
079 
080 TX_INTERRUPT_SAVE_AREA
081 
082 UINT                status;
083 TX_BYTE_POOL        *pool_ptr;
084 TX_THREAD           *thread_ptr;
085 UCHAR               *work_ptr;
086 UCHAR               *temp_ptr;
087 UCHAR               *next_block_ptr;
088 TX_THREAD           *susp_thread_ptr;
089 UINT                suspended_count;
090 TX_THREAD           *next_thread;
091 TX_THREAD           *previous_thread;
092 ULONG               memory_size;
093 ALIGN_TYPE          *free_ptr;
094 TX_BYTE_POOL        **byte_pool_ptr;
095 UCHAR               **block_link_ptr;
096 UCHAR               **suspend_info_ptr;
097 
098 
099     /* Default to successful status.  */
100     status =  TX_SUCCESS;
101     
102     /* Set the pool pointer to NULL.  */
103     pool_ptr =  TX_NULL;
104 
105     /* Lockout interrupts.  */
106     TX_DISABLE
107 
108     /* Determine if the memory pointer is valid.  */
109     work_ptr =  TX_VOID_TO_UCHAR_POINTER_CONVERT(memory_ptr);
110     if (work_ptr != TX_NULL) // 检查释放的内存是否为空指针(if分支主要获取memory_ptr所在的pool)
111     {
112         
113         /* Back off the memory pointer to pickup its header.  */
114         work_ptr =  TX_UCHAR_POINTER_SUB(work_ptr, ((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))); // 获取内存块的起始地址
115 
116         /* There is a pointer, pickup the pool pointer address.  */
117         temp_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *))); // 内存块起始地址加上下一个内存地址大小就是pool指针/free标志所在内存
118         free_ptr =  TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
119         if ((*free_ptr) != TX_BYTE_BLOCK_FREE) // 检查内存是否是空闲内存,如果重复释放内存,返回TX_PTR_ERROR
120         {
121 
122             /* Pickup the pool pointer.  */
123             temp_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *))); // 获取pool指针所在内存地址,内存块结构为"|下一个内存块地址|pool指针/free标志|memory_ptr|"
124             byte_pool_ptr =  TX_UCHAR_TO_INDIRECT_BYTE_POOL_POINTER(temp_ptr); // 指针转换
125             pool_ptr =  *byte_pool_ptr; // 获取pool指针
126 
127             /* See if we have a valid pool pointer.  */
128             if (pool_ptr == TX_NULL) // pool为空,那么memory_ptr不是pool里面的内存块,使用了错误的释放函数
129             {
130                 
131                 /* Return pointer error.  */
132                 status =  TX_PTR_ERROR; // 返回错误
133             }
134             else // pool指针不为空,需要再次检查是否是真正的pool
135             {
136 
137                 /* See if we have a valid pool.  */
138                 if (pool_ptr -> tx_byte_pool_id != TX_BYTE_POOL_ID) // pool id对不上,pool指针不是真正指向有效的pool,返回错误
139                 {
140                 
141                     /* Return pointer error.  */
142                     status =  TX_PTR_ERROR;
143                     
144                     /* Reset the pool pointer is NULL.  */
145                     pool_ptr =  TX_NULL;
146                 }
147             }
148         }
149         else
150         {
151 
152             /* Return pointer error.  */
153             status =  TX_PTR_ERROR;
154         }
155     }
156     else
157     {
158 
159         /* Return pointer error.  */
160         status =  TX_PTR_ERROR;
161     }
162 
163     /* Determine if the pointer is valid.  */
164     if (pool_ptr == TX_NULL) // 没有找到memory_ptr所在的有效pool,memory_ptr不是从pool申请的,返回错误
165     {
166     
167         /* Restore interrupts.  */
168         TX_RESTORE
169     }
170     else // 释放memory_ptr
171     {
172     
173         /* At this point, we know that the pointer is valid.  */
174 
175         /* Pickup thread pointer.  */
176         TX_THREAD_GET_CURRENT(thread_ptr)
177 
178         /* Indicate that this thread is the current owner.  */
179         pool_ptr -> tx_byte_pool_owner =  thread_ptr; // 标记当前线程正在操作pool(与申请内存作用一样)
180 
181 #ifdef TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO
182 
183         /* Increment the total release counter.  */
184         _tx_byte_pool_performance_release_count++;
185 
186         /* Increment the number of releases on this pool.  */
187         pool_ptr -> tx_byte_pool_performance_release_count++;
188 #endif
189 
190         /* If trace is enabled, insert this event into the trace buffer.  */
191         TX_TRACE_IN_LINE_INSERT(TX_TRACE_BYTE_RELEASE, pool_ptr, TX_POINTER_TO_ULONG_CONVERT(memory_ptr), pool_ptr -> tx_byte_pool_suspended_count, pool_ptr -> tx_byte_pool_available, TX_TRACE_BYTE_POOL_EVENTS)
192 
193         /* Log this kernel call.  */
194         TX_EL_BYTE_RELEASE_INSERT
195 
196         /* Release the memory.  */
197         temp_ptr =   TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
198         free_ptr =   TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
199         *free_ptr =  TX_BYTE_BLOCK_FREE; // 将内存块标记为空闲
200 
201         /* Update the number of available bytes in the pool.  */
202         block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
203         next_block_ptr =  *block_link_ptr;
204         pool_ptr -> tx_byte_pool_available =  
205             pool_ptr -> tx_byte_pool_available + TX_UCHAR_POINTER_DIF(next_block_ptr, work_ptr); // "TX_UCHAR_POINTER_DIF(next_block_ptr, work_ptr)"当前内存块与下一个内存块之间的距离(包括下一个内存块地址指针等字段)作为当前空闲内存块的大小,当前释放的空闲内存块可能与相邻的空闲内存块合并,下一个内存地址等字段可能变成有效内存
206 
207         /* Determine if the free block is prior to current search pointer.  */
208         if (work_ptr < (pool_ptr -> tx_byte_pool_search)) // 释放的空闲内存块地址小于下一次查找空闲内存块的起始地址,更新tx_byte_pool_search指向当前释放的空闲内存块;ThreadX尽量从低地址开始分配内存,这样位置越后的空闲内存块被拆分的机会就更少,也就是内存块就越大,有大内存申请的时候才可能获取到大内存;tx_byte_pool_search也不指向内存块表头,这样影响查找效率,所以内核是尽可能从前面的空闲内存块开始查找
209         {
210 
211             /* Yes, update the search pointer to the released block.  */
212             pool_ptr -> tx_byte_pool_search =  work_ptr; // 更新tx_byte_pool_search
213         }
214 
215         /* Determine if there are threads suspended on this byte pool.  */
216         if (pool_ptr -> tx_byte_pool_suspended_count != TX_NO_SUSPENSIONS) // 有线程在等待内存
217         {
218                 
219             /* Now examine the suspension list to find threads waiting for 
220                memory.  Maybe it is now available!  */
221             while (pool_ptr -> tx_byte_pool_suspended_count != TX_NO_SUSPENSIONS) // 循环给表头线程分配内存(表头线程分配到内存后,下一个等待内存的线程将成为新的表头,循环过程就是一次给等待线程分配内存,如果分配不到就退出循环,不给后续等待线程分配内存)
222             {
223 
224                 /* Pickup the first suspended thread pointer.  */
225                 susp_thread_ptr =  pool_ptr -> tx_byte_pool_suspension_list; // 等待内存的线程链表
226 
227                 /* Pickup the size of the memory the thread is requesting.  */
228                 memory_size =  susp_thread_ptr -> tx_thread_suspend_info; // 等待内存的大小(也就是malloc的内存大小)
229 
230                 /* Restore interrupts.  */
231                 TX_RESTORE // 恢复中断,已经获取到了一个等待内存线程的数据,先试图给这个线程分配内存,别的线程还可以继续申请内存(tx_byte_pool_suspension_list是所有申请内存的线程共同访问的,关闭中断将导致其他线程不能申请内存)
232 
233                 /* See if the request can be satisfied.  */
234                 work_ptr =  _tx_byte_pool_search(pool_ptr, memory_size); // 与申请内存时一样,查找memory_size的空闲内存块
235 
236                 /* Optional processing extension.  */
237                 TX_BYTE_RELEASE_EXTENSION
238 
239                 /* Disable interrupts.  */
240                 TX_DISABLE
241 
242                 /* Indicate that this thread is the current owner.  */
243                 pool_ptr -> tx_byte_pool_owner =  thread_ptr;
244 
245                 /* If there is not enough memory, break this loop!  */
246                 if (work_ptr == TX_NULL) // 没有找到可用内存块(这里不用判断是否有其他线程也操作了内存,如果别的线程释放了内存,那么也会试图给阻塞线程分配内存)
247                 {
248           
249                   /* Break out of the loop.  */
250                     break; // 退出循环,不继续分配内存(从这里看,ThreadX内核分配内存是优先给等待链表前面的线程分配内存,如果前面的线程都分配不到内存,那么就不给后面等待内存线程分配内存,即使内存够大)
251                 }
252 
253                 /* Check to make sure the thread is still suspended.  */
254                 if (susp_thread_ptr ==  pool_ptr -> tx_byte_pool_suspension_list) // 再次检查susp_thread_ptr是否在阻塞链表里面(新的等待内存的线程会挂到等待链表末尾,不会改变表头,只有等待内存的线程超时,被从等待链表删除时,表头才可能变成其他线程)
255                 {
256 
257                     /* Also, makes sure the memory size is the same.  */
258                     if (susp_thread_ptr -> tx_thread_suspend_info == memory_size) // 再次检查内存大小是否是刚才查找内存块的大小(存在等待内存的线程超时后再次加入等待链表的情况,也就是查找过程所有等待内存的线程都超时或者其他情况导致等待内存的线程链表都清空了,当前线程还没检测到,然后之前等待内存链表表头的线程又再次申请内存并且阻塞了,那么该线程又回到了阻塞链表的表头)
259                     {
260                   
261                         /* Remove the suspended thread from the list.  */
262 
263                         /* Decrement the number of threads suspended.  */
264                         pool_ptr -> tx_byte_pool_suspended_count--; // 等待内存的线程数量减1
265 
266                         /* Pickup the suspended count.  */
267                         suspended_count =  pool_ptr -> tx_byte_pool_suspended_count;
268 
269                         /* See if this is the only suspended thread on the list.  */
270                         if (suspended_count == TX_NO_SUSPENSIONS) // 如果没有其他线程等待内存,清空等待链表
271                         {
272 
273                             /* Yes, the only suspended thread.  */
274 
275                             /* Update the head pointer.  */
276                             pool_ptr -> tx_byte_pool_suspension_list =  TX_NULL; // 清空等待链表
277                         }
278                         else // 否则从等待链表删除当前分配内存的线程(表头线程)
279                         {
280 
281                             /* At least one more thread is on the same expiration list.  */
282 
283                             /* Update the list head pointer.  */
284                             next_thread =                                susp_thread_ptr -> tx_thread_suspended_next;
285                             pool_ptr -> tx_byte_pool_suspension_list =   next_thread;
286 
287                             /* Update the links of the adjacent threads.  */
288                             previous_thread =                              susp_thread_ptr -> tx_thread_suspended_previous;
289                             next_thread -> tx_thread_suspended_previous =  previous_thread;
290                             previous_thread -> tx_thread_suspended_next =  next_thread;
291                         }
292 
293                         /* Prepare for resumption of the thread.  */
294 
295                         /* Clear cleanup routine to avoid timeout.  */
296                         susp_thread_ptr -> tx_thread_suspend_cleanup =  TX_NULL; // 清空tx_thread_suspend_cleanup,线程已经获取到内存了(这里还没去激活超时定时器,清空tx_thread_suspend_cleanup,即使定时器超时也不会有回调函数调用)
297 
298                         /* Return this block pointer to the suspended thread waiting for
299                            a block.  */
300                         suspend_info_ptr =   TX_VOID_TO_INDIRECT_UCHAR_POINTER_CONVERT(susp_thread_ptr -> tx_thread_additional_suspend_info); // 申请内存时的memory_ptr指针
301                         *suspend_info_ptr =  work_ptr; // 设置memory_ptr,将work_ptr内存分配给线程
302 
303                         /* Clear the memory pointer to indicate that it was given to the suspended thread.  */
304                         work_ptr =  TX_NULL; // 清空work_ptr(指示该内存已经被分配了,后面会用到work_ptr,如果等待内存链表表头变了或者内存大小不一致,那么需要将该内存块重新设置为空闲内存)
305                         
306                         /* Put return status into the thread control block.  */
307                         susp_thread_ptr -> tx_thread_suspend_status =  TX_SUCCESS; // 线程返回状态(阻塞线程被唤醒后,使用这个作为获取内存的结果返回给上一级函数)
308 
309 #ifdef TX_NOT_INTERRUPTABLE
310 
311                         /* Resume the thread!  */
312                         _tx_thread_system_ni_resume(susp_thread_ptr);
313 
314                         /* Restore interrupts.  */
315                         TX_RESTORE
316 #else
317                         /* Temporarily disable preemption.  */
318                         _tx_thread_preempt_disable++;
319 
320                         /* Restore interrupts.  */
321                         TX_RESTORE
322 
323                         /* Resume thread.  */
324                         _tx_thread_system_resume(susp_thread_ptr); // 唤醒已经分配到内存的线程
325 #endif
326 
327                         /* Lockout interrupts.  */
328                         TX_DISABLE
329                     }
330                 }
331                     
332                 /* Determine if the memory was given to the suspended thread.  */
333                 if (work_ptr != TX_NULL) // 等待内存的表头线程被改变了(内存没有分配成功,需要将内存重新变成空闲状态)
334                 {
335                 
336                     /* No, it wasn't given to the suspended thread.  */
337 
338                     /* Put the memory back on the available list since this thread is no longer
339                        suspended.  */
340                     work_ptr =  TX_UCHAR_POINTER_SUB(work_ptr, (((sizeof(UCHAR *)) + (sizeof(ALIGN_TYPE)))));
341                     temp_ptr =  TX_UCHAR_POINTER_ADD(work_ptr, (sizeof(UCHAR *)));
342                     free_ptr =  TX_UCHAR_TO_ALIGN_TYPE_POINTER_CONVERT(temp_ptr);
343                     *free_ptr =  TX_BYTE_BLOCK_FREE;
344 
345                     /* Update the number of available bytes in the pool.  */
346                     block_link_ptr =  TX_UCHAR_TO_INDIRECT_UCHAR_POINTER_CONVERT(work_ptr);
347                     next_block_ptr =  *block_link_ptr;
348                     pool_ptr -> tx_byte_pool_available =  
349                         pool_ptr -> tx_byte_pool_available + TX_UCHAR_POINTER_DIF(next_block_ptr, work_ptr);
350 
351                     /* Determine if the current pointer is before the search pointer.  */
352                     if (work_ptr < (pool_ptr -> tx_byte_pool_search))
353                     {
354 
355                         /* Yes, update the search pointer.  */
356                         pool_ptr -> tx_byte_pool_search =  work_ptr; // work_ptr重新释放,更新tx_byte_pool_search
357                     }
358                 }
359             }
360             
361             /* Restore interrupts.  */
362             TX_RESTORE
363 
364             /* Check for preemption.  */
365             _tx_thread_system_preempt_check();
366         }
367         else // 没有线程等待内存,释放内存块标记为空闲即可,返回上一级函数
368         {
369         
370             /* No, threads suspended, restore interrupts.  */
371             TX_RESTORE
372         }
373     }
374 
375     /* Return completion status.  */
376     return(status);
377 }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值