亚洲av成人无遮挡网站在线观看,少妇性bbb搡bbb爽爽爽,亚洲av日韩精品久久久久久,兔费看少妇性l交大片免费,无码少妇一区二区三区

  免費注冊 查看新帖 |

Chinaunix

  平臺 論壇 博客 文庫
最近訪問板塊 發(fā)新帖
查看: 2154 | 回復(fù): 0
打印 上一主題 下一主題

【轉(zhuǎn)】Linux進(jìn)程:Linux切換機制主流程 [復(fù)制鏈接]

論壇徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辭舊歲徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
跳轉(zhuǎn)到指定樓層
1 [收藏(0)] [報告]
發(fā)表于 2008-12-16 22:28 |只看該作者 |倒序瀏覽
Linux切換并沒有使用X86CPU的切換方法,Linux切換的實質(zhì)就是cr3切換(內(nèi)存空間切換,在switch_mm函數(shù)中)+ 寄存器切換(包括EIP,ESP等,均在switch_to函數(shù)中)。這里我們講述下switch_to主流程:

在switch_mm函數(shù)中將new_task->pgd設(shè)置到cr3寄存器中,實現(xiàn)頁表切換,由于每個進(jìn)程 3-4G的頁表映射機制完全一樣(從內(nèi)核頁表中直接復(fù)制過來的),故這里雖然切換了pgd,但是并無影響,只是在任務(wù)回到用戶空間中時,才會發(fā)生變化,因為每個任務(wù)在0-3G中的頁表映射都是各自獨立的;[注意]因為cr3指向的是"1st level"頁表, 次級頁表(2dn level)本身的address是固定不變的(內(nèi)容可變),所以,內(nèi)核空間所對應(yīng)的一級頁表內(nèi)容是不變的. 在copy_mm()->dup_mm()時進(jìn)行了copy.

壓入esi edi ebp到cur_task堆棧中;
將esp寄存器中的值保存到cur_task.task_struct.thread.esp中,也就是將cur_task切換時的堆棧指針保存起來;
將 new_task.task_struct.thread.esp中的值設(shè)置到esp寄存器中,這里的 new_task.task_struct.thread.esp中的值就是new_task上一次被換出時的堆棧指針,現(xiàn)在被恢復(fù)了,2和3結(jié)合實現(xiàn)了從cur_task到new_task的堆棧切換;
將1f地址設(shè)置到cur_task.task_struct.thread.eip中,當(dāng)下次cur_task恢復(fù)運行時,將會從1f處開始運行,下面闡述了這種原理;
將 new_task.task_struct.thread.eip壓入到new_task的堆棧中,這里 new_task.task_struct.thread.eip的值就是1f,因為從4中可知,new_task上一次被換出時,其也是和現(xiàn)在的 cur_task類似,1f地址被設(shè)置到new_task.task_struct.thread.eip中;
隨后CPU跳轉(zhuǎn)到 __switch_to函數(shù)(注意,在__switch_to中,做了一件非常重要的事情就是讓 init_tss.esp0=new_task.task_struct.esp0,原因見【C.Linux進(jìn)程:Linux進(jìn)程管理和X86進(jìn)程管理的結(jié)合】一文)中開始執(zhí)行,注意這里使用的是jmp,不是call,call會pusheip,而jmp不會,由于__switch_to是函數(shù),當(dāng)CPU 執(zhí)行完該函數(shù)后,最后一條指令必然為iret,該指令會popeip,從5中可以知道,此時new_task堆棧中的鏡像為[......., esi,edi,ebp,eip(&1f)],故popeip將值eip(&1f)設(shè)置到eip寄存器中,這樣當(dāng)iret執(zhí)行完畢后, CPU將從eip處繼續(xù)執(zhí)行,也就是從1f處繼續(xù)執(zhí)行;;
此時已經(jīng)在new_task的執(zhí)行環(huán)境中了,pop ebp, pop edi, popesi,回到schedule函數(shù)中,當(dāng)返回用戶空間中時,由于new_task用戶空間的eip,ss,esp等均被從new_task的堆棧中彈出到對應(yīng)寄存器中,從而new_task得以順利執(zhí)行;

switch_to中cur_task寄存器保存、new_task寄存器恢復(fù)的幾個問題:

