Linux進(jìn)程調(diào)度切換和虛擬空間管理深入分析_第1頁
Linux進(jìn)程調(diào)度切換和虛擬空間管理深入分析_第2頁
Linux進(jìn)程調(diào)度切換和虛擬空間管理深入分析_第3頁
Linux進(jìn)程調(diào)度切換和虛擬空間管理深入分析_第4頁
Linux進(jìn)程調(diào)度切換和虛擬空間管理深入分析_第5頁
已閱讀5頁,還剩87頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、、Linux進(jìn)程切換深入分析#defi ne CLONE_KERNELun likely(x)(CLONE_FS | CLONE_FILES | CLONE_SIGHAND)創(chuàng)建內(nèi)核線程時使用的CLONES志。1 #definebuiltin_ex pect(!(x), 0)編譯器優(yōu)化,實際返回值x是整型表達(dá)式,0表示并不預(yù)期該事件發(fā)生,也就是 說x為0的可能性很小,這是為了讓編譯器對下面得語句進(jìn)行優(yōu)化。2.進(jìn)程內(nèi)核態(tài)堆棧結(jié)構(gòu):進(jìn)程是動態(tài)實體,進(jìn)程描述符是存放在動態(tài)內(nèi)存中的。在一塊進(jìn)程內(nèi)存區(qū)上,Linux存放了兩個數(shù)據(jù)結(jié)構(gòu):指向task_struct 得thread_info 和內(nèi)核態(tài)的進(jìn)程

2、棧。大小一般2頁8K,這要求頁面幀對齊2的13次幕,在X86上編譯時可以配 置大小為4K。threadnfo 在內(nèi)存區(qū)開始處,內(nèi)核棧從內(nèi)存尾向下增長。在 C 語言中可以用union結(jié)構(gòu)表示:task 指針。CPI寄存器。進(jìn)程繼續(xù)執(zhí)行時必在 Linux 中部分硬件上下文存圖 1. 8K 內(nèi)核棧和進(jìn)程描述符 task_struct 及 thread_info 的相互關(guān)系union thread_unionstruct thread_info thread_info;unsigned long stack2048; /* 1024 for 4KB stacks */;CPU勺esp寄存器用于執(zhí)行堆棧

3、的頂部指針,當(dāng)從用戶態(tài)轉(zhuǎn)向內(nèi)核態(tài)時,進(jìn)程內(nèi) 核棧總是空的,所以 esp 就會執(zhí)行堆棧底部。使用 alloc_thread_info 和 free_thread_info 用于分配和釋放一個存放 thread_info 結(jié)構(gòu)和內(nèi)核堆棧的內(nèi)存區(qū)。內(nèi)核通過當(dāng)前 esp 指針可以很方便的得到 thread_info 結(jié)構(gòu)的地址。 current_thread_info(void) 的原理即如下:movl $0 xffff2000,%ecx /* or 0 xfffff000 for 4KB stacks */andl %esp,%ecxmovl %ecx,pthread_info 中 task 指針是

4、第一個,所以 current 宏相當(dāng)于 current_thread_info( )-task ,從而也就得到每個進(jìn)程有自己獨立得進(jìn)程空間, 所有進(jìn)程共享 須裝入寄存器恢復(fù)得數(shù)據(jù)集稱為硬件上下文環(huán)境。 放在進(jìn)程描述符中,部分存放到內(nèi)核態(tài)堆棧里。Linux 使用 當(dāng)更靈活, 可以控制流程, 留下優(yōu)化空間。每個CPU有一個TSS段。從用戶態(tài)到內(nèi)核 用戶態(tài)進(jìn)程訪問I/O端口時,TSS中的I/O 描述了 TSS格式,init_tss 存放初始TSStss_struct 中的內(nèi)容,進(jìn)程切換時把 thread 中就可以反映當(dāng)前任務(wù)的運行CPU環(huán)境。tss_structstruct tss_struct3.

5、 進(jìn)程切換堆棧原理:每個進(jìn)程有自己獨立得進(jìn)程空間,所有進(jìn)程共享CPI寄存器。進(jìn)程繼續(xù)執(zhí)行時必 須裝入寄存器恢復(fù)得數(shù)據(jù)集稱為硬件上下文環(huán)境。 在 Linux 中部分硬件上下文存 放在進(jìn)程描述符中,部分存放到內(nèi)核態(tài)堆棧里。80 x86體系支持在進(jìn)程TSS段跳轉(zhuǎn)時自動執(zhí)行進(jìn)程硬件上下文切換。軟件方法實現(xiàn)。 軟件方式效率差不多,80 x86用TSS段保存硬件上下文內(nèi)容, 態(tài)切換時,從TSS中取出內(nèi)核棧地址。 訪問位圖可以驗證權(quán)限。 tss_struct 內(nèi)容,每次進(jìn)程切換,內(nèi)核更新TSS中的某些字段,以反映當(dāng)前運行進(jìn)程的權(quán)限等級。每個進(jìn)程有個反映任務(wù) CPU犬態(tài)的thread_struct 結(jié)構(gòu)變量t

