ThreadX内核源码分析 - 定时器及线程时间片调度(arm)

Posted arm7star

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadX内核源码分析 - 定时器及线程时间片调度(arm)相关的知识,希望对你有一定的参考价值。

1、线程时间片介绍(tx_thread_time_slice)

ThreadX内核同优先级线程之间是按时间片调度的,tx_thread_new_time_slice记录线程的时间片(一次调度的总的时间片),tx_thread_time_slice记录线程的剩余时间片(ThreadX内核每次调度线程时,并不是从tx_thread_new_time_slice,而是从上次换出cpu时的剩余时间片继续计时,只有当时间片用尽时,tx_thread_time_slice才会从tx_thread_new_time_slice开始);

ThreadX内核正在执行的线程在优先级链表表头,线程被切换出cpu时,线程并不会移动到链表末尾,如果内核线程执行还没多久就被高优先级线程抢占,把线程移动到链表末尾的话,该线程就要等很久才会得到调度,所以线程被抢占的话,线程仍在优先级链表的表头,下次该优先级成为最高优先级时,取表头线程也就是上次被切换出去的线程继续执行;另外,正是由于被换出去的线程仍在表头,如果线程以新的时间片执行的话,如果每次线程都没用尽时间片就被高优先级抢占,那么同优先级链表后面的线程就得不到调度,所以每次线程被调度都是接着上次没有用完的时间片tx_thread_time_slice继续计时,直到时间片用完才分配新的时间片tx_thread_new_time_slice(如果同优先级有其他线程就绪就将线程移动到末尾,调度下一个同优先级线程,否则接着执行当前线程)。

2、定时器中断

2.1、中断介绍

前一篇文章已经介绍过中断上下文保存、中断处理、中断上下文恢复相关内容,汇编代码具体实现参考:
ThreadX内核源码分析 - ports线程上下文相关代码分析(arm)_arm7star的博客-CSDN博客1、ports源码介绍内核与cpu相关的关键代码基本都是用汇编语言实现的,c语言可能实现不了或者不好编写。ThreadX官网针对ARM9 gcc的移植代码在threadx-6.1.2_rel\\ports\\arm9\\gnu\\src目录下,ThreadX文件命名规则基本是以该文件包含的函数名命名的(函数名多了一个"_"前缀,文件名里面没有"_"前缀),每个源文件通常只实现一个函数;ports代码目录如下:tx_thread_context_restore.S是_tx_thread_contexthttps://blog.csdn.net/arm7star/article/details/122930850?spm=1001.2014.3001.5502

IRQ中断处理顶层代码逻辑如下,__tx_irq_handler为IRQ中断处理函数入口,__tx_irq_handler主要就是保存必要的中断上下文,调用中断处理函数,恢复中断上下文(如果有高优先级唤醒,那么要将IRQ栈里面保存的寄存器以及其他没有修改的寄存器保存到被中断线程的栈里面,_tx_thread_context_save只保存了会影响到的必要的寄存器(保存在IRQ栈里面),因为中断返回时并不一定会切换线程,保存过多的寄存器反而影响性能;如果线程没有被抢占或者切换出去,那么恢复保存在IRQ栈里面的寄存器即可):

本文只用到了定时器中断,所以中断服务程序就直接是调用_tx_timer_interrupt;另外ThreadX官网的tx_timer_interrupt是针对一类cpu的,而定时器是处理器相关的,所以官网的tx_timer_interrupt仅是内核相关的,需要自己在tx_timer_interrupt合适的地方加上硬件中断清理工作,否则定时器中断将不断触发;

针对s3c2440的定时器中断清理代码如下,保存r0, lr寄存器(BL指令会修改lr寄存器;c函数r0-r3之外的寄存器如果有用到,编译器会保护及恢复的,所以调用中断清除C函数,通用寄存器只需要考虑r0-r3,_tx_timer_interrupt调用irq_ack之后的代码很显然没有用到r0-r3的旧的值,所以r0-r3是不需要保护的,但是仿照ThreadX的其他代码,为了让栈保持8 byte对齐,还是把r0保存到栈里面了),irq_ack就是c代码清除定时器中断:

2.2、定时器中断服务程序(_tx_timer_interrupt)

_tx_timer_interrupt定时器中断服务程序主要是对正在执行的线程的时间片减1,检查当前线程时间片是否用尽,检查当前是否有timer定时器超时(应用程序定时器,非硬件定时器,线程的sleep、阻塞超时等的唤醒都是通过定时器唤醒的,例如等待某个互斥锁,如果超时了就不继续等待,那么内核就启动了一个定时器,在超时时间内等到了互斥锁,那么就取消定时器,否则定时器超时就唤醒阻塞的线程,返回获取互斥锁失败);如果有定时器超时,调用_tx_timer_expiration_process处理超时定时器,如果线程时间片用尽,调用_tx_thread_time_slice处理时间片(有同优先级的其他线程的话需要调度下一个线程,否则不需要调度,重新设置当前线程的时间片即可,新一轮开始计时),_tx_timer_interrupt代码如下:

