這篇文章主要講解了“Linux內(nèi)核進(jìn)程上下文切換怎么理解”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Linux內(nèi)核進(jìn)程上下文切換怎么理解”吧!
郁南網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),郁南網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為郁南成百上千提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\成都外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的郁南做網(wǎng)站的公司定做!
1.進(jìn)程上下文的概念
進(jìn)程上下文是進(jìn)程執(zhí)行活動全過程的靜態(tài)描述。我們把已執(zhí)行過的進(jìn)程指令和數(shù)據(jù)在相關(guān)寄存器與堆棧中的內(nèi)容稱為進(jìn)程上文,把正在執(zhí)行的指令和數(shù)據(jù)在寄存器與堆棧中的內(nèi)容稱為進(jìn)程正文,把待執(zhí)行的指令和數(shù)據(jù)在寄存器與堆棧中的內(nèi)容稱為進(jìn)程下文。
實際上linux內(nèi)核中,進(jìn)程上下文包括進(jìn)程的虛擬地址空間和硬件上下文。
進(jìn)程硬件上下文包含了當(dāng)前cpu的一組寄存器的集合,arm64中使用task_struct結(jié)構(gòu)的thread成員的cpu_context成員來描述,包括x19-x28,sp, pc等。
如下為硬件上下文存放示例圖:

2.上下文切換詳細(xì)過程
進(jìn)程上下文切換主要涉及到兩部分主要過程:進(jìn)程地址空間切換和處理器狀態(tài)切換。地址空間切換主要是針對用戶進(jìn)程而言,而處理器狀態(tài)切換對應(yīng)于所有的調(diào)度單位。下面我們分別看下這兩個過程:
__schedule // kernel/sched/core.c ->context_switch ->switch_mm_irqs_off //進(jìn)程地址空間切換 ->switch_to //處理器狀態(tài)切換
2.1 進(jìn)程地址空間切換
進(jìn)程地址空間指的是進(jìn)程所擁有的虛擬地址空間,而這個地址空間是假的,是linux內(nèi)核通過數(shù)據(jù)結(jié)構(gòu)來描述出來的,從而使得每一個進(jìn)程都感覺到自己擁有整個內(nèi)存的假象,cpu訪問的指令和數(shù)據(jù)最終會落實到實際的物理地址,對用進(jìn)程而言通過缺頁異常來分配和建立頁表映射。進(jìn)程地址空間內(nèi)有進(jìn)程運行的指令和數(shù)據(jù),因此到調(diào)度器從其他進(jìn)程重新切換到我的時候,為了保證當(dāng)前進(jìn)程訪問的虛擬地址是自己的必須切換地址空間。
實際上,進(jìn)程地址空間使用mm_struct結(jié)構(gòu)體來描述,這個結(jié)構(gòu)體被嵌入到進(jìn)程描述符(我們通常所說的進(jìn)程控制塊PCB)task_struct中,mm_struct結(jié)構(gòu)體將各個vma組織起來進(jìn)行管理,其中有一個成員pgd至關(guān)重要,地址空間切換中最重要的是pgd的設(shè)置。
pgd中保存的是進(jìn)程的頁全局目錄的虛擬地址(本文會涉及到頁表相關(guān)的一些概念,在此不是重點,不清楚的可以查閱相關(guān)資料,后期有機會會講解進(jìn)程頁表),記住保存的是虛擬地址,那么pgd的值是何時被設(shè)置的呢?答案是fork的時候,如果是創(chuàng)建進(jìn)程,需要分配設(shè)置mm_struct,其中會分配進(jìn)程頁全局目錄所在的頁,然后將首地址賦值給pgd。
我們來看看進(jìn)程地址空間究竟是如何切換的,結(jié)果會讓你大吃一驚(這里暫且不考慮asid機制,后面有機會會在其他文章中講解):
代碼路徑如下:
context_switch // kernel/sched/core.c ->switch_mm_irqs_off ->switch_mm ->__switch_mm ->check_and_switch_context ->cpu_switch_mm ->cpu_do_switch_mm(virt_to_phys(pgd),mm) //arch/arm64/include/asm/mmu_context.h arch/arm64/mm/proc.S 158 /* 159 * cpu_do_switch_mm(pgd_phys, tsk) 160 * 161 * Set the translation table base pointer to be pgd_phys. 162 * 163 * - pgd_phys - physical address of new TTB 164 */ 165 ENTRY(cpu_do_switch_mm) 166 mrs x2, ttbr1_el1 167 mmid x1, x1 // get mm->context.id 168 phys_to_ttbr x3, x0 169 170 alternative_if ARM64_HAS_CNP 171 cbz x1, 1f // skip CNP for reserved ASID 172 orr x3, x3, #TTBR_CNP_BIT 173 1: 174 alternative_else_nop_endif 175 #ifdef CONFIG_ARM64_SW_TTBR0_PAN 176 bfi x3, x1, #48, #16 // set the ASID field in TTBR0 177 #endif 178 bfi x2, x1, #48, #16 // set the ASID 179 msr ttbr1_el1, x2 // in TTBR1 (since TCR.A1 is set) 180 isb 181 msr ttbr0_el1, x3 // now update TTBR0 182 isb 183 b post_ttbr_update_workaround // Back to C code... 184 ENDPROC(cpu_do_switch_mm)
代碼中最核心的為181行,最終將進(jìn)程的pgd虛擬地址轉(zhuǎn)化為物理地址存放在ttbr0_el1中,這是用戶空間的頁表基址寄存器,當(dāng)訪問用戶空間地址的時候mmu會通過這個寄存器來做遍歷頁表獲得物理地址(ttbr1_el1是內(nèi)核空間的頁表基址寄存器,訪問內(nèi)核空間地址時使用,所有進(jìn)程共享,不需要切換)。完成了這一步,也就完成了進(jìn)程的地址空間切換,確切的說是進(jìn)程的虛擬地址空間切換。
內(nèi)核處理的是不是很簡單,很優(yōu)雅,別看只是設(shè)置了頁表基址寄存器,也就是將即將執(zhí)行的進(jìn)程的頁全局目錄的物理地址設(shè)置到頁表基址寄存器,他卻完成了地址空間切換的壯舉,有的小伙伴可能不明白為啥這就完成了地址空間切換?試想如果進(jìn)程想要訪問一個用戶空間虛擬地址,cpu的mmu所做的工作,就是從頁表基址寄存器拿到頁全局目錄的物理基地址,然后和虛擬地址配合來查查找頁表,最終找到物理地址進(jìn)行訪問(當(dāng)然如果tlb命中就不需要遍歷頁表),每次用戶虛擬地址訪問的時候(內(nèi)核空間共享不考慮),由于頁表基地址寄存器內(nèi)存放的是當(dāng)前執(zhí)行進(jìn)程的頁全局目錄的物理地址,所以訪問自己的一套頁表,拿到的是屬于自己的物理地址(實際上,進(jìn)程是訪問虛擬地址空間的指令數(shù)據(jù)的時候不斷發(fā)生缺頁異常,然后缺頁異常處理程序為進(jìn)程分配實際的物理頁,然后將頁幀號和頁表屬性填入自己的頁表條目中),就不會訪問其他進(jìn)程的指令和數(shù)據(jù),這也是為何多個進(jìn)程可以訪問相同的虛擬地址而不會出現(xiàn)差錯的原因,而且做到的各個地址空間的隔離互不影響(共享內(nèi)存除外)。
其實,地址空間切換過程中,還會清空tlb,防止當(dāng)前進(jìn)程虛擬地址轉(zhuǎn)化過程中命中上一個進(jìn)程的tlb表項,一般會將所有的tlb無效,但是這會導(dǎo)致很大的性能損失,因為新進(jìn)程被切換進(jìn)來的時候面對的是全新的空的tlb,造成很大概率的tlb miss,需要重新遍歷多級頁表,所以arm64在tlb表項中增加了非全局(nG)位區(qū)分內(nèi)核和進(jìn)程的頁表項,使用ASID區(qū)分不同進(jìn)程的頁表項,來保證可以在切換地址空間的時候可以不刷tlb,后面會主要講解ASID技術(shù)。
還需要注意的是僅僅切換用戶地址空間,內(nèi)核地址空間由于是共享的不需要切換,也就是為何切換到內(nèi)核線程不需要也沒有地址空間的原因。
如下為進(jìn)程地址空間切換示例圖:

2.2 處理器狀態(tài)(硬件上下文)切換
前面進(jìn)行了地址空間切換,只是保證了進(jìn)程訪問指令數(shù)據(jù)時訪問的是自己地址空間(當(dāng)然上下文切換的時候處于內(nèi)核空間,執(zhí)行的是內(nèi)核地址數(shù)據(jù),當(dāng)返回用戶空間的時候才有機會執(zhí)行用戶空間指令數(shù)據(jù)**,地址空間切換為進(jìn)程訪問自己用戶空間做好了準(zhǔn)備**),但是進(jìn)程執(zhí)行的內(nèi)核棧還是前一個進(jìn)程的,當(dāng)前執(zhí)行流也還是前一個進(jìn)程的,需要做切換。
arm64中切換代碼如下:
switch_to ->__switch_to ... //浮點寄存器等的切換 ->cpu_switch_to(prev, next) arch/arm64/kernel/entry.S: 1032 /* 1033 * Register switch for AArch74. The callee-saved registers need to be saved 1034 * and restored. On entry: 1035 * x0 = previous task_struct (must be preserved across the switch) 1036 * x1 = next task_struct 1037 * Previous and next are guaranteed not to be the same. 1038 * 1039 */ 1040 ENTRY(cpu_switch_to) 1041 mov x10, #THREAD_CPU_CONTEXT 1042 add x8, x0, x10 1043 mov x9, sp 1044 stp x19, x20, [x8], #16 // store callee-saved registers 1045 stp x21, x22, [x8], #16 1046 stp x23, x24, [x8], #16 1047 stp x25, x26, [x8], #16 1048 stp x27, x28, [x8], #16 1049 stp x29, x9, [x8], #16 1050 str lr, [x8] 1051 add x8, x1, x10 1052 ldp x19, x20, [x8], #16 // restore callee-saved registers 1053 ldp x21, x22, [x8], #16 1054 ldp x23, x24, [x8], #16 1055 ldp x25, x26, [x8], #16 1056 ldp x27, x28, [x8], #16 1057 ldp x29, x9, [x8], #16 1058 ldr lr, [x8] 1059 mov sp, x9 1060 msr sp_el0, x1 1061 ret 1062 ENDPROC(cpu_switch_to)
其中x19-x28是arm64 架構(gòu)規(guī)定需要調(diào)用保存的寄存器,可以看到處理器狀態(tài)切換的時候?qū)⑶耙粋€進(jìn)程(prev)的x19-x28,fp,sp,pc保存到了進(jìn)程描述符的cpu_contex中,然后將即將執(zhí)行的進(jìn)程(next)描述符的cpu_contex的x19-x28,fp,sp,pc恢復(fù)到相應(yīng)寄存器中,而且將next進(jìn)程的進(jìn)程描述符task_struct地址存放在sp_el0中,用于通過current找到當(dāng)前進(jìn)程,這樣就完成了處理器的狀態(tài)切換。
實際上,處理器狀態(tài)切換就是將前一個進(jìn)程的sp,pc等寄存器的值保存到一塊內(nèi)存上,然后將即將執(zhí)行的進(jìn)程的sp,pc等寄存器的值從另一塊內(nèi)存中恢復(fù)到相應(yīng)寄存器中,恢復(fù)sp完成了進(jìn)程內(nèi)核棧的切換,恢復(fù)pc完成了指令執(zhí)行流的切換。其中保存/恢復(fù)所用到的那塊內(nèi)存需要被進(jìn)程所標(biāo)識,這塊內(nèi)存這就是cpu_contex這個結(jié)構(gòu)的位置(進(jìn)程切換都是在內(nèi)核空間完成)。
由于用戶空間通過異常/中斷進(jìn)入內(nèi)核空間的時候都需要保存現(xiàn)場,也就是保存發(fā)生異常/中斷時的所有通用寄存器的值,內(nèi)核會把“現(xiàn)場”保存到每個進(jìn)程特有的進(jìn)程內(nèi)核棧中,并用pt_regs結(jié)構(gòu)來描述,當(dāng)異常/中斷處理完成之后會返回用戶空間,返回之前會恢復(fù)之前保存的“現(xiàn)場”,用戶程序繼續(xù)執(zhí)行。
所以當(dāng)進(jìn)程切換的時候,當(dāng)前進(jìn)程被時鐘中斷打斷,將發(fā)生中斷時的現(xiàn)場保存到進(jìn)程內(nèi)核棧(如:sp, lr等),然后會切換到下一個進(jìn)程,當(dāng)再次回切換回來的時候,返回用戶空間的時候會恢復(fù)之前的現(xiàn)場,進(jìn)程就可以繼續(xù)執(zhí)行(執(zhí)行之前被中斷打斷的下一條指令,繼續(xù)使用自己用戶態(tài)sp),這對于用戶進(jìn)程來說是透明的。
如下為硬件上下文切換示例圖:

