1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        深入理解Linux內(nèi)核進(jìn)程上下文切換

        共 10624字,需瀏覽 22分鐘

         ·

        2021-01-05 19:57

        在原作者基礎(chǔ)上修改了些文字描述,讓文章更加通俗易懂


        作者簡(jiǎn)介


        韓傳華,就職于南京大魚半導(dǎo)體有限公司,主要從事linux相關(guān)系統(tǒng)軟件開(kāi)發(fā)工作,負(fù)責(zé)Soc芯片BringUp及系統(tǒng)軟件開(kāi)發(fā),樂(lè)于分享喜歡學(xué)習(xí),喜歡專研Linux內(nèi)核源代碼。


        我們都知道操作系統(tǒng)的一個(gè)重要功能就是進(jìn)行進(jìn)程管理,而進(jìn)程管理就是在合適的時(shí)機(jī)選擇合適的進(jìn)程來(lái)執(zhí)行,在單個(gè)cpu運(yùn)行隊(duì)列上各個(gè)進(jìn)程宏觀并行微觀串行執(zhí)行,多個(gè)cpu運(yùn)行隊(duì)列上的各個(gè)進(jìn)程之間完全的并行執(zhí)行。

        進(jìn)程管理是個(gè)復(fù)雜的過(guò)程,例如進(jìn)程的描述、創(chuàng)建和銷毀、生命周期管理、進(jìn)程切換、進(jìn)程搶占、調(diào)度策略、負(fù)載均衡等等。

        本文主要關(guān)注進(jìn)程管理的一個(gè)切入點(diǎn),那就是進(jìn)程的上下文切換,來(lái)理解linux內(nèi)核是如何進(jìn)行進(jìn)程上下文切換的,從而揭開(kāi)上下文切換的神秘面紗。


        「注意:本文以linux-5.0內(nèi)核源碼講解,采用arm64架構(gòu)」


        本文目錄:
        1.進(jìn)程上下文的概念
        2.上下文切換詳細(xì)過(guò)程
         2.1 進(jìn)程地址空間切換
         2.2 處理器狀態(tài)(硬件上下文)切換
        3.ASID機(jī)制
        4.普通用戶進(jìn)程、普通用戶線程、內(nèi)核線程切換的差別
        5.進(jìn)程切換全景視圖
        6.總結(jié)


        1、進(jìn)程上下文的概念

        進(jìn)程上下文是進(jìn)程執(zhí)行活動(dòng)全過(guò)程的靜態(tài)描述。

        我們把已執(zhí)行過(guò)的進(jìn)程指令和數(shù)據(jù)在相關(guān)寄存器與堆棧中的內(nèi)容稱為進(jìn)程上文,把正在執(zhí)行的指令和數(shù)據(jù)在寄存器與堆棧中的內(nèi)容稱為進(jìn)程正文,把待執(zhí)行的指令和數(shù)據(jù)在寄存器與堆棧中的內(nèi)容稱為進(jìn)程下文。

        實(shí)際上linux內(nèi)核中,進(jìn)程上下文包括進(jìn)程的虛擬地址空間和硬件上下文。

        進(jìn)程硬件上下文包含了當(dāng)前cpu的一組寄存器的集合,arm64中使用task_struct結(jié)構(gòu)的thread成員的cpu_context成員來(lái)描述,包括x19-x28,sp, pc等。

        如下為硬件上下文存放示例圖:


        2、上下文切換詳細(xì)過(guò)程

        進(jìn)程上下文切換主要涉及到兩部分主要過(guò)程:

        進(jìn)程地址空間切換和處理器狀態(tài)切換。地址空間切換主要是針對(duì)用戶進(jìn)程而言,而處理器狀態(tài)切換對(duì)應(yīng)于所有的調(diào)度單位。下面我們分別看下這兩個(gè)過(guò)程:


        __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)程所擁有的虛擬地址空間,而這個(gè)地址空間是假的,是linux內(nèi)核通過(guò)數(shù)據(jù)結(jié)構(gòu)來(lái)描述出來(lái)的,從而使得每一個(gè)進(jìn)程都感覺(jué)到自己擁有整個(gè)內(nèi)存的假象,cpu訪問(wèn)的指令和數(shù)據(jù)最終會(huì)落實(shí)到實(shí)際的物理地址,對(duì)用進(jìn)程而言通過(guò)缺頁(yè)異常來(lái)分配和建立頁(yè)表映射。進(jìn)程地址空間內(nèi)有進(jìn)程運(yùn)行的指令和數(shù)據(jù),因此到調(diào)度器從其他進(jìn)程重新切換到我的時(shí)候,為了保證當(dāng)前進(jìn)程訪問(wèn)的虛擬地址是自己的必須切換地址空間。
        實(shí)際上,進(jìn)程地址空間使用mm_struct結(jié)構(gòu)體來(lái)描述,這個(gè)結(jié)構(gòu)體被嵌入到進(jìn)程描述符(我們通常所說(shuō)的進(jìn)程控制塊PCB)task_struct中,mm_struct結(jié)構(gòu)體將各個(gè)vma組織起來(lái)進(jìn)行管理,其中有一個(gè)成員pgd至關(guān)重要,地址空間切換中最重要的是pgd的設(shè)置。

        pgd中保存的是進(jìn)程的頁(yè)全局目錄的虛擬地址(本文會(huì)涉及到頁(yè)表相關(guān)的一些概念,在此不是重點(diǎn),不清楚的可以查閱相關(guān)資料,后期有機(jī)會(huì)會(huì)講解進(jìn)程頁(yè)表),記住保存的是虛擬地址,那么pgd的值是何時(shí)被設(shè)置的呢?答案是fork的時(shí)候,如果是創(chuàng)建進(jìn)程,需要分配設(shè)置mm_struct,其中會(huì)分配進(jìn)程頁(yè)全局目錄所在的頁(yè),然后將首地址賦值給pgd。

        我們來(lái)看看進(jìn)程地址空間究竟是如何切換的,結(jié)果會(huì)讓你大吃一驚(這里暫且不考慮asid機(jī)制,后面有機(jī)會(huì)會(huì)在其他文章中講解):


        代碼路徑如下:

        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中,這是用戶空間的頁(yè)表基址寄存器,當(dāng)訪問(wèn)用戶空間地址的時(shí)候mmu會(huì)通過(guò)這個(gè)寄存器來(lái)做遍歷頁(yè)表獲得物理地址(ttbr1_el1是內(nèi)核空間的頁(yè)表基址寄存器,訪問(wèn)內(nèi)核空間地址時(shí)使用,所有進(jìn)程共享,不需要切換)。完成了這一步,也就完成了進(jìn)程的地址空間切換,確切的說(shuō)是進(jìn)程的虛擬地址空間切換。

        內(nèi)核處理的是不是很簡(jiǎn)單,很優(yōu)雅,別看只是設(shè)置了頁(yè)表基址寄存器,也就是將即將執(zhí)行的進(jìn)程的頁(yè)全局目錄的物理地址設(shè)置到頁(yè)表基址寄存器,他卻完成了地址空間切換的壯舉,有的小伙伴可能不明白為啥這就完成了地址空間切換?試想如果進(jìn)程想要訪問(wèn)一個(gè)用戶空間虛擬地址,cpu的mmu所做的工作,就是從頁(yè)表基址寄存器拿到頁(yè)全局目錄的物理基地址,然后和虛擬地址配合來(lái)查查找頁(yè)表,最終找到物理地址進(jìn)行訪問(wèn)(當(dāng)然如果tlb命中就不需要遍歷頁(yè)表),每次用戶虛擬地址訪問(wèn)的時(shí)候(內(nèi)核空間共享不考慮),由于頁(yè)表基地址寄存器內(nèi)存放的是當(dāng)前執(zhí)行進(jìn)程的頁(yè)全局目錄的物理地址,所以訪問(wèn)自己的一套頁(yè)表,拿到的是屬于自己的物理地址(實(shí)際上,進(jìn)程是訪問(wèn)虛擬地址空間的指令數(shù)據(jù)的時(shí)候不斷發(fā)生缺頁(yè)異常,然后缺頁(yè)異常處理程序?yàn)檫M(jìn)程分配實(shí)際的物理頁(yè),然后將頁(yè)幀號(hào)和頁(yè)表屬性填入自己的頁(yè)表?xiàng)l目中),就不會(huì)訪問(wèn)其他進(jìn)程的指令和數(shù)據(jù),這也是為何多個(gè)進(jìn)程可以訪問(wèn)相同的虛擬地址而不會(huì)出現(xiàn)差錯(cuò)的原因,而且做到的各個(gè)地址空間的隔離互不影響(共享內(nèi)存除外)。

        其實(shí),地址空間切換過(guò)程中,還會(huì)清空tlb,防止當(dāng)前進(jìn)程虛擬地址轉(zhuǎn)化過(guò)程中命中上一個(gè)進(jìn)程的tlb表項(xiàng),一般會(huì)將所有的tlb無(wú)效,但是這會(huì)導(dǎo)致很大的性能損失,因?yàn)樾逻M(jìn)程被切換進(jìn)來(lái)的時(shí)候面對(duì)的是全新的空的tlb,造成很大概率的tlb miss,需要重新遍歷多級(jí)頁(yè)表,所以arm64在tlb表項(xiàng)中增加了非全局(nG)位區(qū)分內(nèi)核和進(jìn)程的頁(yè)表項(xiàng),使用ASID區(qū)分不同進(jìn)程的頁(yè)表項(xiàng),來(lái)保證可以在切換地址空間的時(shí)候可以不刷tlb,后面會(huì)主要講解ASID技術(shù)。

        還需要注意的是僅僅切換用戶地址空間,內(nèi)核地址空間由于是共享的不需要切換,也就是為何切換到內(nèi)核線程不需要也沒(méi)有地址空間的原因。

        如下為進(jìn)程地址空間切換示例圖:



        2.2 處理器狀態(tài)(硬件上下文)切換

        前面進(jìn)行了地址空間切換,只是保證了進(jìn)程訪問(wèn)指令數(shù)據(jù)時(shí)訪問(wèn)的是自己地址空間(當(dāng)然上下文切換的時(shí)候處于內(nèi)核空間,執(zhí)行的是內(nèi)核地址數(shù)據(jù),當(dāng)返回用戶空間的時(shí)候才有機(jī)會(huì)執(zhí)行用戶空間指令數(shù)據(jù)**,地址空間切換為進(jìn)程訪問(wèn)自己用戶空間做好了準(zhǔn)備**),但是進(jìn)程執(zhí)行的內(nèi)核棧還是前一個(gè)進(jìn)程的,當(dāng)前執(zhí)行流也還是前一個(gè)進(jìn)程的,需要做切換。

        arm64中切換代碼如下:


        switch_to
        ->__switch_to
        ...?//浮點(diǎn)寄存器等的切換
        ->cpu_switch_to(prev,?next)

        arch/arm64/kernel/entry.S:
        1032?/*
        1033??*?Register?switch?for?AArch64.?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)切換的時(shí)候?qū)⑶耙粋€(gè)進(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中,用于通過(guò)current找到當(dāng)前進(jìn)程,這樣就完成了處理器的狀態(tài)切換。

        實(shí)際上,處理器狀態(tài)切換就是將前一個(gè)進(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)識(shí),這塊內(nèi)存這就是cpu_contex這個(gè)結(jié)構(gòu)的位置(進(jìn)程切換都是在內(nèi)核空間完成)。

        由于用戶空間通過(guò)異常/中斷進(jìn)入內(nèi)核空間的時(shí)候都需要保存現(xiàn)場(chǎng),也就是保存發(fā)生異常/中斷時(shí)的所有通用寄存器的值,內(nèi)核會(huì)把“現(xiàn)場(chǎng)”保存到每個(gè)進(jìn)程特有的進(jìn)程內(nèi)核棧中,并用pt_regs結(jié)構(gòu)來(lái)描述,當(dāng)異常/中斷處理完成之后會(huì)返回用戶空間,返回之前會(huì)恢復(fù)之前保存的“現(xiàn)場(chǎng)”,用戶程序繼續(xù)執(zhí)行。
        所以當(dāng)進(jìn)程切換的時(shí)候,當(dāng)前進(jìn)程被時(shí)鐘中斷打斷,將發(fā)生中斷時(shí)的現(xiàn)場(chǎng)保存到進(jìn)程內(nèi)核棧(如:sp, lr等),然后會(huì)切換到下一個(gè)進(jìn)程,當(dāng)再次回切換回來(lái)的時(shí)候,返回用戶空間的時(shí)候會(huì)恢復(fù)之前的現(xiàn)場(chǎng),進(jìn)程就可以繼續(xù)執(zhí)行(執(zhí)行之前被中斷打斷的下一條指令,繼續(xù)使用自己用戶態(tài)sp),這對(duì)于用戶進(jìn)程來(lái)說(shuō)是透明的。

        如下為硬件上下文切換示例圖:



        3、ASID機(jī)制

        前面講過(guò),進(jìn)程切換的時(shí)候,由于tlb中存放的可能是其他進(jìn)程的tlb表項(xiàng),所有才需要在進(jìn)程切換的時(shí)候進(jìn)行tlb的清空工作(清空即是使得所有的tlb表項(xiàng)無(wú)效,地址轉(zhuǎn)換需要遍歷多級(jí)頁(yè)表,找到頁(yè)表項(xiàng),然后重新加載頁(yè)表項(xiàng)到tlb),有了ASID機(jī)制之后,命中tlb表項(xiàng),由虛擬地址和ASID共同決定(當(dāng)然還有nG位),可以減小進(jìn)程切換中tlb被清空的機(jī)會(huì)。

        下面我們講解ASID機(jī)制,ASID(Address Space Identifer 地址空間標(biāo)識(shí)符),用于區(qū)別不同進(jìn)程的頁(yè)表項(xiàng),arm64中,可以選擇兩種ASID長(zhǎng)度8位或者16位,這里以8位來(lái)講解。

        如果ASID長(zhǎng)度為8位,那么ASID有256個(gè)值,但是由于0是保留的,所有可以分配的ASID范圍就為1-255,那么可以標(biāo)識(shí)255個(gè)進(jìn)程,當(dāng)超出255個(gè)進(jìn)程的時(shí)候,會(huì)出現(xiàn)兩個(gè)進(jìn)程的ASID相同的情況,因此內(nèi)核使用了ASID版本號(hào)。
        內(nèi)核中處理如下(參考arch/arm64/mm/context.c):

        1)內(nèi)核為每個(gè)進(jìn)程分配一個(gè)64位的軟件ASID,其中低8位為硬件ASID,高56位為ASID版本號(hào),這個(gè)軟件ASID存放放在進(jìn)程的mm_struct結(jié)構(gòu)的context結(jié)構(gòu)的id中,進(jìn)程創(chuàng)建的時(shí)候會(huì)初始化為0。
        2)內(nèi)核中有一個(gè)64位的全局變量asid_generation,同樣它的高56位為ASID版本號(hào),用于標(biāo)識(shí)當(dāng)前ASID分配的批次。
        3)當(dāng)進(jìn)程調(diào)度,由prev進(jìn)程切換到next進(jìn)程的時(shí)候,如果不是內(nèi)核線程則進(jìn)行地址空間切換調(diào)用check_and_switch_context,此函數(shù)會(huì)判斷next進(jìn)程的ASID版本號(hào)是否和全局的ASID版本號(hào)相同(是否處于同一批次),如果相同則不需要為next進(jìn)程分配ASID,不相同則需要分配ASID。
        4)內(nèi)核使用asid_map位圖來(lái)管理硬件ASID的分配,asid_bits記錄使用的ASID的長(zhǎng)度,每處理器變量active_asids保存當(dāng)前分配的硬件ASID,每處理器變量reserved_asids存放保留的ASID,tlb_flush_pending位圖記錄需要清空tlb的cpu集合。

        硬件ASID分配策略如下:
        (1)如果進(jìn)程的ASID版本號(hào)和當(dāng)前全局的ASID版本號(hào)相同(同批次情況),則不需要重新分配ASID。
        (2)如果進(jìn)程的ASID版本號(hào)和當(dāng)前全局的ASID版本號(hào)不相同(不同批次情況),且進(jìn)程原本的硬件ASID已經(jīng)被分配,則重新分配新的硬件ASID,并將當(dāng)前全局的ASID版本號(hào)組合新分配的硬件ASID寫到進(jìn)程的軟件ASID中。
        (3)如果進(jìn)程的ASID版本號(hào)和當(dāng)前全局的ASID版本號(hào)不相同(不同批次情況),且進(jìn)程原本的硬件ASID還沒(méi)有被分配,則不需要重新分配新的硬件ASID,只需要更新進(jìn)程軟件ASID版本號(hào),并將當(dāng)前全局的ASID版本號(hào)組合進(jìn)程原來(lái)的硬件ASID寫到進(jìn)程的軟件ASID中。
        (4)如果進(jìn)程的ASID版本號(hào)和當(dāng)前全局的ASID版本號(hào)不相同(不同批次情況),需要分配硬件ASID時(shí),發(fā)現(xiàn)硬件ASID已經(jīng)被其他進(jìn)程分配完(asid_map位圖中查找,發(fā)現(xiàn)位圖全1),則這個(gè)時(shí)候需要遞增全局的ASID版本號(hào), 清空所有cpu的tlb, 清空asid_map位圖,然后分配硬件ASID,并將當(dāng)前全局的ASID版本號(hào)組合新分配的硬件ASID寫到進(jìn)程的軟件ASID中。

        下面我們以實(shí)例來(lái)看ASID的分配過(guò)程:

        如下圖:


        我們假設(shè)圖中從A進(jìn)程到D進(jìn)程,有255個(gè)進(jìn)程,剛好分配完了asid, ,從A到D的切換過(guò)程中使用的都是同一批次的asid版本號(hào)。

        則這個(gè)過(guò)程中,有進(jìn)程會(huì)創(chuàng)建的時(shí)候被切換到,假設(shè)不超出255個(gè)進(jìn)程,在切換過(guò)程中會(huì)為新進(jìn)程分配硬件的ASID,分配完后下次切換到他時(shí)由于他的ASID版本號(hào)和當(dāng)前的全局的ASID版本號(hào)相同,所以不需要再次分配ASID,當(dāng)然也不需要清空tlb。

        注:這里說(shuō)的ASID即為硬件ASID區(qū)別于ASID版本號(hào)。

        情況1-ASID版本號(hào)不變 屬于策略(1):從C進(jìn)程到D進(jìn)程切換,內(nèi)核判斷D進(jìn)程的ASID版本號(hào)和當(dāng)前的全局的ASID版本號(hào)相同,所以不需要為他分配ASID(執(zhí)行快速路徑switch_mm_fastpath去設(shè)置ttbrx_el1))。

        情況2 -硬件ASID全部分配完 屬于策略(4):假設(shè)到達(dá)D進(jìn)程時(shí),asid已經(jīng)全部分配完(系統(tǒng)中有255個(gè)進(jìn)程都分配到了硬件asid號(hào)),這個(gè)時(shí)候新創(chuàng)建的進(jìn)程E被調(diào)度器選中,切換到E,由于新創(chuàng)建的進(jìn)程的軟件ASID被初始化為0,所以和當(dāng)前的全局的ASID版本號(hào)不同(不在同一批次),則這個(gè)時(shí)候會(huì)執(zhí)行new_context為進(jìn)程分配ASID,但是由于沒(méi)有可以分配的ASID,所以會(huì)將全局的ASID版本號(hào)加1(發(fā)生ASID回繞),這個(gè)時(shí)候全局的ASID為801,然后清空asid_map,置位tlb_flush_pending所有bit用于清空所有cpu的tlb,然后再次去分配硬件ASID給E進(jìn)程,這個(gè)時(shí)候分配到了1給他(將ASID版本號(hào))。

        情況3-ASID版本號(hào)發(fā)生變化,進(jìn)程的硬件ASID可以再次使用 屬于策略(3):假設(shè)從E切換到了B進(jìn)程,而B進(jìn)程之前已經(jīng)在全局的ASID版本號(hào)為800的批次上分配了編號(hào)為5的硬件ASID,但是B進(jìn)程的ASID版本號(hào)800和現(xiàn)在全局的ASID版本號(hào)801不相同,所有需要new_context為進(jìn)程分配ASID,分配的時(shí)候發(fā)現(xiàn)asid_map中編號(hào)為5沒(méi)有被置位,也就是沒(méi)有其他進(jìn)程分配了5這個(gè)ASID,所有可以繼續(xù)使用原來(lái)分配的硬件ASID 5。

        情況4 -?ASID版本號(hào)發(fā)生變化,有其他進(jìn)程已經(jīng)分配了相同的硬件ASID 屬于策略(2): 假設(shè)從B進(jìn)程切換到A進(jìn)程,而B進(jìn)程之前已經(jīng)在全局的ASID版本號(hào)為800的批次上分配了編號(hào)為1的硬件ASID,但是B進(jìn)程的ASID版本號(hào)800和現(xiàn)在全局的ASID版本號(hào)801不相同,所有需要new_context為進(jìn)程分配ASID,分配的時(shí)候發(fā)現(xiàn)asid_map中編號(hào)為1已經(jīng)被置位,也就是其他進(jìn)程已經(jīng)分配了1這個(gè)ASID,需要從asid_map尋找下一個(gè)空閑的ASID,則分配了新的ASID為6。

        假設(shè)從A到E,由于E的ASID版本號(hào)和全局的ASID版本號(hào)(同一批次),和情況1相同,不需要分配ASID。但是之前原來(lái)處于800這個(gè)ASID版本號(hào)批次的進(jìn)程都需要重新分配ASID,有的可以使用原來(lái)的硬件ASID,有的重新分配硬件ASID,但是都將ASID版本號(hào)修改為了現(xiàn)在全局的ASID版本號(hào)801。但是,隨著硬件ASID的不斷分配,最終處于801這一批次的硬件ASID也會(huì)分配完,這個(gè)時(shí)候就是上面的情況2,要情況所有cpu的tlb。

        我可以看到有了ASID機(jī)制之后,由于只有當(dāng)硬件ASID被分配完了(如被255個(gè)進(jìn)程使用),發(fā)生回繞的時(shí)候才會(huì)清空所有cpu的tlb,大大提高了系統(tǒng)的性能(沒(méi)有ASID機(jī)制的情況下每次進(jìn)程切換需要地址空間切換的時(shí)候都需要清空tlb)。

        4、普通用戶進(jìn)程、普通用戶線程、內(nèi)核線程切換的差別

        內(nèi)核地址空間切換的時(shí)候有一下原則:看的是進(jìn)程描述符的mm_struct結(jié)構(gòu),即是成員mm:
        1)如果mm為NULL,則表示即將切換的是內(nèi)核線程,不需要切換地址空間(所有任務(wù)共享內(nèi)核地址空間)。
        2)內(nèi)核線程會(huì)借用前一個(gè)用戶進(jìn)程的mm,賦值到自己的active_mm(本身的mm為空),進(jìn)程切換的時(shí)候就會(huì)比較前一個(gè)進(jìn)程的active_mm和當(dāng)前進(jìn)程的mm。
        3)如果前一個(gè)任務(wù)的和即將切換的任務(wù),具有相同的mm成員,也就是共享地址空間的線程則也不需要切換地址空間。

        ->所有的進(jìn)程線程之間進(jìn)行切換都需要切換處理器狀態(tài)。
        ->對(duì)于普通的用戶進(jìn)程之間進(jìn)行切換需要切換地址空間。
        ->同一個(gè)線程組中的線程之間切換不需要切換地址空間,因?yàn)樗麄児蚕硐嗤牡刂房臻g。
        -> 內(nèi)核線程在上下文切換的時(shí)候不需要切換地址空間,僅僅是借用上一個(gè)進(jìn)程mm_struct結(jié)構(gòu)。

        有一下場(chǎng)景:
        約定:我們將進(jìn)程/線程統(tǒng)稱為任務(wù),其中U表示用戶任務(wù)(進(jìn)程/線程),K表示內(nèi)核線程,帶有數(shù)字表示同一個(gè)線程組中的線程。
        有以下任務(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的時(shí)候已經(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,所以這個(gè)時(shí)候 Kb的active_mm和 Ub的mm相同,所有也不會(huì)切換地址空間。

        如下為多任務(wù)地址空間切換示例圖:



        5、進(jìn)程切換全景視圖

        我們以下場(chǎng)景為例:
        A,B兩個(gè)進(jìn)程都是普通的用戶進(jìn)程,從進(jìn)程A切換到進(jìn)程B,簡(jiǎn)單起見(jiàn)我們?cè)谶@里不考慮其他的搶占時(shí)機(jī),我們假設(shè)A,B進(jìn)程只是循環(huán)進(jìn)行一些基本的運(yùn)算操作,從來(lái)不調(diào)用任何系統(tǒng)調(diào)用,只考慮被時(shí)鐘中斷,返回用戶空間之前被搶占的情況。

        下面給出進(jìn)程切換的全景視圖:

        視圖中已經(jīng)講解很清楚,需要強(qiáng)調(diào)3個(gè)關(guān)鍵點(diǎn):

        1.發(fā)生中斷時(shí)的保存現(xiàn)場(chǎng),將發(fā)生中斷時(shí)的所有通用寄存器保存到進(jìn)程的內(nèi)核棧,使用struct pt_regs結(jié)構(gòu)。

        2.地址空間切換將進(jìn)程自己的頁(yè)全局目錄的基地址pgd保存在ttbr0_le1中,用于mmu的頁(yè)表遍歷的起始點(diǎn)。

        3.硬件上下文切換的時(shí)候,將此時(shí)的調(diào)用保存寄存器和pc, sp保存到struct cpu_context結(jié)構(gòu)中。做好了這幾個(gè)保存工作,當(dāng)進(jìn)程再次被調(diào)度回來(lái)的時(shí)候,通過(guò)cpu_context中保存的pc回到了cpu_switch_to的下一條指令繼續(xù)執(zhí)行,而由于cpu_context中保存的sp導(dǎo)致當(dāng)前進(jìn)程回到自己的內(nèi)核棧,經(jīng)過(guò)一系列的內(nèi)核棧的出棧處理,最后將原來(lái)保存在pt_regs中的通用寄存器的值恢復(fù)到了通用寄存器,這樣進(jìn)程回到用戶空間就可以繼續(xù)沿著被中斷打斷的下一條指令開(kāi)始執(zhí)行,用戶棧也回到了被打斷之前的位置,而進(jìn)程訪問(wèn)的指令數(shù)據(jù)做地址轉(zhuǎn)化(VA到PA)也都是從自己的pgd開(kāi)始進(jìn)行,一切對(duì)用戶來(lái)說(shuō)就好像沒(méi)有發(fā)生一樣,簡(jiǎn)直天衣無(wú)縫。

        6、總結(jié)

        進(jìn)程管理中最重要的一步要進(jìn)行進(jìn)程上下文切換,其中主要有兩大步驟:地址空間切換和處理器狀態(tài)切換(硬件上下文切換),前者保證了進(jìn)程回到用戶空間之后能夠訪問(wèn)到自己的指令和數(shù)據(jù)(其中包括減小tlb清空的ASID機(jī)制),后者保證了進(jìn)程內(nèi)核棧和執(zhí)行流的切換,會(huì)將當(dāng)前進(jìn)程的硬件上下文保存在進(jìn)程所管理的一塊內(nèi)存,然后將即將執(zhí)行的進(jìn)程的硬件上下文從內(nèi)存中恢復(fù)到寄存器,有了這兩步的切換過(guò)程保證了進(jìn)程運(yùn)行的有條不紊,當(dāng)然切換的過(guò)程是在內(nèi)核空間完成,這對(duì)于進(jìn)程來(lái)說(shuō)是透明的。


        推薦閱讀:
        專輯|Linux文章匯總
        專輯|程序人生
        專輯|C語(yǔ)言
        我的知識(shí)小密圈




        瀏覽 81
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            成人免费无码婬片在线观看 | 粉嫩视频在线 | 一起草在线视频 | 免费黄片入口 | 成年免费视频黄网站在线观看 | 差差差 | 大巴车男人狂躁女人小说 | 中文无码在线观看中文字幕av中文 | 黄色小说集| 国产精品白嫩大学美女 |