6、hread,除 eax、ecx等通用寄存器內(nèi)容保存在內(nèi)核態(tài)堆棧中,其他大部分寄存器都保存在 次結(jié)構(gòu)中。該結(jié)構(gòu)一部分對應(yīng)于 中某些內(nèi)容更新到unsigned shortback_link,_unsigned longesp0;unsigned shortss0,_ss0h;unsigned longesp1;unsigned shortss1,_ss1h;2_SYSENTER_CS */unsigned longesp2;unsigned shortss2,_ss2h;unsigned long_cr3;unsigned longeip;unsigned longeflags;/* ss1 is

7、 used to cacheblh;MSR_* The extra 1unsigned longunsigned longeax,ecx,edx,ebx;unsigned longesp;unsigned longebp;unsigned longesi;unsigned longedi;unsigned shortes, _esh;unsigned shortcs, _csh;unsigned shortss, _ssh;unsigned shortds, _dsh;unsigned shortfs, _fsh;unsigned shortgs, _gsh;unsigned shortldt

8、, _ldth;unsigned shorttrace,io_bitmap_base;/*is there because the CPU will access an* additional byte beyond the end of the IO permission * bitmap. The extra byte must be all 1 bits, and must* be within the limit.*/io_bitmapIO_BITMAP_LONGS + 1;/*bitmap:*/unsigned long io_bitmap_max;struct thread_str

9、uct *io_bitmap_owner;/* pads the TSS to be cacheline-aligned (size is 0 x100)*/unsigned long _cacheline_filler35;/* . and then another 0 x100 bytes for emergency kernel stack*/unsigned long stack64; _attribute_(packed);struct thread_struct/* cached TLS descriptors. */struct desc_struct tls_arrayGDT_

10、ENTRY_TLS_ENTRIES;unsigned longesp0;unsigned longsysenter_cs;unsigned longeip;unsigned longesp;unsigned longfs;* Cache the current maximum and the last task that used the1)進(jìn)程頁表PGD切換;unsigned longgs;/* Hardware debugging registers */unsigned longdebugreg8;/* %db0-7 debug registers */* fault info */un

11、signed longcr2, trap_no, error_code;/* floating point info */union i387_unioni387;/* virtual 86 mode info */struct vm86_structuser * vm86_info;*unsigned longscreen_bitmap;unsigned longv86flags, v86mask, saved_esp0;unsigned intsaved_fs, saved_gs;/* IO permissions */unsigned long*io_bitmap_ptr;unsigne

12、d longiopl;/* max allowed port in the bitmap, in bytes: */unsigned longio_bitmap_max;2)內(nèi)核態(tài)堆棧和硬件上下文切換(包括CPI寄存器);1)獲取 cpu 邏輯號。4 進(jìn)程切換流程解析 switch_to進(jìn)程切換本質(zhì)上兩步:2)內(nèi)核態(tài)堆棧和硬件上下文切換(包括CPI寄存器);1)獲取 cpu 邏輯號。2) 的地址空間:上面兩步通過 context_switch() 實現(xiàn),它通過調(diào)用 switch_mm() 切換進(jìn)程 空間, switch_to 切換內(nèi)核上下文環(huán)境。首先看看 context_switch() 做了

13、些什么:1)進(jìn)程描述符中active_mm執(zhí)行進(jìn)程使用的地址空間,mn執(zhí)行進(jìn)程擁有的地址空間,對于普通進(jìn)程它們相同。對于內(nèi)核線程,它的mm總為NULL所以context_switch() 首先判斷if (!next-mm)即next為內(nèi)核線程,則 使用 prev 的進(jìn)程地址空間:if (!next-mm) next-active_mm = prev-active_mm; atomic_inc(&prev-active_mm-mm_count); enter_lazy_tlb(prev-active_mm, next);否則,如果 next 是普通進(jìn)程,則用 next 進(jìn)程空間替換 prevsw

