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) - 线程执行核remap