




已閱讀5頁(yè),還剩59頁(yè)未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
一、Linux進(jìn)程切換深入分析#define CLONE_KERNEL(CLONE_FS | CLONE_FILES | CLONE_SIGHAND)創(chuàng)建內(nèi)核線程時(shí)使用的CLONE標(biāo)志。1#define unlikely(x)_builtin_expect(!(x), 0)編譯器優(yōu)化,實(shí)際返回值x是整型表達(dá)式,0表示并不預(yù)期該事件發(fā)生,也就是說(shuō)x為0的可能性很小,這是為了讓編譯器對(duì)下面得語(yǔ)句進(jìn)行優(yōu)化。2進(jìn)程內(nèi)核態(tài)堆棧結(jié)構(gòu):進(jìn)程是動(dòng)態(tài)實(shí)體,進(jìn)程描述符是存放在動(dòng)態(tài)內(nèi)存中的。在一塊進(jìn)程內(nèi)存區(qū)上,Linux存放了兩個(gè)數(shù)據(jù)結(jié)構(gòu):指向task_struct得thread_info和內(nèi)核態(tài)的進(jìn)程棧。大小一般2頁(yè)8K,這要求頁(yè)面幀對(duì)齊2的13次冪,在X86上編譯時(shí)可以配置大小為4K。thread_info在內(nèi)存區(qū)開(kāi)始處,內(nèi)核棧從內(nèi)存尾向下增長(zhǎng)。在C語(yǔ)言中可以用union結(jié)構(gòu)表示:圖1. 8K內(nèi)核棧和進(jìn)程描述符task_struct及thread_info的相互關(guān)系union thread_union struct thread_info thread_info;unsigned long stack2048; /* 1024 for 4KB stacks */;CPU的esp寄存器用于執(zhí)行堆棧的頂部指針,當(dāng)從用戶態(tài)轉(zhuǎn)向內(nèi)核態(tài)時(shí),進(jìn)程內(nèi)核??偸强盏?,所以esp就會(huì)執(zhí)行堆棧底部。使用alloc_thread_info和free_thread_info用于分配和釋放一個(gè)存放thread_info結(jié)構(gòu)和內(nèi)核堆棧的內(nèi)存區(qū)。內(nèi)核通過(guò)當(dāng)前esp指針可以很方便的得到thread_info結(jié)構(gòu)的地址。current_thread_info(void)的原理即如下:movl $0xffff2000,%ecx /* or 0xfffff000 for 4KB stacks */andl %esp,%ecxmovl %ecx,pthread_info中task指針是第一個(gè),所以current宏相當(dāng)于current_thread_info( )-task,從而也就得到task指針。每個(gè)進(jìn)程有自己獨(dú)立得進(jìn)程空間,所有進(jìn)程共享CPU寄存器。進(jìn)程繼續(xù)執(zhí)行時(shí)必須裝入寄存器恢復(fù)得數(shù)據(jù)集稱為硬件上下文環(huán)境。在Linux中部分硬件上下文存放在進(jìn)程描述符中,部分存放到內(nèi)核態(tài)堆棧里。3.進(jìn)程切換堆棧原理:每個(gè)進(jìn)程有自己獨(dú)立得進(jìn)程空間,所有進(jìn)程共享CPU寄存器。進(jìn)程繼續(xù)執(zhí)行時(shí)必須裝入寄存器恢復(fù)得數(shù)據(jù)集稱為硬件上下文環(huán)境。在Linux中部分硬件上下文存放在進(jìn)程描述符中,部分存放到內(nèi)核態(tài)堆棧里。80x86體系支持在進(jìn)程TSS段跳轉(zhuǎn)時(shí)自動(dòng)執(zhí)行進(jìn)程硬件上下文切換。Linux使用軟件方法實(shí)現(xiàn)。軟件方式效率差不多,當(dāng)更靈活,可以控制流程,留下優(yōu)化空間。80x86用TSS段保存硬件上下文內(nèi)容,每個(gè)CPU有一個(gè)TSS段。從用戶態(tài)到內(nèi)核態(tài)切換時(shí),從TSS中取出內(nèi)核棧地址。用戶態(tài)進(jìn)程訪問(wèn)I/O端口時(shí),TSS中的I/O訪問(wèn)位圖可以驗(yàn)證權(quán)限。tss_struct描述了TSS格式,init_tss存放初始TSS內(nèi)容,每次進(jìn)程切換,內(nèi)核更新TSS中的某些字段,以反映當(dāng)前運(yùn)行進(jìn)程的權(quán)限等級(jí)。每個(gè)進(jìn)程有個(gè)反映任務(wù)CPU狀態(tài)的thread_struct結(jié)構(gòu)變量thread,除eax、ecx等通用寄存器內(nèi)容保存在內(nèi)核態(tài)堆棧中,其他大部分寄存器都保存在次結(jié)構(gòu)中。該結(jié)構(gòu)一部分對(duì)應(yīng)于tss_struct中的內(nèi)容,進(jìn)程切換時(shí)把thread中某些內(nèi)容更新到tss_struct中就可以反映當(dāng)前任務(wù)的運(yùn)行CPU環(huán)境。struct tss_structunsigned shortback_link,_blh;unsigned longesp0;unsigned shortss0,_ss0h;unsigned longesp1;unsigned shortss1,_ss1h;/* ss1 is used to cache MSR_IA32_SYSENTER_CS */unsigned longesp2;unsigned shortss2,_ss2h;unsigned long_cr3;unsigned longeip;unsigned longeflags;unsigned 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, _ldth;unsigned shorttrace, io_bitmap_base;/* The extra 1 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.*/unsigned longio_bitmapIO_BITMAP_LONGS + 1;/* Cache the current maximum and the last task that used the bitmap:*/unsigned long io_bitmap_max;struct thread_struct *io_bitmap_owner;/* pads the TSS to be cacheline-aligned (size is 0x100)*/unsigned long _cacheline_filler35;/* . and then another 0x100 bytes for emergency kernel stack*/unsigned long stack64; _attribute_(packed);struct thread_struct/* cached TLS descriptors. */struct desc_struct tls_arrayGDT_ENTRY_TLS_ENTRIES;unsigned longesp0;unsigned longsysenter_cs;unsigned longeip;unsigned longesp;unsigned longfs;unsigned longgs;/* Hardware debugging registers */unsigned longdebugreg8;/* %db0-7 debug registers */* fault info */unsigned longcr2, trap_no, error_code;/* floating point info */union i387_unioni387;/* virtual 86 mode info */struct vm86_struct _user * vm86_info;unsigned longscreen_bitmap;unsigned longv86flags, v86mask, saved_esp0;unsigned intsaved_fs, saved_gs;/* IO permissions */unsigned long*io_bitmap_ptr;unsigned longiopl;/* max allowed port in the bitmap, in bytes: */unsigned longio_bitmap_max;4進(jìn)程切換流程解析switch_to進(jìn)程切換本質(zhì)上兩步:1)進(jìn)程頁(yè)表PGD切換;2)內(nèi)核態(tài)堆棧和硬件上下文切換(包括CPU寄存器);上面兩步通過(guò)context_switch()實(shí)現(xiàn),它通過(guò)調(diào)用switch_mm()切換進(jìn)程空間,switch_to切換內(nèi)核上下文環(huán)境。首先看看context_switch()做了些什么:1)進(jìn)程描述符中active_mm執(zhí)行進(jìn)程使用的地址空間,mm執(zhí)行進(jìn)程擁有的地址空間,對(duì)于普通進(jìn)程它們相同。對(duì)于內(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);2)否則,如果next是普通進(jìn)程,則用next進(jìn)程空間替換prev的地址空間:switch_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)程空間:1)獲取cpu邏輯號(hào)。2)cpu_clear(cpu, prev-cpu_vm_mask)清除cpu_vm_mask位標(biāo)志。3)per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK設(shè)置cpu_tlbstate狀態(tài)。4)per_cpu(cpu_tlbstate, cpu).active_mm = next設(shè)置cpu_tlbstate的active_mm為next。5)cpu_set(cpu, next-cpu_vm_mask)設(shè)置next的cpu_vm_mask標(biāo)志。6)load_cr3(next-pgd)裝載next的pgd頁(yè)表到cr3寄存器。7)如果next的LDT描述符改變,則加載next的LDT描述符。if (unlikely(prev-context.ldt != next-context.ldt)load_LDT_nolock(&next-context);最后,switch_to進(jìn)行內(nèi)核堆棧和CPU環(huán)境切換操作:#define switch_to(prev,next,last) do unsigned long esi,edi;asm volatile(pushflnt/* Save flags */pushl %ebpntmovl %esp,%0nt/* save ESP */movl %5,%espnt/* restore ESP */movl $1f,%1nt/* save EIP */pushl %6nt/* restore EIP */jmp _switch_ton1:tpopl %ebpntpopfl:=m (prev-thread.esp),=m (prev-thread.eip),=a (last),=S (esi),=D (edi):m (next-thread.esp),m (next-thread.eip),2 (prev), d (next); while (0)流程描述,prev是進(jìn)程A的task結(jié)構(gòu),next是進(jìn)程B的task結(jié)構(gòu),last是進(jìn)程C的結(jié)構(gòu):1)保存prev和next指針的值到eax和edx:movl prev, %eaxmovl next, %edx2)保存eflags和ebp寄存器內(nèi)容到prev內(nèi)核態(tài)堆棧中:pushflpushl %ebp3)將esp內(nèi)容保存到prev-thread.esp中,該字段執(zhí)行prev內(nèi)核堆棧的top地址。movl %esp,484(%eax)4)將next-thread.esp加載到esp中,現(xiàn)在開(kāi)始,esp執(zhí)行next的內(nèi)核堆棧,進(jìn)程切換完成。movl 484(%edx), %esp5)保存下面Label 1到prev-thread.eip指針中,當(dāng)prev進(jìn)程恢復(fù)運(yùn)行時(shí),從該位置開(kāi)始運(yùn)行。movl $1f, 480(%eax)6)將next-thread.eip的指針內(nèi)容壓到next的內(nèi)核態(tài)堆棧中,通常它的內(nèi)容也是Label 1。pushl 480(%edx)7)跳轉(zhuǎn)到_switch_to()C函數(shù)執(zhí)行。jmp _switch_to8)被替換的進(jìn)程A繼續(xù)執(zhí)行,它在Label 1處,首先是恢復(fù)eflags和ebp寄存器內(nèi)容。注意這里是發(fā)生在調(diào)度器選擇prev在CPU上運(yùn)行后,次數(shù)esp已經(jīng)執(zhí)行了prev的內(nèi)核堆棧。1:popl %ebppopfl9)將eax內(nèi)容保存到last任務(wù)結(jié)構(gòu)中。這里eax是被進(jìn)程A切換下來(lái)的進(jìn)程C的task結(jié)構(gòu)指針。movl %eax, last5_switch_to深入分析_switch_to參數(shù)是存放在eax和edx中的內(nèi)容,這通過(guò)#define fastcall_attribute_(regparm(3)告訴gcc編譯器。1)獲取tss_struct tss、prev_p和next_p的thread_struct結(jié)構(gòu)prev和next、當(dāng)前CPU邏輯ID。2)調(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)保存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)級(jí)別是0并且prev-iopl != next-iopl則恢復(fù)IOPL設(shè)置set_iopl_mask(next-iopl)。7)根據(jù)thread_info的TIF標(biāo)志_TIF_WORK_CTXSW和TIF_IO_BITMAP判斷是否需要處理debug寄存器和IO位圖:_switch_to_xtra(next_p, tss);l只有當(dāng)next_p掛起時(shí)即if (test_tsk_thread_flag(next_p, TIF_DEBUG)使用了debug寄存器才需要恢復(fù)set_debugreg(next-debugregi, i)。只有調(diào)試器需要監(jiān)控prev的狀態(tài)時(shí),prev_p-thread.debugreg數(shù)組的內(nèi)容才會(huì)被修改。Debug寄存器dr0dr7,dr4和dr5不用。l當(dāng)prev_p或者next_p定義了自己的I/O訪問(wèn)位圖時(shí),必須更新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訪問(wèn)位圖存放在io_bitmap_ptr指針里,通常進(jìn)程很少修改IO位圖,只有當(dāng)前時(shí)間片中訪問(wèn)IO端口才會(huì)把實(shí)際的IO位圖加載到TSS中。當(dāng)next_p沒(méi)有自定義位圖時(shí):tss-io_bitmap_base = INVALID_IO_BITMAP_OFFSET;返回如果next = tss-io_bitmap_owner則設(shè)置有效的偏移量:tss-io_bitmap_base = IO_BITMAP_OFFSET;返回否則tss-io_bitmap_base = INVALID_IO_BITMAP_OFFSET_LAZY;只有第二種情況tss-io_bitmap_base設(shè)置的是有效的io_bitmap偏移量,對(duì)于其他兩種情況,當(dāng)用戶進(jìn)程訪問(wèn)I/O端口時(shí)將會(huì)觸發(fā)General protection的異常,do_general_protection( )異常處理函數(shù)根據(jù)io_bitmap的值處理異常:如果是0x8000(INVALID_IO_BITMAP_OFFSET)則發(fā)送SIGSEGV信號(hào)給用戶進(jìn)程;如果是0x9000(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中的TSC Disable位。9)arch_leave_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;struct i387_fxsave_structfxsave;struct i387_soft_struct soft;struct i387_fxsave_struct unsigned shortcwd;unsigned shortswd;unsigned shorttwd;unsigned shortfop;longfip;longfcs;longfoo;longfos;longmxcsr;longmxcsr_mask;longst_space32;/* 8*16 bytes for each FP-reg = 128 bytes */longxmm_space32;/* 8*16 bytes for each XMM-reg = 128 bytes */longpadding56; _attribute_ (aligned (16);11)如果需要,則從next-gs中恢復(fù)gs寄存器內(nèi)容。if (prev-gs | next-gs)loadsegment(gs, next-gs);二、Linux實(shí)時(shí)調(diào)度schedule1概述三種調(diào)度策略:SCHED_FIFO,SCHED_RR和SCHED_NORMAL。FIFO實(shí)時(shí)調(diào)度算法當(dāng)調(diào)度器將CPU指定給某個(gè)進(jìn)程時(shí),它把該進(jìn)程放到運(yùn)行隊(duì)列首;除非有更高優(yōu)先級(jí)的進(jìn)程,否則該進(jìn)程將一直占用CPU。Round Robin實(shí)時(shí)進(jìn)程調(diào)度把CPU指定給某進(jìn)程,把它放到運(yùn)行隊(duì)列尾。時(shí)間片運(yùn)行完再選擇其他進(jìn)程調(diào)度。這樣保證了同優(yōu)先級(jí)的公平競(jìng)爭(zhēng)CPU。SCHED_NORMAL是普通的基于運(yùn)行時(shí)間和等待時(shí)間等,動(dòng)態(tài)調(diào)整進(jìn)程優(yōu)先級(jí)的一種調(diào)度策略。實(shí)時(shí)進(jìn)程優(yōu)先級(jí)1100,普通101139。2實(shí)時(shí)進(jìn)程調(diào)度的時(shí)機(jī)1)該進(jìn)程被更高優(yōu)先級(jí)的進(jìn)程搶占;2)進(jìn)程執(zhí)行一個(gè)阻塞操作,被放到睡眠隊(duì)列,狀態(tài)為TASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE;3)進(jìn)程被終止(狀態(tài)為TASK_STOPPED或TASK_TRACED),或者進(jìn)程被殺死(狀態(tài)為EXIT_ZOMBIE或EXIT_DEAD)4)進(jìn)程調(diào)用sched_yield()主動(dòng)放棄CPU;5)RR實(shí)時(shí)進(jìn)程用完了CPU分配的時(shí)間片;3調(diào)度器相關(guān)函數(shù)1)scheduler_tick( )更新當(dāng)前進(jìn)程的運(yùn)行時(shí)間片tick值,在update_process_times( )中調(diào)用,判斷進(jìn)程的時(shí)間片是否用完。2)try_to_wake_up( )喚醒一個(gè)睡眠的進(jìn)程并把它的狀態(tài)設(shè)為TASK_RUNNING,插入到運(yùn)行隊(duì)列中。3)recalc_task_prio( )更新進(jìn)程的睡眠時(shí)間和動(dòng)態(tài)優(yōu)先級(jí),SCHED_NORMAL調(diào)度。4)schedule( )進(jìn)程調(diào)度5)load_balance()SMP系統(tǒng)的負(fù)載均衡。4schedule( )函數(shù)進(jìn)程調(diào)度有兩種方式:直接調(diào)用和延遲調(diào)用。直接調(diào)用schedule,當(dāng)前進(jìn)程資源不可用時(shí)會(huì)直接調(diào)用調(diào)度器,這種情況下,內(nèi)核線程進(jìn)行如下處理:1)將current插入到合適的等待隊(duì)列中;2)將current狀態(tài)變?yōu)門ASK_INTERRUPTIBLE或TASK_UNINTERRUPTIBLE3)調(diào)用schedule();4)檢查資源是否可用,如果不可用,轉(zhuǎn)到第2)步;5)一旦資源可用,從等待隊(duì)列中移除current進(jìn)程;在設(shè)備驅(qū)動(dòng)程序中也經(jīng)常會(huì)檢查TIF_NEED_RESCHED并調(diào)用schedule()。延遲調(diào)用方式是通過(guò)設(shè)置current進(jìn)程的TIF_NEED_RESCHED標(biāo)志為1。當(dāng)恢復(fù)用戶態(tài)進(jìn)程的執(zhí)行前,會(huì)檢查該標(biāo)志并決定是否調(diào)用schedule()。延遲調(diào)度的情形有:1)在scheduler_tick()中如果current用完了時(shí)間片則設(shè)置該標(biāo)志;2)在try_to_wake_up( )中喚醒一個(gè)進(jìn)程并且該進(jìn)程比當(dāng)前運(yùn)行進(jìn)程優(yōu)先級(jí)高。3)調(diào)用sched_setscheduler()時(shí)。schedule()函數(shù)工作流程:進(jìn)程切換前的工作:1)禁止內(nèi)核搶占,初始化局部變量prev,釋放prev占有的大內(nèi)核鎖;need_resched:preempt_disable();prev = current;release_kernel_lock(prev);2)讀取調(diào)度TSC時(shí)間,計(jì)算調(diào)整run_time時(shí)間, 更新調(diào)度狀態(tài)rq-sched_cnt參數(shù),獲取rq的spin鎖:spin_lock_irq(&rq-lock)。3)檢查prev狀態(tài):如果狀態(tài)不是TASK_RUNNING且沒(méi)有在內(nèi)核態(tài)被搶占,則從運(yùn)行隊(duì)列中移除;但是如果prev狀態(tài)是TASK_INTERRUPTIBLE并且擁有非阻塞掛起的信號(hào),則把進(jìn)程狀態(tài)設(shè)為TASK_RUNNING不移出運(yùn)行隊(duì)列。if (prev-state & !(preempt_count() & PREEMPT_ACTIVE) switch_count = &prev-nvcsw;if (unlikely(prev-state & TASK_INTERRUPTIBLE) &unlikely(signal_pending(prev)prev-state = TASK_RUNNING;else if (prev-state = TASK_UNINTERRUPTIBLE)rq-nr_uninterruptible+;deactivate_task(prev, rq);4)獲取當(dāng)前CPU邏輯號(hào),如果當(dāng)前運(yùn)行隊(duì)列為空,則調(diào)用idle_balance(cpu, rq)從其他CPU運(yùn)行隊(duì)列上拉進(jìn)程到本地CPU的運(yùn)行隊(duì)列上。如果調(diào)整后,當(dāng)前運(yùn)行隊(duì)列仍為空則next賦為idle進(jìn)程,跳轉(zhuǎn)到任務(wù)切換代碼行去。if (unlikely(!rq-nr_running) idle_balance(cpu, rq);if (!rq-nr_running) next = rq-idle;rq-expired_timestamp = 0;goto switch_tasks;5)如果runqueue中有進(jìn)程,并且當(dāng)前活得進(jìn)程數(shù)為0,則交換active和expired隊(duì)列指針。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;rq-best_expired_prio = MAX_PRIO;6)從運(yùn)行隊(duì)列的活動(dòng)prio_array數(shù)據(jù)的位圖中查找第一個(gè)位設(shè)置為1的索引,根據(jù)索引找到該優(yōu)先級(jí)隊(duì)列的第一個(gè)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_INTERRUPTED,則重新計(jì)算進(jìn)程睡眠時(shí)間和進(jìn)程優(yōu)先級(jí)。進(jìn)程切換工作:8)更新sched_goidle,預(yù)期next結(jié)構(gòu)數(shù)據(jù),清除TIF_NEED_RESCHED標(biāo)志,設(shè)置quiescent狀態(tài)計(jì)數(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);9)更新prev進(jìn)程運(yùn)行時(shí)間戳prev-sleep_avg,prev-timestamp;10)調(diào)度信息切換到next,更新next;時(shí)間戳和運(yùn)行隊(duì)列信息:sched_info_switch(prev, next);if (likely(prev != next) next-timestamp = next-last_ran = now;rq-nr_switches+;rq-curr = next;+*switch_count;11)進(jìn)行進(jìn)程切換,context_switch參見(jiàn)前面的分析,它進(jìn)行進(jìn)程空間和內(nèi)核堆棧切換。prepare_lock_switch功能是在定義了_ARCH_WANT_INTERRUPTS_ON_CTXSW情況下,在切換前開(kāi)中斷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語(yǔ)句之后的代碼并不是由next進(jìn)程立即執(zhí)行的,而是由調(diào)度器選擇prev進(jìn)程繼續(xù)執(zhí)行的。次時(shí)prev變量指向的已經(jīng)是被prev進(jìn)程替換的其他進(jìn)程的指針。12)finish_task_switch()必須與prepare_task_switch配對(duì)使用,并主要鎖的順序。它所做的工作,finish_lock_switch調(diào)用local_irq_enable(),獲取prev的狀態(tài)和rq-prev_mm,如果mm非空,則調(diào)用mmdrop(mm)減少mm的引用計(jì)數(shù),如果為0則釋放進(jìn)程頁(yè)表和虛擬空間。如果prev_state為TASK_DEAD則釋放進(jìn)程的task結(jié)構(gòu)。struct mm_struct *mm = rq-prev_mm;long prev_state;rq-prev_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);13)最后,if (unlikely(task-lock_depth = 0)則重新獲取大內(nèi)核鎖_reacquire_kernel_lock,否則goto need_resched_nonpreemptible;允許搶占,如果TIF_NEED_RESCHED被設(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ù)來(lái)填充數(shù)據(jù)。如果被文件或設(shè)備映射,如果時(shí)文件映射,filemap_nopage()將替代nopage()函數(shù),如果由虛擬文件映射而來(lái),則shmem_nopage()。每種設(shè)備驅(qū)動(dòng)將提供不同的nopage()函數(shù),該函數(shù)返回struct page結(jié)構(gòu)。3請(qǐng)求換頁(yè):將頁(yè)面交換至后援存儲(chǔ)器后,函數(shù)do_swap_page()負(fù)責(zé)將頁(yè)面讀入內(nèi)存,將在后面講述。通過(guò)PTE的信息就足夠查找到交換的頁(yè)面。頁(yè)面交換出去時(shí),一般先放到交換高速緩存中。缺頁(yè)中斷時(shí)如果頁(yè)面在高速緩存中,則只要簡(jiǎn)單增加頁(yè)面計(jì)數(shù),然后把它放到進(jìn)程頁(yè)表中并計(jì)數(shù)次缺頁(yè)中斷發(fā)生的次數(shù)。如果頁(yè)面僅存在磁盤中,Linux將調(diào)用swapin_readahead()讀取它及后續(xù)的若干頁(yè)面。4頁(yè)面幀回收除了slab分配器,系統(tǒng)中所有正在使用的頁(yè)面都存放在頁(yè)面高速緩存中,并由page-lru鏈接在一起。Slab頁(yè)面不存放到高速緩存中因?yàn)榛诒籹lab使用的對(duì)象對(duì)頁(yè)面計(jì)數(shù)很困難。除了查找每個(gè)進(jìn)程頁(yè)表外沒(méi)有其他方法能把struct page映射為PTE,查找頁(yè)表代價(jià)很大。如果頁(yè)面高速緩存中存在大量的進(jìn)程映射頁(yè)面,系統(tǒng)將會(huì)遍歷進(jìn)程頁(yè)表,通過(guò)swap_out()函數(shù)交換出頁(yè)面直到有足夠的頁(yè)面空閑,而共享頁(yè)會(huì)給swap_out()帶來(lái)問(wèn)題。如果一個(gè)頁(yè)面是共享的,同時(shí)一個(gè)交換項(xiàng)已經(jīng)被分配,PTE就會(huì)填寫所需信息以便在交換分區(qū)里重新找到該頁(yè)并將引用計(jì)數(shù)減1。只有引用計(jì)數(shù)為0時(shí)該頁(yè)才能被替換出去。內(nèi)存和磁盤緩存申請(qǐng)?jiān)絹?lái)越多的頁(yè)面但確無(wú)法判斷如何釋放進(jìn)程頁(yè)面,請(qǐng)求分頁(yè)機(jī)制在進(jìn)程頁(yè)面缺失時(shí)申請(qǐng)新頁(yè)面,但它卻不能強(qiáng)制釋放進(jìn)程不再使用的頁(yè)面。The Page Frame Reclaiming Algorithm(PFRA)頁(yè)面回收算法用于從用戶進(jìn)程和內(nèi)核cache中回收頁(yè)面放到伙伴系統(tǒng)的空閑塊列表中。PFRA必須在系統(tǒng)空閑內(nèi)存達(dá)到某個(gè)最低限度時(shí)進(jìn)行頁(yè)面回收,回收的對(duì)象必須是非空閑頁(yè)面。可將系統(tǒng)頁(yè)面劃分為四種:1)Unreclaimable不可回收的,包括空閑頁(yè)面、保留頁(yè)面設(shè)置了PG_reserved標(biāo)志、內(nèi)核動(dòng)態(tài)分配的頁(yè)面、進(jìn)程內(nèi)核棧的頁(yè)面、設(shè)置了PG_locked標(biāo)志的臨時(shí)鎖住的頁(yè)面、設(shè)置了VM_LOCKED標(biāo)志的內(nèi)存頁(yè)面。2)Swappable可交換的頁(yè)面,用戶進(jìn)程空間的匿名頁(yè)面(用戶堆棧)、tmpfs文件系統(tǒng)的映射頁(yè)面(入IPC共享內(nèi)存頁(yè)面),頁(yè)面存放到交換空間。3)Syncable可同步的頁(yè)面,入用戶態(tài)地址空間的映射頁(yè)面、保護(hù)磁盤數(shù)據(jù)的頁(yè)面緩存的頁(yè)面、塊設(shè)備緩沖頁(yè)、磁盤緩存的頁(yè)面(入inode cache),如果有必要的話,需同步磁盤映像上的數(shù)據(jù)。4)Discardable可丟棄的頁(yè)面,入內(nèi)存緩存中的無(wú)用頁(yè)面(slab分配器中的頁(yè)面)、dentry cache的頁(yè)面。PFRA算法是基于經(jīng)驗(yàn)而非理論的算法,它的設(shè)計(jì)原則如下:1)首先釋放無(wú)損壞的頁(yè)面。進(jìn)程不再引用的磁盤和內(nèi)存緩存應(yīng)該先于用戶態(tài)地址空間的頁(yè)面釋放。2)標(biāo)志所有進(jìn)程態(tài)進(jìn)程的頁(yè)面為可回收的。3)多進(jìn)程共享頁(yè)面的回收,要先清除引用該頁(yè)面的進(jìn)程頁(yè)表項(xiàng),然后再回收。4)回收“不在使用的”頁(yè)面。PFRA用LRU鏈表把進(jìn)程劃分為in-use和unused兩種,PFRA僅回收unused狀態(tài)的頁(yè)面。Linux使用PTE中的Accessed比特位實(shí)現(xiàn)非嚴(yán)格的LRU算法。頁(yè)面回收通常在三種情況下執(zhí)行:1)系統(tǒng)可用內(nèi)存比較低時(shí)進(jìn)行回收(通常發(fā)生在申請(qǐng)內(nèi)存失敗)。2)內(nèi)核進(jìn)入suspend-to-disk狀態(tài)時(shí)進(jìn)行回收。3)周期性回收,內(nèi)核線程周期性激活并在必要時(shí)進(jìn)行頁(yè)面回收。Low on memory回收有以下幾種情形:1)_ _getblk( )調(diào)用的grow_buffers( )函數(shù)分配新緩存頁(yè)失敗;2)create_empty_buffers( )調(diào)用的alloc_page_buffers( )函數(shù)為頁(yè)面分配臨時(shí)的buffer head失?。?)_ _alloc_pages( )函數(shù)在給定內(nèi)存區(qū)里分配一組連續(xù)的頁(yè)面幀失敗。周期性回收涉及的兩種內(nèi)核線程:1)Kswapd內(nèi)核線程在內(nèi)存區(qū)中檢測(cè)空閑頁(yè)面是否低于pages_high的門檻值;2)預(yù)定義工作隊(duì)列中的事件內(nèi)核線程,PFRA周期性調(diào)度該工作隊(duì)列中的task回收slab分配器中所有空閑的slab;所有用戶空間進(jìn)程和頁(yè)面緩存的頁(yè)面被分為活動(dòng)鏈表和非活動(dòng)鏈表,統(tǒng)稱LRU鏈表。每個(gè)區(qū)描述符中包括active_list和inactive_list兩個(gè)鏈表分別將這些頁(yè)面鏈接起來(lái)。nr_active和nr_inactive分別表示這兩種頁(yè)面數(shù)量,lru_lock用于同步。頁(yè)描述符中的PG_lru用于標(biāo)志一個(gè)頁(yè)面是否屬于LRU鏈表,PG_active用于標(biāo)志頁(yè)面是否屬于活動(dòng)鏈表,lru字段用于把LRU中的鏈表串起來(lái)?;顒?dòng)鏈表和非活動(dòng)鏈表的頁(yè)面根據(jù)最近的訪問(wèn)情況進(jìn)行動(dòng)態(tài)調(diào)整。PG_referenced標(biāo)志就是此用途。處理LRU鏈表的函數(shù)有:add_page_to_active_list()、add_page_to_inactive_list()、activate_page()、lru_cache_add()、lru_cache_add_active()等,這些函數(shù)比較簡(jiǎn)單。shrink_active_list ( )用于將頁(yè)表從活動(dòng)鏈表移到非活動(dòng)鏈表。該函數(shù)在shrink_zone()函數(shù)執(zhí)行用戶地址空間的頁(yè)面回收時(shí)執(zhí)行。5交換分區(qū):系統(tǒng)可以有MAX_SWAPFILES的交換分區(qū),每個(gè)分區(qū)可放在磁盤分區(qū)上或者普通文件里。每個(gè)交換區(qū)由一系列頁(yè)槽
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- T-ZRIA 002-2024 工業(yè)巡檢四足機(jī)器人通.用技術(shù)條件
- T-ZSM 0058-2024“領(lǐng)跑者”評(píng)價(jià)技術(shù)要求 飾面木質(zhì)墻板
- 二零二五年度林業(yè)林地經(jīng)營(yíng)權(quán)買賣合同
- T-ZJATA 0022-2024 土壤中揮發(fā)性有機(jī)物測(cè)定用便攜式氣相色譜-質(zhì)譜聯(lián)用儀
- T-ZJZYC 022-2024 靈芝工廠化生產(chǎn)技術(shù)規(guī)程
- 二零二五年度簽約主播與汽車廠商合作直播試駕體驗(yàn)協(xié)議
- 二零二五年度會(huì)展中心物業(yè)管理服務(wù)托管協(xié)議
- 二零二五年度新能源項(xiàng)目投資對(duì)賭協(xié)議
- 二零二五年度股東清算與清算資產(chǎn)評(píng)估及拍賣協(xié)議
- 二零二五年度創(chuàng)新創(chuàng)業(yè)團(tuán)隊(duì)員工合作協(xié)議書
- 畢業(yè)設(shè)計(jì)外文文獻(xiàn)-Spring Boot
- 六年級(jí)下冊(cè)《生命.生態(tài).安全》全冊(cè)教案(表格式)
- 采購(gòu)入庫(kù)單模板
- GB 14930.1-2022食品安全國(guó)家標(biāo)準(zhǔn)洗滌劑
- GB/T 15566.6-2007公共信息導(dǎo)向系統(tǒng)設(shè)置原則與要求第6部分:醫(yī)療場(chǎng)所
- 中國(guó)電信教育基地市級(jí)“三通兩平臺(tái)”建設(shè)方案(教育機(jī)構(gòu))
- 火力發(fā)電廠節(jié)能技術(shù)經(jīng)濟(jì)指標(biāo)釋義
- 智能制造知識(shí)課件
- 雙方責(zé)任及工程分工界面
- 2017醫(yī)學(xué)倫理知情同意書
- 中醫(yī)學(xué)-導(dǎo)論課件
評(píng)論
0/150
提交評(píng)論