14、itch_mm(oldmm, mm, next);3)如果 prev 是內(nèi)核線程或者正在退出,則設(shè)置Prev-active_mm 和 runqueue 的 prev_mm為 NULL if (!prev-mm) prev-active_mm = NULL;WARN_ON(rq-prev_mm);rq-prev_mm = oldmm;面看看switch_mm() 如何切換進(jìn)程空間:movl %5,%espnt/* restore ESP2)標(biāo)志。cpu_clear(cpu, prev-cpu_vm_mask) 清除 cpu_vm_mas位3) cpu_tlbstateper_cpu(cpu_tl

15、bstate, cpu).state = TLBSTATE_OK 設(shè)置 狀態(tài)。4) cpu_tlbstateper_cpu(cpu_tlbstate, cpu).active_mm = next 的 active_mm 為 next 。設(shè)置5)cpu _vm_mas標(biāo)志。cpu_set(cpu, next-cpu_vm_mask) 設(shè)置 next 的6)load_cr3(next-pgd) 裝載 next 的 pgd 頁表到 cr3 寄存器。7)如果next的LDT描述符改變,則加載next的LDT描述符。if (unlikely(prev-context.ldt != next-contex

16、t.ldt)load_LDT_nolock(&next-context);最后,switch_to進(jìn)行內(nèi)核堆棧和CPU環(huán)境切換操作:#define switch_to(prev,next,last) dounsigned long esi,edi;asm volatile(pushflnt/* Save flags*/pushl %ebpntmovl %esp,%0nt/* save ESP*/movl %5,%espnt/* restore ESP(prev-thread.eip),=a (last),=S (esi),=D(edi)pushl %6nt/* restore EIP1:tpo

17、pl %ebpnt popfl :=m (prev-thread.esp),=m:m (next-thread.esp),m(next); while (0)流程描述,prev是進(jìn)程A的task結(jié)構(gòu),next是進(jìn)程B的task結(jié)構(gòu),last是進(jìn) */movl $1f,%1nt/* save EIP*/*/jmpswitch_ton(next-thread.eip),2 (prev), d1)程C的結(jié)構(gòu):保存 prev 和 next 指針的值到 eax 和 edx:movl prev, %eaxmovl %5,%espnt/* restore ESPmovl next, %edx2)保存 efl

18、ags 和 ebp 寄存器內(nèi)容到 prev 內(nèi)核態(tài)堆棧中:pushflpushl %ebp3)內(nèi)核堆棧的 top 地址。4)將 next-thread.esp的內(nèi)核堆棧,進(jìn)程切換完成。加載到esp中,現(xiàn)在開始,esp執(zhí)行next5)保存下面 Label 1 到恢復(fù)運行時,從該位置開始運行。prev-thread.eip 指針中, 當(dāng) prev 進(jìn)程6)通常它的內(nèi)容也是將 next-thread.eipLabel 1 。的指針內(nèi)容壓到 next 的內(nèi)核態(tài)堆棧中,7)跳轉(zhuǎn)到 _switch_to()C函數(shù)執(zhí)行。將 esp 內(nèi)容保存到 prev-thread.esp 中,該字段執(zhí)行 prevmovl

19、 %esp,484(%eax)movl 484(%edx), %espmovl $1f, 480(%eax)pushl 480(%edx)jmp _switch_to9)換下來的進(jìn)程C的task結(jié)構(gòu)指針。8)被替換的進(jìn)程A繼續(xù)執(zhí)行,它在Label 1處,首先是恢復(fù)eflags和ebp寄存器內(nèi)容。注意這里是發(fā)生在調(diào)度器選擇 prev在CPULh運行后,次數(shù) esp 已經(jīng)執(zhí)行了 prev 的內(nèi)核堆棧。1:popl %ebppopfl將 eax 內(nèi)容保存到 last 任務(wù)結(jié)構(gòu)中。這里 eax 是被進(jìn)程 A 切movl %eax, last5 _switch_to深入分析switch_to 參數(shù)是存放

20、在eax和edx中的內(nèi)容,這通過#define fastcall 器。attribute_(regparm(3)告訴 gcc 編譯獲取 tss1)結(jié)構(gòu)prev和next、當(dāng)前CPU邏輯ID。struct tss 、 prev_p 和 next_p 的 thread_struct2)調(diào)用_unlazy_fpu(prev_p) 根據(jù)條件標(biāo)志選擇是否保存prev_p 的 FPU, MMX,和 XMM寄存器內(nèi)容。3)load_esp0(tss, next) 將 next 的堆棧地址存放到 tss 中:tss-esp0 = thread-esp0 。4)savesegment(gs, prev-gs)

21、保存 gs 寄存器到 prev-gs , fs已經(jīng)在棧入口保存, es 和 ds 在內(nèi)核態(tài)下不需要保存。5)load_TLS(next, cpu) 從 next 的 tls_array 緩存中加載線程的 Thread-Local Storage 描述符。TLS在 GDT表中位置 6、7、8。cpu_gdt_tablecpu6 = next_p-thread.tls_array0;則恢cpu_gdt_tablecpu7 = next_p-thread.tls_array1;cpu_gdt_tablecpu8 = next_p-thread.tls_array2;6)如果當(dāng)前特權(quán)級別是 0 并且

22、prev-iopl != next-iopl復(fù) I0PL 設(shè)置 set_iopl_mask(next-iopl) 。7)根據(jù) thread_info 的 TIF 標(biāo)志_TIF_WORK_CTXSWTIF_IO_BITMAP判斷是否需要處理 debug寄存器和IO位圖:_switch_to_xtra(next_p, tss);l只有當(dāng) next_p 掛起時即 if(test_tsk_thread_flag(next_p, TIF_DEBUG) 使用了 debug 寄存器才需要恢復(fù) set_debugreg(next-debugregi, i) 。只有調(diào)試器需要監(jiān)控 prev 的狀態(tài)時,Prev_

23、p-thread.debugreg數(shù)組的內(nèi)容才會被修改。Debug寄存器drOdr7 , dr4和 dr5 不用。l當(dāng) prev_p 或者 next_p 定義了自己的 I/0 訪問位圖時,必須更新 TSS的 I/O bitmap。if (prev_p-thread.io_bitmap_ptr | next_p-thread.io_bitmap_ptr)handle_io_bitmap(&next_p-thread,&init_tsscpu);進(jìn)程的 I/O 訪問位圖存放在 io_bitmap_ptr 指針里,通常進(jìn)程很少修改 IO 位圖, 只有當(dāng)前時間片中訪問10端口才會把實際的10位圖加載到

24、TSS中。當(dāng) next_p 沒有自定義位圖時:tss-io_bitmap_base = INVALID_I0_BITMAP_0FFSET; 返回U如果 next = tss-io_bitmap_owner貝設(shè)置有效的偏移量: tss-io_bitmap_base = IO_BITMAP_OFFSET; 返回U否貝U tss-io_bitmap_base =INVALID_I0_BITMAP_0FFSET_LAZY;只有第二種情況 tss-io_bitmap_base 設(shè)置的是有效的 io_bitmap 偏移量, 對于其他兩種情況,當(dāng)用戶進(jìn)程訪問 I/0 端口時將會觸發(fā) Generalprote

25、ction 的異常, do_general_protection( )異常處理函數(shù)根據(jù) io_bitmap 的值處理異常:如果是0 x8000(INVALID_IO_BITMAP_OFFSE則發(fā)送SIGSEG信 號給用戶進(jìn)程;如果是0 x9000(INVALID_IO_BITMAP_OFFSET_LAZY拷貝進(jìn)程的 thread 中的 io_bitmap_ptr 內(nèi)容到 io_bitmap 中,并設(shè)置 io_bitmap_base 為正 確的偏移量 (104) 。8)disable_tsc(prev_p, next_p) 設(shè)置 cr4 中的 TSCDisable 位。9)arch_leave_

26、lazy_cpu_mode() 設(shè)置 CPU的 lazy 模式。10)如果next_p-fpu_counter 5則恢復(fù)next_p的FPU寄存器內(nèi)容:math_state_restore() 。 FPU寄存器存放在 next_p-thread-i387 中,i387 是 i387_union 的 union 結(jié)構(gòu):union i387_union struct i387_fsave_structfsave;fxsave;struct i387_fxsave_struct struct i387_soft_struct soft;struct i387_fxsave_struct unsign

27、ed shortcwd;unsigned shortswd;unsigned shorttwd;unsigned shortfop;longfip;longfcs;longfoo;longfos;longmxcsr;longmxcsr_mask;longst_space32;*/longxmm_space32;*/longpadding56;/* 8*16 bytes for each FP-reg = 128 bytes/* 8*16 bytes for each XMM-reg= 128 bytes11)如果需要,則從 next-gs 中恢復(fù) gs 寄存器內(nèi)容。if (prev-gs |

28、next-gs)loadsegment(gs, next-gs);、 Linux 實時調(diào)度 schedule1概述三種調(diào)度策略:SCHED_FIF,OSCHED_R1R SCHED_NORMALFIFO實時調(diào)度算法當(dāng)調(diào)度器將 CPU指定給某個進(jìn)程時,它把該進(jìn)程放到運行隊 列首;除非有更高優(yōu)先級的進(jìn)程, 否則該進(jìn)程將一直占用 CPU。Round Robin實時進(jìn)程調(diào)度把CPU旨定給某進(jìn)程,把它放到運行隊列尾。時間片 運行完再選擇其他進(jìn)程調(diào)度。 這樣保證了同優(yōu)先級的公平競爭 CPU。SCHED_NORMA普通的基于運行時間和等待時間等,動態(tài)調(diào)整進(jìn)程優(yōu)先級的一 種調(diào)度策略。實時進(jìn)程優(yōu)先級1100,普

29、通101139。 _attribute_ (aligned (16);1)該進(jìn)程被更高優(yōu)先級的進(jìn)程搶占;4)5)RR實時進(jìn)程用完了CPU分配的時間片;2實時進(jìn)程調(diào)度的時機(jī)2)進(jìn)程執(zhí)行一個阻塞操作,被放到睡眠隊列,狀態(tài)為TASK_INTERRU PTIBBETASKUNINTERRU PTIBLE3)進(jìn)程被終止(狀態(tài)為TASK_STOPPED或TASK_TRACED或者進(jìn)程被殺死 (狀態(tài)為 EXIT_ZOMBIE 或 EXIT_DEAD)進(jìn)程調(diào)用 sched_yield() 主動放棄 CPU;3調(diào)度器相關(guān)函數(shù)更新當(dāng)前進(jìn)程的運行時間片 tick 值, 斷進(jìn)程的時間片是否用完。在 update_pr

30、ocess_times( ) 中調(diào)用,判喚醒一個睡眠的進(jìn)程并把它的狀態(tài)設(shè)為TASK_RUNNINGS入到運行隊列中。1)scheduler_tick( )2)try_to_wake_up( )3)recalc_task_prio( )更新進(jìn)程的睡眠時間和動態(tài)優(yōu)先級,SCHED_NORMIA度。4)schedule( )進(jìn)程調(diào)度3)調(diào)用 sched_setscheduler() 時。5)4 schedule( )函數(shù)1)將 current 插入到合適的等待隊列中;3)調(diào)用 schedule();4)檢查資源是否可用, 如果不可用, 轉(zhuǎn)到第 2)步;5)一旦資源可用, 從等待隊列中移除 curre

31、nt 進(jìn)程;在設(shè)備驅(qū)動程序中也經(jīng)常會檢查 TIF_NEED_RESCH并調(diào)用schedule。延遲調(diào)用方式是通過設(shè)置current進(jìn)程的TIF_NEED_RESCH標(biāo)志為1。當(dāng)恢復(fù) 用戶態(tài)進(jìn)程的執(zhí)行前,會檢查該標(biāo)志并決定是否調(diào)用 schedule() 。延遲調(diào)度的 情形有:1) 標(biāo)志;在 scheduler_tick() 中如果 current 用完了時間片則設(shè)置該2) 進(jìn)程優(yōu)先級高。在 try_to_wake_up( ) 中喚醒一個進(jìn)程并且該進(jìn)程比當(dāng)前運行l(wèi)oad_balance()SMP系統(tǒng)的負(fù)載均衡。進(jìn)程調(diào)度有兩種方式:直接調(diào)用和延遲調(diào)用。直接調(diào)用 schedule ,當(dāng)前進(jìn)程資源不可用

32、時會直接調(diào)用調(diào)度器,這種情況下, 內(nèi)核線程進(jìn)行如下處理:2)將 current 狀態(tài)變?yōu)?TASK_INTERRUPTIBL或TASK_UNINTERRUPTIBLE1)核鎖;TASK RUNNIN不移出運行隊列。schedule() 函數(shù)工作流程:進(jìn)程切換前的工作:禁止內(nèi)核搶占,初始化局部變量 prev ,釋放 prev 占有的大內(nèi)need_resched:preempt_disable();prev = current;release_kernel_lock(prev);2)讀取調(diào)度TSC時間,計算調(diào)整run_time時間,更新調(diào)度狀態(tài)rq-sched_cnt 參數(shù),獲取 rq 的 spi

33、n 鎖: spin_lock_irq(&rq-lock) 。3)檢查prev狀態(tài):如果狀態(tài)不是TASK_RUNNIN且沒有在內(nèi)核態(tài)被搶占,則從運行隊列中移除;但是如果prev狀態(tài)是TASK_INTERRUPTIB并且 擁有非阻塞掛起的信號,則把進(jìn)程狀態(tài)設(shè)為if (p rev-state & !(preemp t_cou nt() & P REE MP T_ACTIVE) switch_count = &prev-nvcsw;if (unlikely(prev-state & TASK_INTERRUPTIBLE) &unlikely(signal_pending(prev)prev-state

34、 = TASK_RUNNING;else if (prev-state = TASK_UNINTERRUPTIBLE)獲取當(dāng)前CPU邏輯號,如果當(dāng)前運行隊列為空,貝U調(diào)用rq)從其他CPI運行隊列上拉進(jìn)程到本地 CPU勺運行隊列上。5) activerq-nr_uninterruptible+;deactivate_task(prev, rq);4) idle_balance(cpu, 如果調(diào)整后, 當(dāng)前運行隊列仍為空則 next 賦為 idle 進(jìn)程,跳轉(zhuǎn)到任務(wù)切換代碼 行去。if (unlikely(!rq-nr_running) idle_balance(cpu, rq);if (!rq

35、-nr_running) next = rq-idle;rq-expired_timestamp = 0;goto switch_tasks;如果 runqueue 中有進(jìn)程,并且當(dāng)前活得進(jìn)程數(shù)為 0,則交換 expired 隊列指針。array = rq-active;if (unlikely(!array-nr_active) schedstat_inc(rq, sched_switch);rq-active = rq-expired;rq-expired = array;array = rq-active;rq-expired_timestamp = 0;6)置為 1 的索引,根據(jù)索引找

36、到該優(yōu)先級隊列的第一個rq-best_expired_prio = MAX_PRIO;從運行隊列的活動 prio_array 數(shù)據(jù)的位圖中查找第一個位設(shè) task 。idx = sched_find_first_bit(array-bitmap);queue = array-queue + idx;next = list_entry(queue-next, struct task_struct, run_list);7)如果 next 是普通進(jìn)程,并且 next-sleep_type 是SLEEP_INTERACTIVE或 SLEEP_INTERRUPT,則重新計算進(jìn)程睡眠時間和進(jìn)程 優(yōu)先級。

37、進(jìn)程切換工作 :8) 更新 sched_goidle ,預(yù)期 next 結(jié)構(gòu)數(shù)據(jù),清除TIF_NEED_RESCH志,設(shè)置 quiesce nt 狀態(tài)計數(shù)為 1: rcu_data -passed_quiesc = 1;switch_tasks: if (next = rq-idle)schedstat_inc(rq, sched_goidle);prefetch(next);prefetch_stack(next);clear_tsk_need_resched(prev);rcu_qsctr_inc(task_cpu(prev);10)進(jìn)行進(jìn)程切換, context_switch 參見前面的

38、分析,它進(jìn)行進(jìn)程空間 prepare_lock_switch9) 更新 prev 進(jìn)程運行時間戳 prev-sleep_avg , prev-timestamp;調(diào)度信息切換到 next ,更新 next; 時間戳和運行隊列信息:sched_info_switch(prev, next);if (likely(prev != next) next-timestamp = next-last_ran = now;rq-nr_switches+;rq-curr = next;+*switch_count;11) 和內(nèi)核堆棧切換。 prepare_lock_switch 功能是在定義了 _ARCH_

39、WANT_INTERRU PTS_ONj情況下,在切換前開中斷 spin_unlock_irq(&rq-lock); barrier() 是保證代碼執(zhí)行順序不變。prepare_task_switch(rq, next);prev = context_switch(rq, prev, next);barrier();finish_task_switch(this_rq(), prev);進(jìn)程切換后的工作: 進(jìn)程切換 context_switch 語句之后的代碼并不是由 next 進(jìn)程立即執(zhí)行的, 而是 由調(diào)度器選擇 prev 進(jìn)程繼續(xù)執(zhí)行的。次時 prev 變量指向的已經(jīng)是被 prev 進(jìn)程

40、替換的其他進(jìn)程的指針。12) finish_task_switch() 必須與 prepare_task_switch 配對使用,并 主要鎖的順序。 13)它所做的工作, finish_lock_switch 調(diào)用 local_irq_enable(), 獲取 prev 的狀態(tài)和 rq-prev_mm,如果 mn非空,則調(diào)用 mmdrop(mm減少 mm勺 引用計數(shù),如果為 0則釋放進(jìn)程頁表和虛擬空間。 如果 prev_state 為 TASK_DEAD 則釋放進(jìn)程的 task 結(jié)構(gòu)。struct mm_struct *mm = rq-prev_mm;long prev_state;rq-pr

41、ev_mm = NULL;prev_state = prev-state;finish_arch_switch(prev);finish_lock_switch(rq, prev);if (mm)mmdrop(mm);if (unlikely(prev_state = TASK_DEAD) kprobe_flush_task(prev);put_task_struct(prev);最后, if (unlikely(task-lock_depth = 0)則重新獲取大內(nèi)核鎖_reacquire_kernel_lock ,否則 goto need_resched_nonpreemptible; 允

42、 許搶占,如果TIF_NEED_RESCH被設(shè)置,則跳轉(zhuǎn)到need_resched重新進(jìn)行調(diào)度。prev = current;if (unlikely(reacquire_kernel_lock(prev) vm_ops提供了 nopage()函數(shù),則用它填充數(shù)據(jù);否則調(diào) 用do_anonymous_page()匿名函數(shù)來填充數(shù)據(jù)。如果被文件或設(shè)備映射,如果時 文件映射, filemap_nopage() 將替代 nopage() 函數(shù),如果由虛擬文件映射而來, 則shmem_nopage(。每種設(shè)備驅(qū)動將提供不同的 nopage()函數(shù),該函數(shù)返回 struct page 結(jié)構(gòu)。3請求換頁:

43、將頁面交換至后援存儲器后,函數(shù) do_swap_page()負(fù)責(zé)將頁面讀入內(nèi)存,將在 后面講述。通過PTE的信息就足夠查找到交換的頁面。頁面交換出去時,一般先 放到交換高速緩存中。缺頁中斷時如果頁面在高速緩存中, 則只要簡單增加頁面計數(shù), 然后把它放到進(jìn) 程頁表中并計數(shù)次缺頁中斷發(fā)生的次數(shù)。如果頁面僅存在磁盤中, Linux 將調(diào)用 swapin_readahead() 讀取它及后續(xù)的若干 頁面。4頁面幀回收除了 slab 分配器,系統(tǒng)中所有正在使用的頁面都存放在頁面高速緩存中,并由 page-lru 鏈接在一起。 Slab 頁面不存放到高速緩存中因為基于被 slab 使用的 對象對頁面計數(shù)很

44、困難。除了查找每個進(jìn)程頁表外沒有其他方法能把 struct page映射為PTE查找頁表代價很大。如果頁面高速緩存中存在大量的進(jìn)程映射 頁面,系統(tǒng)將會遍歷進(jìn)程頁表,通過 swap_out() 函數(shù)交換出頁面直到有足夠的 頁面空閑,而共享頁會給 swap_out() 帶來問題。如果一個頁面是共享的,同時 一個交換項已經(jīng)被分配,PTE就會填寫所需信息以便在交換分區(qū)里重新找到該頁 2)標(biāo)志所有進(jìn)程態(tài)進(jìn)程的頁面為可回收的。并將引用計數(shù)減 1。只有引用計數(shù)為 0 時該頁才能被替換出去。內(nèi)存和磁盤緩存申請越來越多的頁面但確無法判斷如何釋放進(jìn)程頁面, 請求分頁 機(jī)制在進(jìn)程頁面缺失時申請新頁面,但它卻不能強(qiáng)制

45、釋放進(jìn)程不再使用的頁面。 The Page Frame Reclaiming Algorithm(PFRA) 頁面回收算法用于從用戶進(jìn)程和 內(nèi)核cache中回收頁面放到伙伴系統(tǒng)的空閑塊列表中。PFRA必須在系統(tǒng)空閑內(nèi)存達(dá)到某個最低限度時進(jìn)行頁面回收, 回收的對象必須是非空閑頁面??蓪⑾到y(tǒng)頁面劃分為四種:1)Unreclaimable 不可回收的,包括空閑頁面、保留頁面設(shè)置了PG_reserved 標(biāo)志、內(nèi)核動態(tài)分配的頁面、 進(jìn)程內(nèi)核棧的頁面、 設(shè)置了 PG_locked 標(biāo)志的臨時鎖住的頁面、設(shè)置了 VM_LOCKED志的內(nèi)存頁面。2)Swa PP able可交換的頁面,用戶進(jìn)程空間的匿名頁面

46、(用戶堆棧)、tmpfs文件系統(tǒng)的映射頁面(入IPC共享內(nèi)存頁面),頁面存放到交換空 間。3)Syncable 可同步的頁面,入用戶態(tài)地址空間的映射頁面、保護(hù)磁盤數(shù)據(jù)的頁面緩存的頁面、塊設(shè)備緩沖頁、磁盤緩存的頁面(入 inode cache),如果有必要的話,需同步磁盤映像上的數(shù)據(jù)。4)Discardable 可丟棄的頁面,入內(nèi)存緩存中的無用頁面( slab分配器中的頁面)、 dentry cache 的頁面。PFRA算法是基于經(jīng)驗而非理論的算法,它的設(shè)計原則如下:1)首先釋放無損壞的頁面。 進(jìn)程不再引用的磁盤和內(nèi)存緩存應(yīng)該先于用戶態(tài)地址空間的頁面釋放。3) 然后再回收。多進(jìn)程共享頁面的回收,

47、要先清除引用該頁面的進(jìn)程頁表項,4)回收“不在使用的”頁面。PFRA用 LRU鏈表把進(jìn)程劃分為in-use和unused兩種,PFRA僅回收unused狀態(tài)的頁面。Linux使用PTE中的 Accessed比特位實現(xiàn)非嚴(yán)格的LRU算法。頁面回收通常在三種情況下執(zhí)行:1)系統(tǒng)可用內(nèi)存比較低時進(jìn)行回收 ( 通常發(fā)生在申請內(nèi)存失敗 ) 。2)內(nèi)核進(jìn)入 suspend-to-disk 狀態(tài)時進(jìn)行回收。3)周期性回收,內(nèi)核線程周期性激活并在必要時進(jìn)行頁面回收。Low on memory 回收有以下幾種情形:1)_ _getblk( ) 調(diào)用的 grow_buffers( ) 函數(shù)分配新緩存頁失?。?)c

48、reate_empty_buffers( ) 調(diào)用的 alloc_page_buffers( ) 函數(shù)為頁面分配臨時的 buffer head 失??;3) 幀失敗。_ _alloc_pages( ) 函數(shù)在給定內(nèi)存區(qū)里分配一組連續(xù)的頁面周期性回收涉及的兩種內(nèi)核線程:1)Kswa pd內(nèi)核線程在內(nèi)存區(qū)中檢測空閑頁面是否低于pages_high 的門檻值;2)預(yù)定義工作隊列中的事件內(nèi)核線程,P FRA周期性調(diào)度該工作隊列中的 task 回收 slab 分配器中所有空閑的 slab ;所有用戶空間進(jìn)程和頁面緩存的頁面被分為活動鏈表和非活動鏈表,統(tǒng)稱 LRU 鏈表。每個區(qū)描述符中包括 active_l

49、ist 和 inactive_list 兩個鏈表分別將這些 頁面鏈接起來。 nr_active 和 nr_inactive 分別表示這兩種頁面數(shù)量, lru_lock 用于同步。頁描述符中的PG Iru用于標(biāo)志一個頁面是否屬于LRU鏈表,PG active 用于標(biāo)志頁面是否屬于活動鏈表,Iru字段用于把LRU中的鏈表串起來。活動鏈 表和非活動鏈表的頁面根據(jù)最近的訪問情況進(jìn)行動態(tài)調(diào)整。 PG_referenced 標(biāo)志 就是此用途。處理LRU鏈表的函數(shù)有:add_page_to_active_list()、 add_page_to_inactive_list()、activate_page()

50、、lru_cache_add() 、lru_cache_add_active() 等,這些函數(shù)比 較簡單。shrink_active_list ( ) 用于將頁表從活動鏈表移到非活動鏈表。該函數(shù)在 shrink_zone() 函數(shù)執(zhí)行用戶地址空間的頁面回收時執(zhí)行。5交換分區(qū):系統(tǒng)可以有MAX_SWAPFILES交換分區(qū),每個分區(qū)可放在磁盤分區(qū)上或者普通文 件里。每個交換區(qū)由一系列頁槽組成。每個交換區(qū)有個swap_header結(jié)構(gòu)描述交 換區(qū)版本等信息。每個交換區(qū)有若干個 swap_extent 組成,每個 swap_extent 是連續(xù)的物理區(qū)域。對于磁盤交換區(qū)只有一個 swap_extent

51、 ,對于文件交換區(qū)則 由多個swap_extent組成,因為文件并不是放在連續(xù)的磁盤塊上的。mkswap命令可以創(chuàng)建交換分區(qū)。圖交換分區(qū)結(jié)構(gòu)圖交換頁結(jié)構(gòu)swp_type() 和swp_offset()函數(shù)根據(jù)頁槽索引和交換區(qū)號得到 type和offset 值,函數(shù) swp_entry(type,offset) 得到交換槽。最后一位總是清0表示頁不在 RAMLt。槽最大224(64G)。第一個可用槽索引為1。槽索引不能全為0。一個頁面可能被多個進(jìn)程共用,它可能被從一個進(jìn)程地址空間換出但仍然在物理 內(nèi)存上,因此一個頁面可能被多次換出。但物理上僅第一次被換出并存儲在交換 區(qū)上,接下來的換出操作只是增

52、加 swap_map的引用計數(shù)。swap_d up licate(swp_e ntry_t en try)的功能正是用戶嘗試換出一個已經(jīng)換出的頁面。6.交換緩存: 多個進(jìn)程同時換進(jìn)一個共享匿名頁時或者一個進(jìn)程換入一個正被 P FRA換出的頁 時存在競爭條件,引入交換緩存解決這種同步問題。 通過PG_locked標(biāo)志可以保 證對一個頁的并發(fā)的交換操作只作用在一個頁面上,從而避免競爭條件。7.頁面回收算法描述:下圖是各種情況下進(jìn)行頁面回收時的函數(shù)調(diào)用關(guān)系圖。 可以看出最終調(diào)用函數(shù)為 cache_reap()、shrink_slab() 和 shrink_list() 。cache_reap()用于

53、周期性回 收slab分配器中的無用slab。shrink_slab()用于回收磁盤緩存的頁面。shri nk_list()是頁面回收的核心函數(shù),在最新代碼中該函數(shù)名改為shri nk_page_list()。下面會重點講解。圖中 shrink_caches() 最新函數(shù)名為 shrink_zones() 、shrink_cache() 最新函數(shù)名為shrink_inactive_list()。其他函數(shù)不變。函數(shù)。函數(shù)的目標(biāo)是通過仍然沒有釋放掉32 函數(shù)選擇一個進(jìn)程sea n_con trol *sc)低內(nèi)存回收頁面:如上圖所示,當(dāng)內(nèi)存分配失敗時,內(nèi)核調(diào)用free_more_memory(),該

54、函數(shù)首先調(diào)用wakeup_bdflush()喚醒pdflush內(nèi)核線程觸發(fā)寫操作,從磁盤頁面緩沖中寫 1024個dirty頁面到磁盤上以釋放包含緩沖、緩沖頭和VFS的數(shù)據(jù)結(jié)構(gòu)所占用的頁表;然后進(jìn)行系統(tǒng)調(diào)用sched_yield(),以使pdflush線程能夠有機(jī)會運 行;最后該函數(shù)循環(huán)遍歷系統(tǒng)節(jié)點,對每個節(jié)點上的低內(nèi)存區(qū) (Z0NE_DMA和 ZONE_NORMA調(diào)用 try_to_free_pages()try_to_free_ pages(struct zone *z on es, gfp_t gfp_mask)循環(huán)調(diào)用shrink_slab() 和shrink_zones()釋放至少32

55、個頁幀,每次調(diào)用增加 優(yōu)先級參數(shù),初始優(yōu)先級是12,最高為0。如果循環(huán)13次, 個頁面,則PFRA行內(nèi)存異出保護(hù):調(diào)用 out_of_memory() 回收它所有的頁面。shri nk_zon es(i ntp riority,struct zone *z on es,struct圖 P FRA函數(shù)結(jié)構(gòu)調(diào)用關(guān)系函數(shù)對zones列表中每個區(qū)調(diào)用shrink_zone()函數(shù)。調(diào)用shrink_zone()前,1) 數(shù);atomic_inc(&zone-reclaim_in_progress)增加區(qū)的回收計2) zone-nr_active/2 zone-nr_scan_active = 32zo

56、ne-nr_scan_active 設(shè)為 0,否貝U nr_active = 0;增加 zone-nr_scan_active ,根據(jù)優(yōu)先級,12 to zone-nr_active/2 0 。如果 則賦給 nr_active 變量,同時增加范圍是3)zone-nr_scan_inactive 和 nr_inactive 做同樣處理;如果4)循環(huán)進(jìn)行 5、 6 步操作:nr_active和 nr_inactive 不同時為空,則進(jìn)行 while5) 如果 inactive 鏈表:nr_active非 0,則從 active 鏈表移動某些頁面到6)先用 sc-priority 的值更新 zone

57、 描述符中的 prev_priority ,如果 zone-all_unreclaimable 字段不為 0 且優(yōu)先級不是 12,則不對該區(qū)進(jìn)行頁面回 收。shrink_zone(int priority, struct zone *zone, struct scan_control *sc) 函數(shù)嘗試回收 32 個頁面。該函數(shù)循環(huán)進(jìn)行 shrink_active_list() shrink_inactive_list 的操作以達(dá)到目標(biāo)。 該函數(shù)流程如下:nr_to_scan = min(nr_active,(unsigned long)sc-swap_cluster_max);nr_acti

58、ve -= nr_to_scan;shrink_active_list(nr_to_scan, zone, sc, priority);如果 nr_inactive 非 0,則回收 inactive 鏈表中的頁面:nr_to_scan = min(nr_inactive,(unsigned long)sc-swap_cluster_max);nr_inactive -= nr_to_scan;nr_reclaimed += shrink_inactive_list(nr_to_scan,zone,sc);7)atomic_dec(&zone-reclaim_in_progress) 減小回收計

59、數(shù),并返回回收頁面數(shù) nr_reclaimed ;shrink_inactive_list(unsigned long max_scan, struct zone *zone, struct scan_control *sc) 函數(shù)從區(qū)的 inactive 鏈表中抽取一組頁面, 放到臨時鏈表中, 調(diào)用 shrink_page_list()對鏈表中的每個頁面進(jìn)行回收。下面是shrink_inactive_list()主要步驟:1)調(diào)用 lru_add_drain() 將當(dāng)前 CPUh pagevec結(jié)構(gòu)的lru_add_pvecs 和 lru_add_active_pvecs 中的頁面分別移到活

60、動鏈表和非活動 鏈表中;2)獲取 LRU鎖 spin_lock_irq(&zone-lru_lock);3)最多掃描max_scan個頁面,對每個頁面增加使用計數(shù),檢查該頁面是否正被釋放到伙伴系統(tǒng)中, 將該頁面移動一個臨時鏈表中;4)從 zone-nr_inactive 中減去移到臨時鏈表中的頁面數(shù);5)增加 zone-pages_scanned 計數(shù);6)釋放 LRU鎖:spin_unlock_irq(&zone-lru_lock)7)面;對臨時鏈表調(diào)用 shrink_page_list(&page_list, sc)回收頁8)增加 nr_reclaimed 計數(shù);9)將 shrink_pa

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論