3.ASID機制
前面講過,進(jìn)程切換的時候,由于tlb中存放的可能是其他進(jìn)程的tlb表項,所有才需要在進(jìn)程切換的時候進(jìn)行tlb的清空工作(清空即是使得所有的tlb表項無效,地址轉(zhuǎn)換需要遍歷多級頁表,找到頁表項,然后重新加載頁表項到tlb),有了ASID機制之后,命中tlb表項,由虛擬地址和ASID共同決定(當(dāng)然還有nG位),可以減小進(jìn)程切換中tlb被清空的機會。
下面我們講解ASID機制,ASID(Address Space Identifer 地址空間標(biāo)識符),用于區(qū)別不同進(jìn)程的頁表項,arm64中,可以選擇兩種ASID長度8位或者16位,這里以8位來講解。
如果ASID長度為8位,那么ASID有256個值,但是由于0是保留的,所有可以分配的ASID范圍就為1-255,那么可以標(biāo)識255個進(jìn)程,當(dāng)超出255個進(jìn)程的時候,會出現(xiàn)兩個進(jìn)程的ASID相同的情況,因此內(nèi)核使用了ASID版本號。
內(nèi)核中處理如下(參考arch/arm64/mm/context.c):
1)內(nèi)核為每個進(jìn)程分配一個64位的軟件ASID,其中低8位為硬件ASID,高56位為ASID版本號,這個軟件ASID存放放在進(jìn)程的mm_struct結(jié)構(gòu)的context結(jié)構(gòu)的id中,進(jìn)程創(chuàng)建的時候會初始化為0。
2)內(nèi)核中有一個64位的全局變量asid_generation,同樣它的高56位為ASID版本號,用于標(biāo)識當(dāng)前ASID分配的批次。
3)當(dāng)進(jìn)程調(diào)度,由prev進(jìn)程切換到next進(jìn)程的時候,如果不是內(nèi)核線程則進(jìn)行地址空間切換調(diào)用check_and_switch_context,此函數(shù)會判斷next進(jìn)程的ASID版本號是否和全局的ASID版本號相同(是否處于同一批次),如果相同則不需要為next進(jìn)程分配ASID,不相同則需要分配ASID。
4)內(nèi)核使用asid_map位圖來管理硬件ASID的分配,asid_bits記錄使用的ASID的長度,每處理器變量active_asids保存當(dāng)前分配的硬件ASID,每處理器變量reserved_asids存放保留的ASID,tlb_flush_pending位圖記錄需要清空tlb的cpu集合。
硬件ASID分配策略如下:
(1)如果進(jìn)程的ASID版本號和當(dāng)前全局的ASID版本號相同(同批次情況),則不需要重新分配ASID。
(2)如果進(jìn)程的ASID版本號和當(dāng)前全局的ASID版本號不相同(不同批次情況),且進(jìn)程原本的硬件ASID已經(jīng)被分配,則重新分配新的硬件ASID,并將當(dāng)前全局的ASID版本號組合新分配的硬件ASID寫到進(jìn)程的軟件ASID中。
(3)如果進(jìn)程的ASID版本號和當(dāng)前全局的ASID版本號不相同(不同批次情況),且進(jìn)程原本的硬件ASID還沒有被分配,則不需要重新分配新的硬件ASID,只需要更新進(jìn)程軟件ASID版本號,并將當(dāng)前全局的ASID版本號組合進(jìn)程原來的硬件ASID寫到進(jìn)程的軟件ASID中。
(4)如果進(jìn)程的ASID版本號和當(dāng)前全局的ASID版本號不相同(不同批次情況),需要分配硬件ASID時,發(fā)現(xiàn)硬件ASID已經(jīng)被其他進(jìn)程分配完(asid_map位圖中查找,發(fā)現(xiàn)位圖全1),則這個時候需要遞增全局的ASID版本號, 清空所有cpu的tlb, 清空asid_map位圖,然后分配硬件ASID,并將當(dāng)前全局的ASID版本號組合新分配的硬件ASID寫到進(jìn)程的軟件ASID中。
下面我們以實例來看ASID的分配過程:
如下圖:

我們假設(shè)圖中從A進(jìn)程到D進(jìn)程,有255個進(jìn)程,剛好分配完了asid, ,從A到D的切換過程中使用的都是同一批次的asid版本號。
則這個過程中,有進(jìn)程會創(chuàng)建的時候被切換到,假設(shè)不超出255個進(jìn)程,在切換過程中會為新進(jìn)程分配硬件的ASID,分配完后下次切換到他時由于他的ASID版本號和當(dāng)前的全局的ASID版本號相同,所以不需要再次分配ASID,當(dāng)然也不需要清空tlb。
注:這里說的ASID即為硬件ASID區(qū)別于ASID版本號。
情況1-ASID版本號不變 屬于策略(1):從C進(jìn)程到D進(jìn)程切換,內(nèi)核判斷D進(jìn)程的ASID版本號和當(dāng)前的全局的ASID版本號相同,所以不需要為他分配ASID(執(zhí)行快速路徑switch_mm_fastpath去設(shè)置ttbrx_el1))。
情況2 -硬件ASID全部分配完 屬于策略(4):假設(shè)到達(dá)D進(jìn)程時,asid已經(jīng)全部分配完(系統(tǒng)中有255個進(jìn)程都分配到了硬件asid號),這個時候新創(chuàng)建的進(jìn)程E被調(diào)度器選中,切換到E,由于新創(chuàng)建的進(jìn)程的軟件ASID被初始化為0,所以和當(dāng)前的全局的ASID版本號不同(不在同一批次),則這個時候會執(zhí)行new_context為進(jìn)程分配ASID,但是由于沒有可以分配的ASID,所以會將全局的ASID版本號加1(發(fā)生ASID回繞),這個時候全局的ASID為801,然后清空asid_map,置位tlb_flush_pending所有bit用于清空所有cpu的tlb,然后再次去分配硬件ASID給E進(jìn)程,這個時候分配到了1給他(將ASID版本號)。
情況3-ASID版本號發(fā)生變化,進(jìn)程的硬件ASID可以再次使用 屬于策略(3):假設(shè)從E切換到了B進(jìn)程,而B進(jìn)程之前已經(jīng)在全局的ASID版本號為800的批次上分配了編號為5的硬件ASID,但是B進(jìn)程的ASID版本號800和現(xiàn)在全局的ASID版本號801不相同,所有需要new_context為進(jìn)程分配ASID,分配的時候發(fā)現(xiàn)asid_map中編號為5沒有被置位,也就是沒有其他進(jìn)程分配了5這個ASID,所有可以繼續(xù)使用原來分配的硬件ASID 5。
情況4 - ASID版本號發(fā)生變化,有其他進(jìn)程已經(jīng)分配了相同的硬件ASID 屬于策略(2): 假設(shè)從B進(jìn)程切換到A進(jìn)程,而B進(jìn)程之前已經(jīng)在全局的ASID版本號為800的批次上分配了編號為1的硬件ASID,但是B進(jìn)程的ASID版本號800和現(xiàn)在全局的ASID版本號801不相同,所有需要new_context為進(jìn)程分配ASID,分配的時候發(fā)現(xiàn)asid_map中編號為1已經(jīng)被置位,也就是其他進(jìn)程已經(jīng)分配了1這個ASID,需要從asid_map尋找下一個空閑的ASID,則分配了新的ASID為6。
假設(shè)從A到E,由于E的ASID版本號和全局的ASID版本號(同一批次),和情況1相同,不需要分配ASID。但是之前原來處于800這個ASID版本號批次的進(jìn)程都需要重新分配ASID,有的可以使用原來的硬件ASID,有的重新分配硬件ASID,但是都將ASID版本號修改為了現(xiàn)在全局的ASID版本號801。但是,隨著硬件ASID的不斷分配,最終處于801這一批次的硬件ASID也會分配完,這個時候就是上面的情況2,要情況所有cpu的tlb。
我可以看到有了ASID機制之后,由于只有當(dāng)硬件ASID被分配完了(如被255個進(jìn)程使用),發(fā)生回繞的時候才會清空所有cpu的tlb,大大提高了系統(tǒng)的性能(沒有ASID機制的情況下每次進(jìn)程切換需要地址空間切換的時候都需要清空tlb)。
4. 普通用戶進(jìn)程、普通用戶線程、內(nèi)核線程切換的差別
內(nèi)核地址空間切換的時候有一下原則:看的是進(jìn)程描述符的mm_struct結(jié)構(gòu),即是成員mm:
1)如果mm為NULL,則表示即將切換的是內(nèi)核線程,不需要切換地址空間(所有任務(wù)共享內(nèi)核地址空間)。
2)內(nèi)核線程會借用前一個用戶進(jìn)程的mm,賦值到自己的active_mm(本身的mm為空),進(jìn)程切換的時候就會比較前一個進(jìn)程的active_mm和當(dāng)前進(jìn)程的mm。
3)如果前一個任務(wù)的和即將切換的任務(wù),具有相同的mm成員,也就是共享地址空間的線程則也不需要切換地址空間。
->所有的進(jìn)程線程之間進(jìn)行切換都需要切換處理器狀態(tài)。
->對于普通的用戶進(jìn)程之間進(jìn)行切換需要切換地址空間。
->同一個線程組中的線程之間切換不需要切換地址空間,因為他們共享相同的地址空間。
-> 內(nèi)核線程在上下文切換的時候不需要切換地址空間,僅僅是借用上一個進(jìn)程mm_struct結(jié)構(gòu)。
有一下場景:
約定:我們將進(jìn)程/線程統(tǒng)稱為任務(wù),其中U表示用戶任務(wù)(進(jìn)程/線程),K表示內(nèi)核線程,帶有數(shù)字表示同一個線程組中的線程。
有以下任務(wù):Ua1 Ua2 Ub Uc Ka Kb (eg:Ua1為用戶進(jìn)程, Ua2為和Ua1在同一線程組的用戶進(jìn)程,Ub普通的用戶進(jìn)程,Ka普通的內(nèi)核線程 )。
如果調(diào)度順序如下:
Uc -> Ua1 -> Ua2 -> Ub -> Ka -> Kb -> Ub
從Uc -> Ua1 由于是不同的進(jìn)程,需要切換地址空間。
從 Ua1 -> Ua2 由于是相同線程組中的不同線程,共享地址空間,在切換到Ua1的時候已經(jīng)切換了地址空間,所有不需要切換地址空間。
從 Ua2 -> Ub 由于是不同的進(jìn)程,需要切換地址空間。
從 Ub -> Ka 由于切換到內(nèi)核線程,所以不需要切換地址空間。
從Ka -> Kb 倆內(nèi)核線程之前切換,不需要切換地址空間。
從Kb -> Ub 從內(nèi)核線程切換到用戶進(jìn)程,由于Ka和Kb都是借用Ub的active_mm,而Ub的active_mm 等于Ub的mm,所以這個時候 Kb的active_mm和 Ub的mm相同,所有也不會切換地址空間。
如下為多任務(wù)地址空間切換示例圖:

5. 進(jìn)程切換全景視圖
我們以下場景為例:
A,B兩個進(jìn)程都是普通的用戶進(jìn)程,從進(jìn)程A切換到進(jìn)程B,簡單起見我們在這里不考慮其他的搶占時機,我們假設(shè)A,B進(jìn)程只是循環(huán)進(jìn)行一些基本的運算操作,從來不調(diào)用任何系統(tǒng)調(diào)用,只考慮被時鐘中斷,返回用戶空間之前被搶占的情況。
下面給出進(jìn)程切換的全景視圖:

視圖中已經(jīng)講解很清楚,需要強調(diào)3個關(guān)鍵點:
1.發(fā)生中斷時的保存現(xiàn)場,將發(fā)生中斷時的所有通用寄存器保存到進(jìn)程的內(nèi)核棧,使用struct pt_regs結(jié)構(gòu)。
2.地址空間切換將進(jìn)程自己的頁全局目錄的基地址pgd保存在ttbr0_le1中,用于mmu的頁表遍歷的起始點。
3.硬件上下文切換的時候,將此時的調(diào)用保存寄存器和pc, sp保存到struct cpu_context結(jié)構(gòu)中。做好了這幾個保存工作,當(dāng)進(jìn)程再次被調(diào)度回來的時候,通過cpu_context中保存的pc回到了cpu_switch_to的下一條指令繼續(xù)執(zhí)行,而由于cpu_context中保存的sp導(dǎo)致當(dāng)前進(jìn)程回到自己的內(nèi)核棧,經(jīng)過一系列的內(nèi)核棧的出棧處理,最后將原來保存在pt_regs中的通用寄存器的值恢復(fù)到了通用寄存器,這樣進(jìn)程回到用戶空間就可以繼續(xù)沿著被中斷打斷的下一條指令開始執(zhí)行,用戶棧也回到了被打斷之前的位置,而進(jìn)程訪問的指令數(shù)據(jù)做地址轉(zhuǎn)化(VA到PA)也都是從自己的pgd開始進(jìn)行,一切對用戶來說就好像沒有發(fā)生一樣,簡直天衣無縫。
感謝各位的閱讀,以上就是“Linux內(nèi)核進(jìn)程上下文切換怎么理解”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Linux內(nèi)核進(jìn)程上下文切換怎么理解這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
分享題目:Linux內(nèi)核進(jìn)程上下文切換怎么理解
文章網(wǎng)址:http://chinadenli.net/article30/pgpdso.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、自適應(yīng)網(wǎng)站、網(wǎng)站導(dǎo)航、移動網(wǎng)站建設(shè)、網(wǎng)站收錄、網(wǎng)站策劃
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)