GCC在編譯該段代碼時,會注意到EAX,EBX,EDX在這里被使用,因此此處無需要顯示的用匯編語句來保存這三個寄存器,GCC在編譯時會自動考慮添加對這些寄存器的保護(hù);
由于在內(nèi)核中所有的內(nèi)核段寄存器均為統(tǒng)一的,因此這里無需保存ES,CS,SS,DS,FS,GS;
CR3(Linux中沒有使用LDT)已經(jīng)在前面的switch_mm處理了;
由于Linux沒使用TSS-previous task link field,其切換完全采用軟件處理切換,故這里無需考慮TSS-previous task link field;
指令EIP會保存在task.thread.eip中,ESP會保存在task.thread.esp中,EBP,ESI,EDI會用顯示指令入棧保存;
對于Linux而言,其僅僅使用到了CPU的4級機制中的0和3兩級,使用方法如下:
當(dāng)進(jìn)程正運行在用戶空間時,如果此時來了個中斷,CPU將會執(zhí)行:從TR->GDTR[i ]->TSS中取出當(dāng)前的SS0:ESP0,從IDTR->IDT[i ]中取出執(zhí)行代碼CS:IP,將當(dāng)前所有寄存器壓到SS0:ESP0堆棧中,包括進(jìn)程的SS3:ESP3,隨后從CS:IP處開始執(zhí)行代碼。當(dāng)中斷代碼執(zhí)行完畢后,內(nèi)核將會從進(jìn)程堆棧中,將SS3:ESP3、CS:IP彈出,從而回到用戶空間重新開始執(zhí)行,此時并不需要CPU主動來切換級別了;從這里可知,CPU是需要TSS中的SS0和ESP0來進(jìn)行高->低級別切換的,因此進(jìn)程在切換時,必須要將自己的SS0和ESP0保存到 TR->GDTR[i ]->TSS的SS0和ESP0字段中去,其實,在Linux中,對于同一個CPU,所有的進(jìn)程都使用一個TSS,只是在進(jìn)程切換時,被切換到的進(jìn)程將會把自己的ESP0保存到TSS.ESP0中去(在函數(shù)__switch_to中),那為什么不把自己的SS0也保存到TSS.SS0中呢,這是因為所有進(jìn)程的SS0都是統(tǒng)一的,為內(nèi)核的SS,而內(nèi)核在初始化的時候,已經(jīng)將該TSS.SS0設(shè)置為自己的SS,因此無需繼續(xù)設(shè)置SS0;
至于EFLAGS為什么沒有保存,這點在2.6中已經(jīng)糾正,即執(zhí)行了pushf和popf;
ECX為什么沒有保存,則涉及到了如下的理由:i386 ABI / function calling sequence
Allregisters on the Intel386 are global and thus visible to both a callingand a called function. Registers %ebp, %ebx, %edi,%esi, and %esp'belong' to the calling function. In other words, a called functionmust preserve these registers' values for its caller. Remainingregisters 'belong' to the called function. If a calling function wantsto preserve such a register value across a function call, it must savethe value in its local stack frame.Some registers have assigned rolesin the standard calling sequence:
%esp:The stack pointerholds the limit of the current stack frame, which is the address of thestack’s bottom-most, valid word. At all times, the stack pointer shouldpoint to a word-aligned area.
......
%ecx and%edx:Scratch registers have no specified role in the standard callingsequence. Functions do not have to preserve their values for the caller.
......

Linux進(jìn)程管理和X86進(jìn)程管理的結(jié)合

從上面描述中,我們知道X86要求每個進(jìn)程都必須有自己的TSS,在每次進(jìn)程切換的時候,通過對應(yīng)的TR[i ]+GDTRbase找到該進(jìn)程的TSS,然后保存前一個任務(wù)的所有寄存器,同時將找到的TSS中所有的寄存器值恢復(fù)到系統(tǒng)對應(yīng)的寄存器中,從而實現(xiàn)進(jìn)程切換。
由于Linux實現(xiàn)進(jìn)程切換的時候,并不采用該機制,但是卻避不過X86這個機制,因此Linux內(nèi)核設(shè)置了init_tss全局變量,在 start_kernel->trap_init->cpu_init初始化時,設(shè)置了對應(yīng)的GDT[i ]指向該init_tss以及TR.index=i;此后,在Linux系統(tǒng)運行過程中,就再也不會改變TR以及該GDT[i ],對應(yīng)X86來講,就好像永遠(yuǎn)運行著1個進(jìn)程;Linux使用自身的切換機制來實現(xiàn)了進(jìn)程的切換,這些在上面的文章中已經(jīng)說明,這里不在多說。
另一重要問題Linux必須面對:當(dāng)從高級別切換到低級別時,會引起CPU運行級別的變化,例如從級別3到級別0,此時CPU需要獲取0級別的SS0以及 ESP0(例如前面描述的當(dāng)進(jìn)程正運行在用戶空間時來了個中斷范例)來恢復(fù)SS:ESP,其設(shè)計方法就是從當(dāng)前的TSS->SS0:ESP0中獲取。為了適應(yīng)CPU的這種設(shè)計,Linux內(nèi)核在每次switch_to切換進(jìn)程時,都將被切換來的進(jìn)程的ESP0保存到init_tss.esp0中;另外由于Linux內(nèi)核的SS0始終為KERNEL_SS保持不變,故無需每次切換都將其保存到init_tss.ss0中,只需要在Linux內(nèi)核初始化時將init_tss.ss0設(shè)置為KERNEL_SS就可以了;
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則 發(fā)表回復(fù)

  

北京盛拓優(yōu)訊信息技術(shù)有限公司. 版權(quán)所有 京ICP備16024965號-6 北京市公安局海淀分局網(wǎng)監(jiān)中心備案編號:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年舉報專區(qū)
中國互聯(lián)網(wǎng)協(xié)會會員  聯(lián)系我們:huangweiwei@itpub.net
感謝所有關(guān)心和支持過ChinaUnix的朋友們 轉(zhuǎn)載本站內(nèi)容請注明原作者名及出處

清除 Cookies - ChinaUnix - Archiver - WAP - TOP