115 _tx_timer_interrupt:
116 @
117 @    /* IRQ acknowledge.  */
118 @    irq_ack();
119 @
120     STMDB   sp!, r0, lr                       @ Save the lr register on the stack
121     BL      irq_ack // 清除定时器中断
122     LDMIA   sp!, r0, lr                       @ Recover lr register
123 @
124 @    /* Upon entry to this routine, it is assumed that context save has already
125 @       been called, and therefore the compiler scratch registers are available
126 @       for use.  */
127 @
128 @    /* Increment the system clock.  */
129 @    _tx_timer_system_clock++;
130 @
131     LDR     r1, =_tx_timer_system_clock         @ Pickup address of system clock
132     LDR     r0, [r1]                            @ Pickup system clock
133     ADD     r0, r0, #1                          @ Increment system clock // _tx_timer_system_clock++,每次定时器中断,系统的时钟加1
134     STR     r0, [r1]                            @ Store new system clock
135 @
136 @    /* Test for time-slice expiration.  */
137 @    if (_tx_timer_time_slice)
138 @    
139 @
140     LDR     r3, =_tx_timer_time_slice           @ Pickup address of time-slice 
141     LDR     r2, [r3]                            @ Pickup time-slice
142     CMP     r2, #0                              @ Is it non-active?
143     BEQ     __tx_timer_no_time_slice            @ Yes, skip time-slice processing // 检查_tx_timer_time_slice是否激活,_tx_timer_time_slice不为0,表示线程使用了时间片,每次定时器中断要对线程时间片计时,_tx_timer_time_slice为0,表示线程没有使用时间片,不对线程运行时间计时,只有线程被抢占或者线程自己让出cpu,否则同优先级的其他就绪线程可能就得不到调度,一般不启用时间片的线程都会有某些阻塞调用
144 @
145 @       /* Decrement the time_slice.  */
146 @       _tx_timer_time_slice--;
147 @
148     SUB     r2, r2, #1                          @ Decrement the time-slice // 线程启用了时间片,对线程时间片减1(线程调度时,线程剩余时间片保存在_tx_timer_time_slice里面,线程被换出时,_tx_timer_time_slice保存到线程的剩余时间片tx_thread_time_slice里面)
149     STR     r2, [r3]                            @ Store new time-slice value
150 @
151 @       /* Check for expiration.  */
152 @       if (__tx_timer_time_slice == 0)
153 @
154     CMP     r2, #0                              @ Has it expired? // 检查是否超时(线程时间片用尽了)
155     BNE     __tx_timer_no_time_slice            @ No, skip expiration processing // _tx_timer_time_slice不为0表示还有时间片,跳转到__tx_timer_no_time_slice
156 @
157 @       /* Set the time-slice expired flag.  */
158 @       _tx_timer_expired_time_slice =  TX_TRUE;
159 @
160     LDR     r3, =_tx_timer_expired_time_slice   @ Pickup address of expired flag // _tx_timer_time_slice为0,线程时间片用尽了,设置_tx_timer_expired_time_slice线程时间片用尽标志
161     MOV     r0, #1                              @ Build expired value
162     STR     r0, [r3]                            @ Set time-slice expiration flag
163 @
164 @    
165 @
166 __tx_timer_no_time_slice: // 检查完了线程时间片
167 @
168 @    /* Test for timer expiration.  */
169 @    if (*_tx_timer_current_ptr)
170 @    
171 @
172     LDR     r1, =_tx_timer_current_ptr          @ Pickup current timer pointer address
173     LDR     r0, [r1]                            @ Pickup current timer
174     LDR     r2, [r0]                            @ Pickup timer list entry // *_tx_timer_current_ptr
175     CMP     r2, #0                              @ Is there anything in the list? // 检查*_tx_timer_current_ptr是否为空,是否有定时器,_tx_timer_current_ptr类似墙上的一个挂钟,每次定时器中断移动一格,每个格子下面挂的是超时的定时器,如果没有定时器的话,就是空的,否则有定时器超时
176     BEQ     __tx_timer_no_timer                 @ No, just increment the timer // 没有定时器的话,跳转到__tx_timer_no_timer
177 @
178 @        /* Set expiration flag.  */
179 @        _tx_timer_expired =  TX_TRUE;
180 @
181     LDR     r3, =_tx_timer_expired              @ Pickup expiration flag address // 有定时器超时,设置_tx_timer_expired标志
182     MOV     r2, #1                              @ Build expired value
183     STR     r2, [r3]                            @ Set expired flag
184     B       __tx_timer_done                     @ Finished timer processing
185 @
186 @    
187 @    else
188 @    
189 __tx_timer_no_timer: // 没有定时器超时
190 @
191 @        /* No timer expired, increment the timer pointer.  */
192 @        _tx_timer_current_ptr++;
193 @
194     ADD     r0, r0, #4                          @ Move to next timer // _tx_timer_current_ptr移动到下一个元素(_tx_timer_current_ptr指向_tx_timer_list数组的一个节点,_tx_timer_list的每个节点下是一个超时定时器链表,该链表里面的定时器全是在同一个时间点超时,_tx_timer_list每个节点间超时时间相差一个定时器中断),类似秒针走一格
195 @
196 @        /* Check for wraparound.  */
197 @        if (_tx_timer_current_ptr == _tx_timer_list_end)
198 @
199     LDR     r3, =_tx_timer_list_end             @ Pickup address of timer list end // 因为_tx_timer_list使用数组实现链表的,_tx_timer_current_ptr指向_tx_timer_list的元素,_tx_timer_current_ptr超过_tx_timer_list的最后一个元素时,需要重新指向_tx_timer_list的第1个元素,即wrap回环,跟秒针一样,秒针走到59之后,下一次就回到0
200     LDR     r2, [r3]                            @ Pickup list end
201     CMP     r0, r2                              @ Are we at list end?
202     BNE     __tx_timer_skip_wrap                @ No, skip wraparound logic
203 @
204 @            /* Wrap to beginning of list.  */
205 @            _tx_timer_current_ptr =  _tx_timer_list_start;
206 @
207     LDR     r3, =_tx_timer_list_start           @ Pickup address of timer list start
208     LDR     r0, [r3]                            @ Set current pointer to list start
209 @
210 __tx_timer_skip_wrap:
211 @
212     STR     r0, [r1]                            @ Store new current timer pointer
213 @    
214 @
215 __tx_timer_done: // 检查完了定时器
216 @
217 @
218 @    /* See if anything has expired.  */
219 @    if ((_tx_timer_expired_time_slice) || (_tx_timer_expired))
220 @    
221 @
222     LDR     r3, =_tx_timer_expired_time_slice   @ Pickup address of expired flag
223     LDR     r2, [r3]                            @ Pickup time-slice expired flag
224     CMP     r2, #0                              @ Did a time-slice expire? // 检查线程时间片超时标志_tx_timer_expired_time_slice(如果线程时间片用尽,前面会设置_tx_timer_expired_time_slice)
225     BNE     __tx_something_expired              @ If non-zero, time-slice expired // 如果__tx_something_expired不为0,线程时间片用尽了,跳转到_tx_timer_expired,需要检查是否换出当前执行的线程
226     LDR     r1, =_tx_timer_expired              @ Pickup address of other expired flag // 检查是否有设置定时器超时标志
227     LDR     r0, [r1]                            @ Pickup timer expired flag
228     CMP     r0, #0                              @ Did a timer expire?
229     BEQ     __tx_timer_nothing_expired          @ No, nothing expired // 定时器没有超时,跳转到__tx_timer_nothing_expired(需要注意,线程时间片没有用尽的情况才会走到这里的指令,也就是__tx_timer_nothing_expired是线程时间片没有用尽而且没有定时器超时)
230 @
231 __tx_something_expired: // 走到这里是至少有一个超时(线程时间片用尽了、有定时器超时)
232 @
233 @
234     STMDB   sp!, r0, lr                       @ Save the lr register on the stack // lr入栈(BL指令会修改lr,r0入栈只是为了让栈保持8 byte对齐)
235                                                 @   and save r0 just to keep 8-byte alignment
236 @
237 @    /* Did a timer expire?  */
238 @    if (_tx_timer_expired)
239 @    
240 @
241     LDR     r1, =_tx_timer_expired              @ Pickup address of expired flag
242     LDR     r0, [r1]                            @ Pickup timer expired flag
243     CMP     r0, #0                              @ Check for timer expiration
244     BEQ     __tx_timer_dont_activate            @ If not set, skip timer activation // 再次检查一下_tx_timer_expired是否超时,没有超时的话,跳转到_tx_timer_expired
245 @
246 @        /* Process timer expiration.  */
247 @        _tx_timer_expiration_process();
248 @
249     BL      _tx_timer_expiration_process        @ Call the timer expiration handling routine // 调用定时器超时处理函数_tx_timer_expiration_process,处理_tx_timer_current_ptr下面挂载的超时定时器
250 @
251 @    
252 __tx_timer_dont_activate:
253 @
254 @    /* Did time slice expire?  */
255 @    if (_tx_timer_expired_time_slice)
256 @    
257 @
258     LDR     r3, =_tx_timer_expired_time_slice   @ Pickup address of time-slice expired 
259     LDR     r2, [r3]                            @ Pickup the actual flag
260     CMP     r2, #0                              @ See if the flag is set // 检查线程时间片是否用尽
261     BEQ     __tx_timer_not_ts_expiration        @ No, skip time-slice processing // 线程时间片没有用尽,跳转到__tx_timer_not_ts_expiration
262 @
263 @        /* Time slice interrupted thread.  */
264 @        _tx_thread_time_slice();
265 @
266     BL      _tx_thread_time_slice               @ Call time-slice processing // 调用线程时间片处理函数_tx_thread_time_slice
267 @
268 @    
269 @
270 __tx_timer_not_ts_expiration:
271 @
272     LDMIA   sp!, r0, lr                       @ Recover lr register (r0 is just there for
273                                                 @   the 8-byte stack alignment
274 @
275 @    
276 @
277 __tx_timer_nothing_expired:
278 @
279 #ifdef __THUMB_INTERWORK
280     BX      lr                                  @ Return to caller
281 #else
282     MOV     pc, lr                              @ Return to caller // 中断处理函数返回(中断服务程序不会切换线程,线程切换在上下文恢复的时候判断)
283 #endif
284 @

3、线程时间片用尽(_tx_thread_time_slice)

当前线程没有启用抢占情况下,调度下一个就绪线程,当前线程开启抢占的话,可以抢占自己优先级的线程,那么就算有其他同优先级就绪线程,也不调度下一个就绪线程,当前线程抢占同优先级的其他就绪线程;调度下一个就绪线程主要是更新_tx_thread_execute_ptr,中断返回时用到_tx_thread_execute_ptr;_tx_thread_time_slice代码如下:

079 VOID  _tx_thread_time_slice(VOID)
080 
081 
082 TX_INTERRUPT_SAVE_AREA
083 
084 TX_THREAD       *thread_ptr;
085 #ifdef TX_ENABLE_STACK_CHECKING
086 TX_THREAD       *next_thread_ptr;
087 #endif
088 #ifdef TX_ENABLE_EVENT_TRACE
089 ULONG           system_state;
090 UINT            preempt_disable;
091 #endif
092 
093     /* Pickup thread pointer.  */
094     TX_THREAD_GET_CURRENT(thread_ptr) // 获取当前正在执行的线程_tx_thread_current_ptr
095 
096 #ifdef TX_ENABLE_STACK_CHECKING
097 
098     /* Check this thread's stack.  */
099     TX_THREAD_STACK_CHECK(thread_ptr)
100     
101     /* Set the next thread pointer to NULL.  */
102     next_thread_ptr =  TX_NULL;
103 #endif
104 
105     /* Lockout interrupts while the time-slice is evaluated.  */
106     TX_DISABLE // 关闭中断
107 
108     /* Clear the expired time-slice flag.  */
109     _tx_timer_expired_time_slice =  TX_FALSE; // 清除线程时间片用尽标志
110 
111     /* Make sure the thread pointer is valid.  */
112     if (thread_ptr != TX_NULL) // 再次检查_tx_thread_current_ptr是否为空(应该是关中断前_tx_thread_current_ptr可能被设置为TX_NULL,定时器超时或者嵌套中断服务程序可能把正在执行的线程删掉也是可能的)
113     
114 
115         /* Make sure the thread is still active, i.e. not suspended.  */
116         if (thread_ptr -> tx_thread_state == TX_READY) // 再次检查线程状态(如果线程非就绪状态,是不用管时间片的,唤醒线程时应该会重新设置时间片,毕竟线程睡眠这么久了,给刚唤醒的线程多一点点时间片也是合理的)
117         
118 
119             /* Setup a fresh time-slice for the thread.  */
120             thread_ptr -> tx_thread_time_slice =  thread_ptr -> tx_thread_new_time_slice; // 重新设置线程时间片
121 
122             /* Reset the actual time-slice variable.  */
123             _tx_timer_time_slice =  thread_ptr -> tx_thread_time_slice; // 重新设置_tx_timer_time_slice,如果当前线程不切换出去的话,设置_tx_timer_time_slice是有意义的,中断返回不会重新设置_tx_timer_time_slice,如果切换到别的线程,_tx_timer_time_slice会被再次设置,这个也不影响
124 
125             /* Determine if there is another thread at the same priority and preemption-threshold
126                is not set.  Preemption-threshold overrides time-slicing.  */
127             if (thread_ptr -> tx_thread_ready_next != thread_ptr) // 检查同一优先级线程就绪链表是否有下一个就绪线程;之前文章有介绍,正在执行的线程在就绪链表的表头,thread_ptr -> tx_thread_ready_next就是下一个就绪的线程
128             
129 
130                 /* Check to see if preemption-threshold is not being used.  */
131                 if (thread_ptr -> tx_thread_priority == thread_ptr -> tx_thread_preempt_threshold) // 当前正在执行的线程的抢占阈值等于线程优先级(没有启用抢占),就调度下一个就绪线程,设置_tx_thread_execute_ptr;有抢占的话就不管后续就绪线程;一般情况下,这两个值是一样的,如果不相等,抢占阈值优先级应该高于线程优先级,那么只看下一个就绪线程优先级的话,当前线程明显可以抢占下一个线程,这个判断一定意义上等价于比较当前线程的抢占阈值与下一个就绪线程的优先级
132                 
133                 
134                     /* Preemption-threshold is not being used by this thread.  */
135         
136                     /* There is another thread at this priority, make it the highest at
137                        this priority level.  */
138                     _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr -> tx_thread_ready_next; // 就绪链表表头指针指向下一个就绪线程(下一个就绪线程移到了表头,当前线程移到了表尾)
139     
140                     /* Designate the highest priority thread as the one to execute.  Don't use this 
141                        thread's priority as an index just in case a higher priority thread is now 
142                        ready!  */
143                     _tx_thread_execute_ptr =  _tx_thread_priority_list[_tx_thread_highest_priority]; // _tx_thread_execute_ptr指向下一个就绪线程,下次调度将要执行的线程
144 
145 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
146 
147                     /* Increment the thread's time-slice counter.  */
148                     thread_ptr -> tx_thread_performance_time_slice_count++;
149 
150                     /* Increment the total number of thread time-slice operations.  */
151                     _tx_thread_performance_time_slice_count++;
152 #endif
153 
154 
155 #ifdef TX_ENABLE_STACK_CHECKING
156 
157                     /* Pickup the next execute pointer.  */
158                     next_thread_ptr =  _tx_thread_execute_ptr;
159 #endif
160                 
161             
162         
163     
164 
165 #ifdef TX_ENABLE_EVENT_TRACE
166 
167     /* Pickup the volatile information.  */
168     system_state =  TX_THREAD_GET_SYSTEM_STATE();
169     preempt_disable =  _tx_thread_preempt_disable;
170    
171     /* Insert this event into the trace buffer.  */
172     TX_TRACE_IN_LINE_INSERT(TX_TRACE_TIME_SLICE, _tx_thread_execute_ptr, system_state, preempt_disable, TX_POINTER_TO_ULONG_CONVERT(&thread_ptr), TX_TRACE_INTERNAL_EVENTS)
173 #endif
174     
175     /* Restore previous interrupt posture.  */
176     TX_RESTORE // 开启中断
177 
178 #ifdef TX_ENABLE_STACK_CHECKING
179 
180     /* Determine if there is a next thread pointer to perform stack checking on.  */
181     if (next_thread_ptr != TX_NULL)
182     
183 
184         /* Yes, check this thread's stack.  */
185         TX_THREAD_STACK_CHECK(next_thread_ptr)
186     
187 #endif
188 

4、软件定时器timer

4.1、定时器结构

_tx_timer_list是一个超时定时器链表数组,首先_tx_timer_list是个数组,数组的每个元素是个超时定时器链表,其次_tx_timer_list是一个用数组实现的单向循环链表,逻辑上,数组前一个元素指向后一个元素,最末尾的元素指向第一个元素;

_tx_timer_current_ptr指向超时的定时器链表(_tx_timer_list对应的元素)。

假设硬件定时器中断时间周期为1秒,_tx_timer_current_ptr当前指向_tx_timer_list[0],此时要加入一个n秒的定时器,那么内核就会将定时器挂载到_tx_timer_current_ptr + n的超时定时器链表(_tx_timer_current_ptr + n没有超过_tx_timer_list最后一个元素,超过后要从第一个元素开始计算);

定时器中断一次_tx_timer_current_ptr加1,中断n次_tx_timer_current_ptr加n,中断n次之后,此时正好已经过了n秒了,内核检测到_tx_timer_current_ptr + n链表不为空,就会去处理_tx_timer_current_ptr + n指向的超时定时器链表。

定时器超时时间超过_tx_timer_list能表示的范围的情况,假如_tx_timer_list只有4个元素,_tx_timer_current_ptr指向_tx_timer_list[0],下一次定时器中断就认为_tx_timer_list[0]超时了,如果定时器时间为4秒,那么计算之后定时器似乎挂载到_tx_timer_list[0],很明显这个超时时间不对,最多只能挂载到_tx_timer_list[3],也就是最多只有3秒,内核做了个简单处理,就是先延迟3秒,先挂个超时定时器到_tx_timer_list[3],还剩下1秒先记录下来,_tx_timer_list[3]超时的时候,定时器处理函数检查到还有1秒时间,那么再挂一个超时定时器到_tx_timer_list[0]即可。

ThreadX超时定时器数据结构大致如下图所示:

 4.2、定时器超时处理函数(_tx_timer_expiration_process)

前面代码注释里面已经讲过了_tx_timer_current_ptr,在此略过。前面可以看到_tx_timer_expiration_process是在中断上下文里面调用的,定时器处理需要较长时间,因此_tx_timer_expiration_process唤醒一个专门处理超时定时器的线程_tx_timer_thread,线程入口为_tx_timer_thread_entry;_tx_timer_thread是个无限循环线程,没有超时定时器时就睡眠。

前面大致介绍过了定时器原理,_tx_timer_thread_entry主要就是判断定时器是否真正超时,是否需要重新激活,然后调用定时器超时回调函数;

因为_tx_timer_thread_entry与中断服务程序使用共同的变量,所以开关中断比较频繁,既要互斥也不能关中断太久,_tx_timer_thread_entry代码实现如下:

077 VOID  _tx_timer_thread_entry(ULONG timer_thread_input)
078 
079 
080 TX_INTERRUPT_SAVE_AREA
081 
082 TX_TIMER_INTERNAL           *expired_timers;
083 TX_TIMER_INTERNAL           *reactivate_timer;
084 TX_TIMER_INTERNAL           *next_timer;
085 TX_TIMER_INTERNAL           *previous_timer;
086 TX_TIMER_INTERNAL           *current_timer;
087 VOID                        (*timeout_function)(ULONG id);
088 ULONG                       timeout_param =  ((ULONG) 0);
089 TX_THREAD                   *thread_ptr;
090 #ifdef TX_REACTIVATE_INLINE
091 TX_TIMER_INTERNAL           **timer_list;               /* Timer list pointer           */
092 UINT                        expiration_time;            /* Value used for pointer offset*/
093 ULONG                       delta;
094 #endif
095 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
096 TX_TIMER                    *timer_ptr;
097 #endif
098 
099 
100     /* Make sure the timer input is correct.  This also gets rid of the
101        silly compiler warnings.  */
102     if (timer_thread_input == TX_TIMER_ID)
103     
104     
105         /* Yes, valid thread entry, proceed...  */
106 
107         /* Now go into an infinite loop to process timer expirations.  */
108         while (TX_LOOP_FOREVER)
109         
110 
111             /* First, move the current list pointer and clear the timer 
112                expired value.  This allows the interrupt handling portion
113                to continue looking for timer expirations.  */
114             TX_DISABLE // 关中断,中断服务程序会访问修改_tx_timer_current_ptr,有定时器超时时,中断服务程序不会移动_tx_timer_current_ptr,_tx_timer_current_ptr没有被取走的情况下,发生再多中断,_tx_timer_current_ptr也不会变,所以_tx_timer_current_ptr要被尽快取走,否则定时器就不准了
115 
116             /* Save the current timer expiration list pointer.  */
117             expired_timers =  *_tx_timer_current_ptr; // 取出超时定时器链表
118 
119             /* Modify the head pointer in the first timer in the list, if there
120                is one!  */
121             if (expired_timers != TX_NULL)
122             
123         
124                 expired_timers -> tx_timer_internal_list_head =  &expired_timers;
125             
126 
127             /* Set the current list pointer to NULL.  */
128             *_tx_timer_current_ptr =  TX_NULL; // _tx_timer_current_ptr设置为空,中断服务程序才会对_tx_timer_current_ptr进行移动,软件定时器才会计时
129 
130             /* Move the current pointer up one timer entry wrap if we get to 
131                the end of the list.  */
132             _tx_timer_current_ptr =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, 1); // 下一次中断时的超时定时器链表
133             if (_tx_timer_current_ptr == _tx_timer_list_end) // 链表回环
134             
135         
136                 _tx_timer_current_ptr =  _tx_timer_list_start;
137             
138 
139             /* Clear the expired flag.  */
140             _tx_timer_expired =  TX_FALSE; // 清除定时器超时_tx_timer_expired
141 
142             /* Restore interrupts temporarily.  */
143             TX_RESTORE // 允许中断,可以对软件定时器计时了
144 
145             /* Disable interrupts again.  */
146             TX_DISABLE // 紧接着又立即禁止了中断(主要是前面关闭时间可能有点长,如果有中断等待处理,开启中断就会使cpu立即处理中断,避免中断等待太久;另外接下来的关中断也可能很久,有中断的话先处理中断)
147 
148             /* Next, process the expiration of the associated timers at this
149                time slot.  */
150             while (expired_timers != TX_NULL) // 超时定时器不为空,循环处理当前链表里面的所有超时定时器
151             
152 
153                 /* Something is on the list.  Remove it and process the expiration.  */
154                 current_timer =  expired_timers;
155 
156                 /* Pickup the next timer.  */
157                 next_timer =  expired_timers -> tx_timer_internal_active_next;
158             
159                 /* Set the reactivate_timer to NULL.  */
160                 reactivate_timer =  TX_NULL;
161 
162                 /* Determine if this is the only timer.  */
163                 if (current_timer == next_timer) // 超时定时器下一个定时器指向自己(只有一个超时定时器)
164                 
165 
166                     /* Yes, this is the only timer in the list.  */
167 
168                     /* Set the head pointer to NULL.  */
169                     expired_timers =  TX_NULL; // 清空超时定时器链表即可
170                 
171                 else // 将当前处理的超时定时器从链表中删除
172                 
173 
174                     /* No, not the only expired timer.  */
175             
176                     /* Remove this timer from the expired list.  */
177                     previous_timer =                                   current_timer -> tx_timer_internal_active_previous;
178                     next_timer -> tx_timer_internal_active_previous =  previous_timer;
179                     previous_timer -> tx_timer_internal_active_next =  next_timer;
180 
181                     /* Modify the next timer's list head to point at the current list head.  */
182                     next_timer -> tx_timer_internal_list_head =  &expired_timers;
183 
184                     /* Set the list head pointer.  */
185                     expired_timers =  next_timer;
186                 
187 
188                 /* In any case, the timer is now off of the expired list.  */
189 
190                 /* Determine if the timer has expired or if it is just a really 
191                    big timer that needs to be placed in the list again.  */
192                 if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES) // 定时器剩余超时时间大于_tx_timer_list最大超时时间
193                 
194 
195                     /* Timer is bigger than the timer entries and must be
196                        rescheduled.  */
197 
198 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
199 
200                     /* Increment the total expiration adjustments counter.  */
201                     _tx_timer_performance__expiration_adjust_count++;
202 
203                     /* Determine if this is an application timer.  */
204                     if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
205                     
206             
207                         /* Derive the application timer pointer.  */
208                     
209                         /* Pickup the application timer pointer.  */
210                         TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
211 
212                         /* Increment the number of expiration adjustments on this timer.  */
213                         if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
214                         
215                     
216                             timer_ptr -> tx_timer_performance__expiration_adjust_count++;
217                         
218                     
219 #endif
220 
221                     /* Decrement the remaining ticks of the timer.  */
222                     current_timer -> tx_timer_internal_remaining_ticks =  
223                             current_timer -> tx_timer_internal_remaining_ticks - TX_TIMER_ENTRIES; // 需要先超时TX_TIMER_ENTRIES,剩余超时时间保留下来
224                 
225                     /* Set the timeout function to NULL in order to bypass the
226                        expiration.  */
227                     timeout_function =  TX_NULL; // 定时器没有真正超时,还不需要调用定时器超时函数,timeout_function设置为空即可
228 
229                     /* Make the timer appear that it is still active while interrupts
230                        are enabled.  This will permit proper processing of a timer
231                        deactivate from an ISR.  */
232                     current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
233                     current_timer -> tx_timer_internal_active_next =  current_timer;
234 
235                     /* Setup the temporary timer list head pointer.  */
236                     reactivate_timer =  current_timer; // 需要重新激活的定时器
237                 
238                 else // 剩余时间少于TX_TIMER_ENTRIES是不会再起定时器了,毕竟代码调用要花费时间,这些时间加上关中断的延迟,甚至就可以弥补这些偏差了,没办法计算到很精确
239                 
240 
241                     /* Timer did expire.  */
242 
243 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
244 
245                     /* Increment the total expirations counter.  */
246                     _tx_timer_performance_expiration_count++;
247 
248                     /* Determine if this is an application timer.  */
249                     if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
250                     
251             
252                         /* Derive the application timer pointer.  */
253                     
254                         /* Pickup the application timer pointer.  */
255                         TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
256 
257                         /* Increment the number of expirations on this timer.  */
258                         if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
259                         
260                     
261                             timer_ptr -> tx_timer_performance_expiration_count++;
262                         
263                     
264 #endif
265                   
266                     /* Copy the calling function and ID into local variables before interrupts 
267                        are re-enabled.  */
268                     timeout_function =  current_timer -> tx_timer_internal_timeout_function; // 定时器超时回调函数
269                     timeout_param =     current_timer -> tx_timer_internal_timeout_param; // 定时器超时回调函数指针
270 
271                     /* Copy the reinitialize ticks into the remaining ticks.  */
272                     current_timer -> tx_timer_internal_remaining_ticks =  current_timer -> tx_timer_internal_re_initialize_ticks; // tx_timer_internal_re_initialize_ticks不为0的话就是个循环定时器
273 
274                     /* Determine if the timer should be reactivated.  */
275                     if (current_timer -> tx_timer_internal_remaining_ticks != ((ULONG) 0))
276                     
277 
278                         /* Make the timer appear that it is still active while processing
279                            the expiration routine and with interrupts enabled.  This will 
280                            permit proper processing of a timer deactivate from both the
281                            expiration routine and an ISR.  */
282                         current_timer -> tx_timer_internal_list_head =    &reactivate_timer;
283                         current_timer -> tx_timer_internal_active_next =  current_timer;
284                         
285                         /* Setup the temporary timer list head pointer.  */
286                         reactivate_timer =  current_timer; // 需要重新激活的定时器
287                     
288                     else
289                     
290 
291                         /* Set the list pointer of this timer to NULL.  This is used to indicate
292                            the timer is no longer active.  */
293                         current_timer -> tx_timer_internal_list_head =  TX_NULL;
294                     
295                 
296 
297                 /* Set pointer to indicate the expired timer that is currently being processed.  */
298                 _tx_timer_expired_timer_ptr =  current_timer; // 正在处理的定时器,避免其他线程操作该定时器
299 
300                 /* Restore interrupts for timer expiration call.  */
301                 TX_RESTORE
302 
303                 /* Call the timer-expiration function, if non-NULL.  */
304                 if (timeout_function != TX_NULL)
305                 
306             
307                     (timeout_function) (timeout_param); // 定时器回调函数
308                 
309 
310                 /* Lockout interrupts again.  */
311                 TX_DISABLE
312 
313                 /* Clear expired timer pointer.  */
314                 _tx_timer_expired_timer_ptr =  TX_NULL;
315 
316                 /* Determine if the timer needs to be reactivated.  */
317                 if (reactivate_timer == current_timer) // reactivate_timer要么为空,要么为current_timer,实际就是判断是否需要重新激活current_timer
318                 
319 
320                     /* Reactivate the timer.  */
321 
322 #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO
323 
324                     /* Determine if this timer expired.  */
325                     if (timeout_function != TX_NULL)
326                     
327 
328                         /* Increment the total reactivations counter.  */
329                         _tx_timer_performance_reactivate_count++;
330 
331                         /* Determine if this is an application timer.  */
332                         if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout)
333                         
334             
335                             /* Derive the application timer pointer.  */
336                     
337                             /* Pickup the application timer pointer.  */
338                             TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr)
339 
340                             /* Increment the number of expirations on this timer.  */
341                             if (timer_ptr -> tx_timer_id == TX_TIMER_ID)
342                             
343                               
344                                 timer_ptr -> tx_timer_performance_reactivate_count++;
345                             
346                         
347                     
348 #endif
349 
350 #ifdef TX_REACTIVATE_INLINE
351 
352                     /* Calculate the amount of time remaining for the timer.  */
353                     if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES)
354                     
355 
356                         /* Set expiration time to the maximum number of entries.  */
357                         expiration_time =  TX_TIMER_ENTRIES - ((UINT) 1);
358                     
359                     else
360                     
361 
362                         /* Timer value fits in the timer entries.  */
363 
364                         /* Set the expiration time.  */
365                         expiration_time =  ((UINT) current_timer -> tx_timer_internal_remaining_ticks) - ((UINT) 1);
366                     
367 
368                     /* At this point, we are ready to put the timer back on one of
369                        the timer lists.  */
370     
371                     /* Calculate the proper place for the timer.  */
372                     timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, expiration_time);
373                     if (TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(timer_list) >= TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(_tx_timer_list_end))
374                     
375 
376                         /* Wrap from the beginning of the list.  */
377                         delta =  TX_TIMER_POINTER_DIF(timer_list, _tx_timer_list_end);
378                         timer_list =  TX_TIMER_POINTER_ADD(_tx_timer_list_start, delta);
379                     
380 
381                     /* Now put the timer on this list.  */
382                     if ((*timer_list) == TX_NULL)
383                     
384                 
385                         /* This list is NULL, just put the new timer on it.  */
386 
387                         /* Setup the links in this timer.  */
388                         current_timer -> tx_timer_internal_active_next =      current_timer;
389                         current_timer -> tx_timer_internal_active_previous =  current_timer;
390 
391                         /* Setup the list head pointer.  */
392                         *timer_list =  current_timer;
393                     
394                     else
395                     
396 
397                         /* This list is not NULL, add current timer to the end. */
398                         next_timer =                                          *timer_list;
399                         previous_timer =                                      next_timer -> tx_timer_internal_active_previous;
400                         previous_timer -> tx_timer_internal_active_next =     current_timer;
401                         next_timer -> tx_timer_internal_active_previous =     current_timer;
402                         current_timer -> tx_timer_internal_active_next =      next_timer;
403                         current_timer -> tx_timer_internal_active_previous =  previous_timer;
404                     
405 
406                     /* Setup list head pointer.  */
407                     current_timer -> tx_timer_internal_list_head =  timer_list;
408 #else
409 
410                     /* Reactivate through the timer activate function.  */
411 
412                     /* Clear the list head for the timer activate call.  */
413                     current_timer -> tx_timer_internal_list_head = TX_NULL;
414 
415                     /* Activate the current timer.  */
416                     _tx_timer_system_activate(current_timer); // 激活当前定时器(挂载到超时定时器链表上面去)
417 #endif
418                 
419 
420                 /* Restore interrupts.  */
421                 TX_RESTORE
422 
423                 /* Lockout interrupts again.  */
424                 TX_DISABLE
425             
426 
427             /* Finally, suspend this thread and wait for the next expiration.  */
428 
429             /* Determine if another expiration took place while we were in this
430                thread.  If so, process another expiration.  */
431             if (_tx_timer_expired == TX_FALSE) // 定时器中断没有激活新的超时定时器,那么需要挂起自己
432             
433 
434                 /* Otherwise, no timer expiration, so suspend the thread.  */
435 
436                 /* Build pointer to the timer thread.  */
437                 thread_ptr =  &_tx_timer_thread;
438 
439                 /* Set the status to suspending, in order to indicate the 
440                    suspension is in progress.  */
441                 thread_ptr -> tx_thread_state =  TX_SUSPENDED;
442 
443 #ifdef TX_NOT_INTERRUPTABLE
444 
445                 /* Call actual non-interruptable thread suspension routine.  */
446                 _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
447 
448                 /* Restore interrupts.  */
449                 TX_RESTORE
450 #else
451 
452                 /* Set the suspending flag. */
453                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
454 
455                 /* Increment the preempt disable count prior to suspending.  */
456                 _tx_thread_preempt_disable++; // 禁止抢占(挂起线程过程中,线程自己把自己切换出去,没必要被其他线程抢占,保存上下文,调度线程恢复上下文,再睡眠保存上下文,禁止抢占避免了一些不必要的操作)
457 
458                 /* Restore interrupts.  */
459                 TX_RESTORE
460 
461                 /* Call actual thread suspension routine.  */
462                 _tx_thread_system_suspend(thread_ptr); // 挂起自己(挂起线程需要一些时间,所以中断是打开的,但是又不想别其他线程抢占,所以前面的抢占是禁止的,_tx_thread_system_suspend会对_tx_thread_preempt_disable进行减1操作)
463 #endif
464             
465             else
466             
467 
468                 /* Restore interrupts.  */
469                 TX_RESTORE
470             
471         
472     
473 
474 #ifdef TX_SAFETY_CRITICAL
475 
476     /* If we ever get here, raise safety critical exception.  */
477     TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0);
478 #endif
479 
480 
481 #endif
482 

以上是关于ThreadX内核源码分析 - 定时器及线程时间片调度(arm)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

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

ThreadX内核源码分析 - ports线程上下文相关代码分析(arm)

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