1. 程式人生 > >RT-Thread核心之執行緒排程(六)

RT-Thread核心之執行緒排程(六)

5.執行緒切換的本質? 到現在我們知道,每個執行緒的執行需要一定的“物質”基礎。首先,需要獲得CPU的使用權,這就包括CPU內部各暫存器的使用,然後有自己獨立的棧空間,這部分的空間每個執行緒應該各自獨立。然後,每個執行緒都有一段獨特的指令以完成特定的功能。由這些就組成了“執行緒上下文”,執行緒的切換就是執行緒上下文的切換。在RT-Thread中有兩個架構相關的函式來完成這項工作:rt_hw_context_switch,rt_hw_context_switch_interrupt。 那麼這兩個函式有什麼區別呢?顯然,rt_hw_context_switch是在非中斷中進行上下文切換,而rt_hw_context_switch_interrupt則是在中斷上下文中完成執行緒切換的。這裡以S3C2440處理為例:
/******************************************************************************************* ** 函式名稱: rt_hw_context_switch ** 函式功能: 非中斷中進行上下文切換 ** 入口引數: from 被切出的執行緒的棧頂指標 ** to 被切入的執行緒的棧頂指標 ** 返 回 值: 無 ** 調 用: *******************************************************************************************/
.globl rt_hw_context_switch rt_hw_context_switch:     stmfd sp!, {lr}    /** 將被中斷的執行緒的下一條要執行的指令的地址壓入棧中  * (LR存放下一條將要執行的指令地址)   */     stmfd sp!, {r0-r12, lr}/** 將LR,R12-R0暫存器依次入棧 */     mrs r4, cpsr             /** 讀取CPSR暫存器的值到R4中 */     stmfd sp!, {r4}          /** 將R4暫存器的值(CPSR)壓入棧中 */     mrs r4, spsr             /** 讀取SPSR暫存器的值到R4暫存器中 */
    stmfd sp!, {r4}          /** 將R4暫存器的值(SPSR)壓入棧中 */     str sp, [r0]             /** 將執行緒的棧頂指標儲存到執行緒結構的sp中 */     ldr sp, [r1]             /** 從新執行緒的執行緒結構的sp中取出該執行緒的棧頂指標 */     ldmfd sp!, {r4}            /** 從執行緒的棧中彈出SPSR暫存器值到R4暫存器中 */     msr spsr_cxsf, r4          /** 將值寫入SPSR暫存器中 */     ldmfd sp!, {r4}            /** 從執行緒的棧中彈出CPSR暫存器值到R4暫存器中 */     msr spsr_cxsf, r4          /** 將值寫入CPSR暫存器中 */     ldmfd sp!, {r0-r12, lr, pc}^     /** 恢復該執行緒其他暫存器的值PC,LR,R12 - R0 */ /******************************************************************************************* ** 函式名稱: rt_hw_context_switch_interrupt ** 函式功能: 中斷中進行上下文切換 ** 入口引數: from 被切出的執行緒的棧頂指標 ** to 被切入的執行緒的棧頂指標 ** 返 回 值: 無 ** 調 用: ******************************************************************************************/ .globl rt_thread_switch_interrupt_flag .globl rt_interrupt_from_thread .globl rt_interrupt_to_thread .globl rt_hw_context_switch_interrupt rt_hw_context_switch_interrupt:     /** 1.判斷rt_thread_switch_interrupt_flag變數的值是否為1 */ ldr r2, =rt_thread_switch_interrupt_flag    /** 載入變數rt_thread_switch_interrupt_flag                                                  * 的地址到r2暫存器中           */ ldr r3, [r2] /** 讀取rt_thread_switch_interrupt_flag暫存器的值到R3暫存器中 */ cmp r3, #1/** 判斷rt_thread_switch_interrupt_flag的值是否為1 */     /** 如果rt_thread_switch_interrupt_flag值為1 */ beq _reswitch     /** 如果rt_thread_switch_interrupt_flag值為1,跳轉到標號_reswitch執行 */ mov r3, #1/** 如果rt_thread_switch_interrupt_flag值為0,將其值設定為1 */ str r3, [r2] ldr r2, =rt_interrupt_from_thread    /** 載入rt_interrupt_from_thread變數的地址到R2寄存                                           * 器中                                            */ str r0, [r2] /** 將被切換出的執行緒的棧頂地址儲存到變數rt_interrupt_from_thread中 */ _reswitch: ldr r2, =rt_interrupt_to_thread     /** 載入rt_interrupt_to_thread變數的地址到R2暫存器                                           * 中                                           */ str r1, [r2]/** 將被切入的執行緒的棧頂地址儲存到變數rt_interrupt_to_thread中 */ mov pc, lr 
/******************************************************************************************* ** 函式名稱: rt_hw_context_switch_interrupt ** 函式功能: 中斷中進行上下文切換 ** 入口引數: from 被切出的執行緒的棧頂指標 ** to 被切入的執行緒的棧頂指標 ** 返 回 值: 無 ** 調 用: *******************************************************************************************/ .globl rt_thread_switch_interrupt_flag .globl rt_interrupt_from_thread .globl rt_interrupt_to_thread .globl rt_hw_context_switch_interrupt rt_hw_context_switch_interrupt:  /** 1.判斷rt_thread_switch_interrupt_flag變數的值是否為1 */  ldr r2, =rt_thread_switch_interrupt_flag/** 載入變數rt_thread_switch_interrupt_flag的地址到r2暫存器中 */  ldr r3, [r2]     /** 讀取rt_thread_switch_interrupt_flag暫存器的值到R3暫存器中 */  cmp r3, #1       /** 判斷rt_thread_switch_interrupt_flag的值是否為1 */  /** 如果rt_thread_switch_interrupt_flag值為1 */  beq _reswitch     /** 如果rt_thread_switch_interrupt_flag值為1,跳轉到標號_reswitch執行 */  mov r3, #1        /** 如果rt_thread_switch_interrupt_flag值為0,將其值設定為1 */  str r3, [r2]  ldr r2, =rt_interrupt_from_thread    /** 載入rt_interrupt_from_thread變數的地址到R2暫存器中 */  str r0, [r2]      /** 將被切換出的執行緒的棧頂地址儲存到變數rt_interrupt_from_thread中 */ _reswitch:  ldr r2, =rt_interrupt_to_thread     /** 載入rt_interrupt_to_thread變數的地址到R2暫存器中 */  str r1, [r2]       /** 將被切入的執行緒的棧頂地址儲存到變數rt_interrupt_to_thread中 */  mov pc, lr  我們發現rt_hw_context_switch_interrupt並沒有完成執行緒的切換,只是用全域性變rt_interrupt_from_thread 和rt_interrupt_to_thread儲存了被換出和換入的執行緒的棧頂指標,而真正的切換過程在中斷處理中完成。 .globl rt_interrupt_enter .globl rt_interrupt_leave .globl rt_thread_switch_interrupt_flag .globl rt_interrupt_from_thread .globl rt_interrupt_to_thread vector_irq:     stmfd sp!, {r0-r12,lr}     /** 使用中斷模式的棧空間來儲存SVC模式下的PC, R12 - R0 */     bl rt_interrupt_enter      /** 呼叫rt_interrupt_enter函式: 中斷巢狀的層數加1 */     bl rt_hw_trap_irq          /** 根據中斷號去呼叫中斷處理程式:由於中斷處理程式是在IRQ模式執行,                                 * 因此係統是不支援中斷巢狀的                                  */     bl rt_interrupt_leave     /** 呼叫rt_interrupt_leave函式: 中斷巢狀的層數減1 */     /** 在中斷退出之前,判斷rt_thread_switch_interrupt_flag變數的值是否為1 */     ldr r0, =rt_thread_switch_interrupt_flag     /** 讀取變數rt_thread_switch_interrupt_flag                                                   * 的地址到r0暫存器中                                                    */     ldr r1, [r0]          /** 讀取變數rt_thread_switch_interrupt_flag的值 */     cmp r1, #1            /** 判斷變數rt_thread_switch_interrupt_flag的值是否為1 */     beq _interrupt_thread_switch     /** 如果為1說明在退出中斷模式之前還需要進行任務切換工作;                                       * 如果為0則可以安全的退出中斷模式了                                        */     ldmfd sp!, {r0-r12,lr}      /** 恢復SVC模式下的各個暫存器值 */     subs pc, lr, #4             /** 繼續從被中斷點執行 */ _interrupt_thread_switch:     /** 1.將變數rt_thread_switch_interrupt_flag的值清0 */     mov r1,  #0           /** 設定R1暫存器的值為0 */     str r1,  [r0]         /** 將變數rt_thread_switch_interrupt_flag的值設定為0 */     ldmfd sp!, {r0-r12,lr}     /** 恢復儲存在IRQ模式中的各暫存器值 */     stmfd sp!, {r0-r3}         /** 將R0-R3暫存器入棧 */     mov r1,  sp                /** 將此時的棧指標儲存在R1中 */     add sp,  sp, #16           /** 將SP的值加16,SP重新指向R0-R3入棧時的位置 */     sub r2,  lr, #4            /** 計算出被中斷的執行緒的PC值儲存到R2中 */     mrs r3,  spsr             /** 載入被中斷的執行緒的CPSR暫存器值到R3暫存器中 */     orr r0,  r3, #NOINT       /** 遮蔽中斷位 */     msr spsr_c, r0            /** 將設定後的值寫回IRQ模式的SPSR暫存器中 */     ldr r0,  =.+8             /** 通過反彙編檢視: 是將下面第二條指令的地址存到R0中 */     movs pc,  r0              /** movs指令會影響到CPSR,包括N,Z,C標誌位,CPSR會被SPSR覆蓋                                * 因此執行此條指令相當於完成處理器從IRQ到SVC模式的切換                                * 下面指令的sp將為SVC下的sp暫存器,而非IRQ模式的sp                                */     stmfd sp!, {r2}             /** 將被中斷的執行緒的PC值入棧 */     stmfd sp!, {r4-r12,lr}      /** 將被中斷的執行緒的LR,R12-R4暫存器入棧 */     mov r4,  r1                 /** 將R1的值儲存到R4 */     mov r5,  r3                 /** 將R3的值儲存到R5(IRQ_SPSR) */     ldmfd r4!, {r0-r3}          /** 將棧中儲存的R0-R3暫存器值恢復 */     stmfd sp!, {r0-r3}          /** 將R3-R0暫存器值入棧 */     stmfd sp!, {r5}             /** 將舊任務的CPSR值入棧 */     mrs r4,  spsr     stmfd sp!, {r4}             /** 將舊任務的SPSR值入棧 */     /** 讀取儲存在變數rt_interrupt_from_thread的舊執行緒的sp值 */     ldr r4,  =rt_interrupt_from_thread     ldr r5,  [r4]     str sp,  [r5]               /** 儲存換出任務的棧頂指標 */     /** 獲取新執行緒的棧頂指標 */     ldr r6,  =rt_interrupt_to_thread     /** 載入變數rt_interrupt_to_thread的地址到R6暫存器                                           * 中                                            */     ldr r6,  [r6]             /** 載入變數rt_interrupt_to_thread的值到R6中 */     ldr sp,  [r6]             /** 載入變數rt_interrupt_to_thread的值到SP暫存器中 */     ldmfd sp!, {r4}             /** 彈出新執行緒的SPSR暫存器值 */     msr SPSR_cxsf, r4     ldmfd sp!, {r4}             /** 彈出新執行緒的CPSR暫存器值 */     msr CPSR_cxsf, r4     ldmfd sp!, {r0-r12,lr,pc}   /** 彈出新執行緒的其他各暫存器,執行緒恢復 */