Linux进程调度切换和虚拟空间管理深入分析_第1页
Linux进程调度切换和虚拟空间管理深入分析_第2页
Linux进程调度切换和虚拟空间管理深入分析_第3页
Linux进程调度切换和虚拟空间管理深入分析_第4页
Linux进程调度切换和虚拟空间管理深入分析_第5页
已阅读5页,还剩96页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、一、Linux进程切换深入分析#define CLONE_KERNEL(CLONE_FS | CLONE_FILES | CLONE_SIGHAND)创建内核线程时使用的CLONE标志。1#defiine unnlikelly(x)_builttin_exxpect(!(x), 0)编译器优化,实实际返回值xx是整型表达达式,0表示示并不预期该该事件发生,也也就是说x为为0的可能性性很小,这是是为了让编译译器对下面得得语句进行优优化。2进程内核态态堆栈结构:进程是动态实体体,进程描述述符是存放在在动态内存中中的。在一块块进程内存区区上,Linnux存放了了两个数据结结构:指向ttask_sst

2、ructt得threead_innfo和内核核态的进程栈栈。大小一般般2页8K,这这要求页面帧帧对齐2的113次幂,在在X86上编编译时可以配配置大小为44K。thrread_iinfo在内内存区开始处处,内核栈从从内存尾向下下增长。在CC语言中可以以用unioon结构表示示:图1. 8K内内核栈和进程程描述符taask_sttruct及及threaad_inffo的相互关关系union tthreadd_unioon struuct thhread_info threaad_inffo;unsiigned long stackk20488; /* 10244 for 4KB sstackss

3、 */;CPU的espp寄存器用于于执行堆栈的的顶部指针,当当从用户态转转向内核态时时,进程内核核栈总是空的的,所以essp就会执行行堆栈底部。使用allocc_threead_innfo和ffree_tthreadd_infoo用于分配和和释放一个存存放threead_innfo结构和和内核堆栈的的内存区。内核通过当前eesp指针可可以很方便的的得到thrread_iinfo结构构的地址。ccurrennt_thrread_iinfo(vvoid)的的原理即如下下:movl $00 xfffff2000,%ecx /* orr 0 xffffff0000 forr 4KB stackks *

4、/andll %espp,%ecxxmovl %eecx,pthread_info中中task指指针是第一个个,所以cuurrentt宏相当于ccurrennt_thrread_iinfo( )-taask,从而而也就得到ttask指针针。每个进程有自己己独立得进程程空间,所有有进程共享CCPU寄存器器。进程继续续执行时必须须装入寄存器器恢复得数据据集称为硬件件上下文环境境。在Linnux中部分分硬件上下文文存放在进程程描述符中,部部分存放到内内核态堆栈里里。3.进程切换换堆栈原理:每个进程有自己己独立得进程程空间,所有有进程共享CCPU寄存器器。进程继续续执行时必须须装入寄存器器恢复得数据据

5、集称为硬件件上下文环境境。在Linnux中部分分硬件上下文文存放在进程程描述符中,部部分存放到内内核态堆栈里里。80 x86体系系支持在进程程TSS段跳跳转时自动执执行进程硬件件上下文切换换。Linuux使用软件件方法实现。软软件方式效率率差不多,当当更灵活,可可以控制流程程,留下优化化空间。80 x86用TTSS段保存存硬件上下文文内容,每个个CPU有一一个TSS段段。从用户态态到内核态切切换时,从TTSS中取出出内核栈地址址。用户态进进程访问I/O端口时,TTSS中的II/O访问位位图可以验证证权限。tsss_strruct描述述了TSS格格式,iniit_tsss存放初始TTSS内容,每

6、每次进程切换换,内核更新新TSS中的的某些字段,以以反映当前运运行进程的权权限等级。每每个进程有个个反映任务CCPU状态的的threaad_strruct结构构变量thrread,除除eax、eecx等通用用寄存器内容容保存在内核核态堆栈中,其其他大部分寄寄存器都保存存在次结构中中。该结构一一部分对应于于tss_sstructt中的内容,进进程切换时把把threaad中某些内内容更新到ttss_sttruct中中就可以反映映当前任务的的运行CPUU环境。struct tss_sstructtunssignedd shorrtbaack_liink,_blh;unssignedd longges

7、sp0;unssignedd shorrtsss0,_sss0h;unssignedd longgessp1;unssignedd shorrtsss1,_sss1h;/* sss1 is used to caache MMSR_IAA32_SYYSENTEER_CS */unssignedd longgessp2;unssignedd shorrtsss2,_sss2h;unssignedd longg_cr3;unssignedd longgeiip;unssignedd longgefflags;unssignedd longgeaax,ecxx,edx,ebx;unssignedd l

8、onggessp;unssignedd longgebbp;unssignedd longgessi;unssignedd longgeddi;unssignedd shorrtess, _eesh;unssignedd shorrtcss, _ccsh;unssignedd shorrtsss, _sssh;unssignedd shorrtdss, _ddsh;unssignedd shorrtfss, _ffsh;unssignedd shorrtgss, _ggsh;unssignedd shorrtlddt, _ldth;unssignedd shorrttrrace, io_bii

9、tmap_base;/* The eextra 1 is theree becaause tthe CPPU willl acccess aan* addittionall bytee beyoond thhe endd of tthe IOO permmissioon* bitmaap. Thhe exttra byyte muust bee all 1 bitts, annd musst* be wiithin the llimit.*/unssignedd longgioo_bitmmapIOO_BITMMAP_LOONGS + 1;/* Cachee the curreent maax

10、imumm and the llast ttask tthat uused tthe biitmap:*/unssignedd longg io_bbitmapp_max;strruct tthreadd_struuct *iio_bittmap_oowner;/* pads the TTSS too be ccachelline-aaligneed (siize iss 0 x1000)*/unssignedd longg _caacheliine_fiiller35;/* . annd theen anoother 0 x1000 bytees forr emerrgencyy kernn

11、el sttack*/unssignedd longg stacck64; _atttributte_(packeed);struct threaad_strruct/* cachhed TLLS desscripttors. */struct desc_strucct tlss_arraayGDTT_ENTRRY_TLSS_ENTRRIES;unsigneed lonngeesp0;unsigneed lonngssysentter_css;unsigneed lonngeeip;unsigneed lonngeesp;unsigneed lonngffs;unsigneed lonnggg

12、s;/* Harddware debuggging regissters */unsigneed lonngddebugrreg8;/* %dbb0-7 ddebug regissters */* faullt inffo */unsigneed lonngccr2, ttrap_nno, errror_ccode;/* floaating pointt infoo */union ii387_uunioni3387;/* virttual 886 modde inffo */struct vm86_strucct _uuser * vm866_infoo;unsigneed lonngscc

13、reen_bitmaap;unsigneed lonngv886flaggs, v886maskk, savved_essp0;unsigneed inttsaaved_ffs, saaved_ggs;/* IO ppermisssionss */unsigneed lonng*io_biitmap_ptr;unnsigneed lonngiiopl;/* max allowwed poort inn the bitmaap, inn bytees: */unsigneed lonngiio_bittmap_mmax;4进程切换流流程解析swwitch_to进程切换本质上上两步:1)进程页页表

14、PGD切切换;2)内核态态堆栈和硬件件上下文切换换(包括CPPU寄存器);上面两步步通过conntext_switcch()实现现,它通过调调用swittch_mmm()切换进进程空间,sswitchh_to切换换内核上下文文环境。首先看看conntext_switcch()做了了些什么:1)进程程描述符中aactivee_mm执行行进程使用的的地址空间,mmm执行进程程拥有的地址址空间,对于于普通进程它它们相同。对对于内核线程程,它的mmm总为NULLL。所以ccontexxt_swiitch()首先判断iif (!nnext-mm)即nnext为内内核线程,则则使用preev的进程地地址空

15、间:if (!neext-mmm) nexxt-acctive_mm = prev-actiive_mmm; atoomic_iinc(&pprev-activve_mm-mm_ccount); entter_laazy_tllb(preev-acctive_mm, nnext);2)否则则,如果neext是普通通进程,则用用next进进程空间替换换prev的的地址空间:swiitch_mmm(olddmm, mmm, neext);3)如果果prev是是内核线程或或者正在退出出,则设置pprev-activve_mm和runqqueue的的prevv_mm为NNULL:if (!prrev-

16、mmm) pprev-activve_mm = NULLL;WWARN_OON(rq-prevv_mm);rrq-prrev_mmm = olldmm;下面看看swiitch_mmm()如何何切换进程空空间:1)获取取cpu逻辑辑号。2)cppu_cleear(cppu, prrev-ccpu_vmm_maskk)清除cppu_vm_mask位位标志。3)peer_cpuu(cpu_tlbsttate, cpu).statee = TLLBSTATTE_OK设设置cpu_tlbsttate状态态。4)peer_cpuu(cpu_tlbsttate, cpu).activve_mm = nexx

17、t设置cppu_tlbbstatee的actiive_mmm为nextt。5)cppu_sett(cpu, nextt-cpuu_vm_mmask)设设置nextt的cpu_vm_maask标志。6)looad_crr3(nexxt-pggd)装载nnext的ppgd页表到到cr3寄存存器。7)如果nnext的LLDT描述符符改变,则加加载nextt的LDT描描述符。if (unllikelyy(prevv-conntext.ldt != nexxt-coontextt.ldt)load_LDT_nnolockk(&nexxt-coontextt);最后,swittch_too进行内核堆堆栈和

18、CPUU环境切换操操作:#definee swittch_too(prevv,nextt,lastt) do unssignedd longg esi,edi;asmm volaatile(pushhflnt/* Savve flaags */pusshl %ebpnntmovvl %eesp,%00nt/* savve ESPP */movvl %5,%esppnt/* resstore ESP */movvl $1ff,%1nnt/* savve EIPP */pusshl %66nt/* reestoree EIP */jmpp _swwitch_ton1:tpoppl %eebpntp

19、oppfl:=mm (prrev-tthreadd.esp),=m (preev-thhread.eip),=aa (laast),=S (esi),=D (edi):m (nexxt-thhread.esp),m (next-threead.eiip),2 (preev), d (nnext); whilee (0)流程描述,prrev是进程程A的tassk结构,nnext是进进程B的taask结构,llast是进进程C的结构构:1)保存pprev和nnext指针针的值到eaax和edxx:movl prrev, %eaxmovl neext, %edx2)保存eeflagss和ebbp寄存

20、器器内容到prrev内核态态堆栈中:pushflpushl %ebp3)将essp内容保存存到prevv-thrread.eesp中,该该字段执行pprev内核核堆栈的toop地址。movl %eesp,4884(%eaax)4)将neext-tthreadd.esp加加载到espp中,现在开开始,espp执行nexxt的内核堆堆栈,进程切切换完成。movl 4884(%eddx), %esp5)保存下下面Labeel 1到pprev-threaad.eipp指针中,当当prev进进程恢复运行行时,从该位位置开始运行行。movl $11f, 4880(%eaax)6)将neext-tthrea

21、dd.eip的的指针内容压压到nextt的内核态堆堆栈中,通常常它的内容也也是Labeel 1。pushl 4480(%eedx)7)跳转到到_swiitch_tto()C函函数执行。jmp _sswitchh_to8)被替换换的进程A继继续执行,它它在Labeel 1处,首首先是恢复eeflagss和ebp寄寄存器内容。注注意这里是发发生在调度器器选择preev在CPUU上运行后,次次数esp已已经执行了pprev的内内核堆栈。1:ppopl %ebppoppfl9)将eaax内容保存存到lastt任务结构中中。这里eaax是被进程程A切换下来来的进程C的的task结结构指针。movl %e

22、eax, llast5_swiitch_tto深入分析析_switcch_to参参数是存放在在eax和eedx中的内内容,这通过过#definee fasttcall_atttribuute_(regpparm(33)告诉诉gcc编译译器。1)获取ttss_sttruct tss、pprev_pp和nextt_p的thhread_strucct结构prrev和neext、当前前CPU逻辑辑ID。2)调用_unlaazy_fppu(preev_p)根根据条件标志志选择是否保保存prevv_p的FPPU, MMMX,和XXMM寄存器器内容。3)loaad_espp0(tsss, nexxt)将ne

23、ext的堆栈栈地址存放到到tss中:tss-esp0 = thrread-esp0。4)savvesegmment(ggs, prrev-ggs)保存ggs寄存器到到prev-gs,ffs已经在栈栈入口保存,ees和ds在在内核态下不不需要保存。5)loaad_TLSS(nextt, cpuu)从nexxt的tlss_arraay缓存中中加载线程的的Threaad-Loccal Sttoragee描述符。TTLS在GDDT表中位置置6、7、88。cpu_gdtt_tabllecpuu6 = nexxt_p-threaad.tlss_arraay0;cpu_gdtt_tabllecpuu7 =

24、nexxt_p-threaad.tlss_arraay1;cpuu_gdt_tableecpu8 = nextt_p-tthreadd.tls_arrayy2;6)如果当当前特权级别别是0并且pprev-iopl != neext-iiopl则恢恢复IOPLL设置sett_iopll_maskk(nextt-ioppl)。7)根据tthreadd_infoo的TIF标标志_TIFF_WORKK_CTXSSW和TIFF_IO_BBITMAPP判断是否需需要处理deebug寄存存器和IO位位图:_sswitchh_to_xxtra(nnext_pp, tsss);l只有有当nextt_p挂起时时即

25、if (test_tsk_tthreadd_flagg(nextt_p, TTIF_DEEBUG)使用了deebug寄存存器才需要恢恢复set_debuggreg(nnext-debuggregii, i)。只有调试试器需要监控控prev的的状态时,pprev_pp-thrread.ddebugrreg数组的的内容才会被被修改。Deebug寄存存器dr0dr7,ddr4和drr5不用。l当pprev_pp或者nexxt_p定义义了自己的II/O访问位位图时,必须须更新TSSS的I/O bitmaap。if (preev_p-threaad.io_bitmaap_ptrr | nnext_pp-

26、thrread.iio_bittmap_pptr)haandle_io_biitmap(&nextt_p-tthreadd, &innit_tssscpuu);进程的I/O访访问位图存放放在io_bbitmapp_ptr指指针里,通常常进程很少修修改IO位图图,只有当前前时间片中访访问IO端口口才会把实际际的IO位图图加载到TSSS中。当nnext_pp没有自定义义位图时:tss-ioo_bitmmap_baase = INVALLID_IOO_BITMMAP_OFFFSET;返回如果果next = tsss-ioo_bitmmap_owwner则设设置有效的偏偏移量:tsss-ioo_bit

27、mmap_baase = IO_BIITMAP_OFFSEET;返回回否则则tss-io_biitmap_base = INVVALID_IO_BIITMAP_OFFSEET_LAZZY;只有第二种种情况tsss-io_bitmaap_basse设置的是是有效的ioo_bitmmap偏移量量,对于其他他两种情况,当当用户进程访访问I/O端端口时将会触触发Genneral proteectionn的异常常,do_ggeneraal_prootectiion( )异常处理函函数根据ioo_bitmmap的值处处理异常:如如果是0 x88000(IINVALIID_IO_BITMAAP_OFFFSE

28、T)则则发送SIGGSEGV信信号给用户进进程;如果是是0 x90000(INVVALID_IO_BIITMAP_OFFSEET_LAZZY)则拷贝贝进程的thhread中中的io_bbitmapp_ptr内内容到io_bitmaap中,并设设置io_bbitmapp_basee为正确的偏偏移量(1004)。8)dissable_tsc(pprev_pp, nexxt_p)设设置cr4中中的TSC Disabble位。9)arcch_leaave_laazy_cppu_modde()设置置CPU的llazy模式式。10)如果nexxt_p-fpu_ccounteer 55则恢复neext_p的

29、的FPU寄存存器内容:math_sttate_rrestorre()。FFPU寄存器器存放在neext_p-threead-ii387中,ii387是ii387_uunion的的unionn结构:union ii387_uunion struct i387_fsavee_struuctfsavve;struct i387_fxsavve_strructfxsaave;struct i387_soft_strucct sofft;struct i387_fxsavve_strruct unsigneed shoortccwd;unsigneed shoortsswd;unsigneed shoo

30、rtttwd;unsigneed shoortffop;longfip;longfcs;longfoo;longfos;longmxcssr;longmxcssr_massk;longst_sspace32;/* 8*16 bytess for each FP-reeg = 1128 byytes */longxmm_spacee32;/* 8*16 bytess for each XMM-rreg = 128 bbytes */longpaddding556; _atttributte_ (aliggned (16);11)如果需要,则则从nextt-gs中中恢复gs寄寄存器内容。if (p

31、reev-gss | neext-ggs)loaadsegmment(ggs, neext-ggs);二、Linuxx实时调度sscheduule1概述三种调度策略:SCHEDD_FIFOO,SCHEED_RR和和SCHEDD_NORMMAL。FIFO实时调调度算法当调调度器将CPPU指定给某某个进程时,它它把该进程放放到运行队列列首;除非有有更高优先级级的进程,否否则该进程将将一直占用CCPU。Round RRobin实实时进程调度度把CPU指指定给某进程程,把它放到到运行队列尾尾。时间片运运行完再选择择其他进程调调度。这样保保证了同优先先级的公平竞竞争CPU。SCHED_NNORMALL是

32、普通的基基于运行时间间和等待时间间等,动态调调整进程优先先级的一种调调度策略。实时进程优先级级11000,普通10011399。2实时进程调调度的时机1)该进程程被更高优先先级的进程抢抢占;2)进程执执行一个阻塞塞操作,被放放到睡眠队列列,状态为TTASK_IINTERRRUPTIBBLE或TAASK_UNNINTERRRUPTIIBLE;3)进程被被终止(状态态为TASKK_STOPPPED或或TASK_TRACEED),或者者进程被杀死死(状态为EEXIT_ZZOMBIEE或EXXIT_DEEAD)4)进程调调用scheed_yieeld()主主动放弃CPPU;5)RR实实时进程用完完了C

33、PU分分配的时间片片;3调度器相关关函数1)schheduleer_ticck( )更新当前进程的的运行时间片片tick值值,在upddate_pprocesss_timmes( )中调用,判判断进程的时时间片是否用用完。2)tryy_to_wwake_uup( )唤醒一个睡眠的的进程并把它它的状态设为为TASK_RUNNIING,插入入到运行队列列中。3)reccalc_ttask_pprio( )更新进程的睡眠眠时间和动态态优先级,SSCHED_NORMAAL调度。4)schhedulee( )进程调度5)loaad_ballance()SMP系统的负负载均衡。4scheddule( )函

34、数进程调度有两种种方式:直接接调用和延迟迟调用。直接调用schhedulee,当前进程程资源不可用用时会直接调调用调度器,这这种情况下,内内核线程进行行如下处理:1)将cuurrentt插入到合适适的等待队列列中;2)将cuurrentt状态变为TTASK_IINTERRRUPTIBBLE或TTASK_UUNINTEERRUPTTIBLE3)调用sscheduule();4)检查资资源是否可用用,如果不可可用,转到第第2)步;5)一旦资资源可用,从从等待队列中中移除currrent进进程;在设备驱动程序序中也经常会会检查TIFF_NEEDD_RESCCHED并调调用scheedule()。延迟

35、调用方式是是通过设置ccurrennt进程的TTIF_NEEED_REESCHEDD标志为1。当当恢复用户态态进程的执行行前,会检查查该标志并决决定是否调用用scheddule()。延迟调度度的情形有:1)在sccheduller_tiick()中中如果currrent用用完了时间片片则设置该标标志;2)在trry_to_wake_up( )中唤醒一个个进程并且该该进程比当前前运行进程优优先级高。3)调用ssched_setsccheduller()时时。schedulle()函数数工作流程:进程切换前的工工作:1)禁止内内核抢占,初初始化局部变变量prevv,释放prrev占有的的大内核锁;

36、need_reeschedd:preeempt_disabble();preev = ccurrennt;rellease_kerneel_locck(preev);2)读取调调度TSC时时间,计算调调整run_time时时间, 更新新调度状态rrq-scched_ccnt参数,获获取rq的sspin锁:spin_lock_irq(&rq-llock)。3)检查pprev状态态:如果状态态不是TASSK_RUNNNING且且没有在内核核态被抢占,则则从运行队列列中移除;但但是如果prrev状态是是TASK_INTERRRUPTIIBLE并且且拥有非阻塞塞挂起的信号号,则把进程程状态设为TTAS

37、K_RRUNNINNG不移出运运行队列。iff (preev-sttate & !(ppreemppt_couunt() & PREEEMPT_ACTIVVE) swittch_coount = &preev-nvvcsw;if (unlikkely(prev-statte & TTASK_IINTERRRUPTIBBLE) &uunlikeely(siignal_pendiing(prrev)prev-statte = TTASK_RRUNNINNG;elsee if (pprev-statee = TTASK_UUNINTEERRUPTTIBLE)rrq-nrr_uninnterruupt

38、iblle+;deacttivatee_taskk(prevv, rq);4)获取当当前CPU逻逻辑号,如果果当前运行队队列为空,则则调用idlle_ballance(cpu, rq)从其其他CPU运运行队列上拉拉进程到本地地CPU的运运行队列上。如如果调整后,当当前运行队列列仍为空则nnext赋为为idle进进程,跳转到到任务切换代代码行去。if (unliikely(!rq-nr_ruunningg) idlee_balaance(ccpu, rrq);if (!rq-nr_ruunningg) next = rq-idlee;rq-eexpireed_timmestammp = 00;g

39、oto switcch_tassks;5)如果rrunqueeue中有进进程,并且当当前活得进程程数为0,则则交换acttive和和expiired队列列指针。arrray = rq-aactivee;if (unliikely(!arraay-nrr_actiive) scheedstatt_inc(rq, ssched_switcch);rq-activve = rrq-exxpiredd;rq-expirred = arrayy;arraay = rrq-acctive;rq-expirred_tiimestaamp = 0;rq-best_expirred_prrio = MAX_PP

40、RIO;6)从运行行队列的活动动prio_arrayy数据的位图图中查找第一一个位设置为为1的索引,根根据索引找到到该优先级队队列的第一个个task。idx = ssched_find_firstt_bit(arrayy-bittmap);queeue = arrayy-queeue + idx;nexxt = llist_eentry(queuee-nexxt, sttruct task_strucct, ruun_lisst);7)如果nnext是普普通进程,并并且nextt-sleeep_tyype是SLLEEP_IINTERAACTIVEE或SLEEEP_INNTERRUUPTED,则

41、则重新计算进进程睡眠时间间和进程优先先级。进程切换工作:8)更新ssched_goidlle,预期nnext结构构数据,清除除TIF_NNEED_RRESCHEED标志,设设置quieescentt状态计数为为1:rcuu_dataa-paassed_quiessc = 11;switch_taskss:if (nexxt = rq-iidle)scchedsttat_innc(rq, scheed_goiidle);prefetcch(nexxt);prefetcch_staack(neext);clear_ttsk_neeed_reeschedd(prevv);rcu_qscctr_inn

42、c(tassk_cpuu(prevv);9)更新pprev进程程运行时间戳戳prev-sleeep_avgg,prevv-timmestammp;10)调度信息切切换到nexxt,更新nnext;时时间戳和运行行队列信息:sched_iinfo_sswitchh(prevv, nexxt);if (likkely(pprev != nexxt) neext-ttimesttamp = nextt-lasst_rann = noow;rqq-nr_switcches+;rqq-currr = nnext;+*swittch_coount;11)进行进程切切换,conntext_switcch参见

43、前面面的分析,它它进行进程空空间和内核堆堆栈切换。ppreparre_locck_swiitch功功能是在定义义了_ARRCH_WAANT_INNTERRUUPTS_OON_CTXXSW情况下下,在切换前前开中断sppin_unnlock_irq(&rq-llock);barrrier()是保证代码码执行顺序不不变。prreparee_taskk_swittch(rqq, nexxt);prrev = conteext_swwitch(rq, pprev, next);baarrierr();fiinish_task_switcch(thiis_rq(), prrev);进程切换后的工工作:进

44、程切换conntext_switcch语句之后后的代码并不不是由nexxt进程立即即执行的,而而是由调度器器选择preev进程继续续执行的。次次时prevv变量指向的的已经是被pprev进程程替换的其他他进程的指针针。12)finissh_tassk_swiitch()必须与prreparee_taskk_swittch配对使使用,并主要要锁的顺序。它它所做的工作作,finiish_loock_swwitch调调用locaal_irqq_enabble(),获取preev的状态和和rq-pprev_mmm,如果mmm非空,则则调用mmddrop(mmm)减少mmm的引用计计数,如果为为0则释放

45、进进程页表和虚虚拟空间。如如果prevv_statte为TASSK_DEAAD则释放进进程的tassk结构。struct mm_sttruct *mm = rq-prev_mm;long prrev_sttate;rq-preev_mm = NULLL;prev_sttate = prevv-staate;finish_arch_switcch(preev);finish_lock_switcch(rq, prevv);if (mm)mmmdrop(mm);if (unllikelyy(prevv_statte = TASK_DEAD) kpprobe_flushh_taskk(prevv);

46、puut_tassk_strruct(pprev);13)最后,iff (unllikelyy(taskk-locck_deppth = 0)则则重新获取大大内核锁_reacqquire_kerneel_locck,否则ggoto nneed_rrescheed_nonnpreemmptiblle;允许许抢占,如果果TIF_NNEED_RRESCHEED被设置,则则跳转到neeed_reeschedd重新进行调调度。prev = curreent;if (unllikelyy(reaccquiree_kernnel_loock(prrev) vm_oops提供了了nopagge()函数数,则用

47、它填填充数据;否否则调用doo_anonnymouss_pagee()匿名函函数来填充数数据。如果被被文件或设备备映射,如果果时文件映射射,fileemap_nnopagee()将替代代nopagge()函数数,如果由虚虚拟文件映射射而来,则sshmem_nopagge()。每每种设备驱动动将提供不同同的nopaage()函函数,该函数数返回strruct ppage结构构。3请求换页:将页面交换至后后援存储器后后,函数doo_swapp_pagee()负责将将页面读入内内存,将在后后面讲述。通通过PTE的的信息就足够够查找到交换换的页面。页页面交换出去去时,一般先先放到交换高高速缓存中。缺页

48、中断时如果果页面在高速速缓存中,则则只要简单增增加页面计数数,然后把它它放到进程页页表中并计数数次缺页中断断发生的次数数。如果页面仅存在在磁盘中,LLinux将将调用swaapin_rreadahhead()读取它及后后续的若干页页面。4页面帧回收收除了slab分分配器,系统统中所有正在在使用的页面面都存放在页页面高速缓存存中,并由plru链接接在一起。SSlab页面面不存放到高高速缓存中因因为基于被sslab使用用的对象对页页面计数很困困难。除了查查找每个进程程页表外没有有其他方法能能把struuct paage映射为为PTE,查查找页表代价价很大。如果果页面高速缓缓存中存在大大量的进程映映

49、射页面,系系统将会遍历历进程页表,通通过swapp_out()函数交换换出页面直到到有足够的页页面空闲,而而共享页会给给swap_out()带来问题。如如果一个页面面是共享的,同同时一个交换换项已经被分分配,PTEE就会填写所所需信息以便便在交换分区区里重新找到到该页并将引引用计数减11。只有引用用计数为0时时该页才能被被替换出去。内存和磁盘缓存存申请越来越越多的页面但但确无法判断断如何释放进进程页面,请请求分页机制制在进程页面面缺失时申请请新页面,但但它却不能强强制释放进程程不再使用的的页面。Thhe Pagge Fraame Reeclaimming AAlgoriithm(PPFRA)页

50、页面回收算法法用于从用户户进程和内核核cachee中回收页面面放到伙伴系系统的空闲块块列表中。PPFRA必须须在系统空闲闲内存达到某某个最低限度度时进行页面面回收,回收收的对象必须须是非空闲页页面。可将系统页面划划分为四种:1)Unrreclaiimablee不可回收的的,包括空闲闲页面、保留留页面设置了了PG_reeserveed标志、内内核动态分配配的页面、进进程内核栈的的页面、设置置了PG_llockedd标志的临时时锁住的页面面、设置了VVM_LOCCKED标志志的内存页面面。2)Swaappablle可交换的的页面,用户户进程空间的的匿名页面(用户堆栈)、tmpffs文件系统统的映射

51、页面面(入IPCC共享内存页页面),页面面存放到交换换空间。3)Synncablee可同步的页页面,入用户户态地址空间间的映射页面面、保护磁盘盘数据的页面面缓存的页面面、块设备缓缓冲页、磁盘盘缓存的页面面(入inoode caache),如如果有必要的的话,需同步步磁盘映像上上的数据。4)Disscardaable可丢丢弃的页面,入入内存缓存中中的无用页面面(slabb分配器中的的页面)、ddentryy cachhe的页面。PFRA算法是是基于经验而而非理论的算算法,它的设设计原则如下下:1)首先释释放无损坏的的页面。进程程不再引用的的磁盘和内存存缓存应该先先于用户态地地址空间的页页面释放。

52、2)标志所所有进程态进进程的页面为为可回收的。3)多进程程共享页面的的回收,要先先清除引用该该页面的进程程页表项,然然后再回收。4)回收“不不在使用的”页页面。PFRRA用LRUU链表把进程程划分为inn-use和和unuseed两种,PPFRA仅回回收unussed状态的的页面。Liinux使用用PTE中的的Accesssed比特特位实现非严严格的LRUU算法。页面回收通常在在三种情况下下执行:1)系统可可用内存比较较低时进行回回收(通常发发生在申请内内存失败)。2)内核进进入susppend-tto-dissk状态时进进行回收。3)周期性性回收,内核核线程周期性性激活并在必必要时进行页页面

53、回收。Low on memorry回收有以以下几种情形形:1)_ _getbllk( )调调用的groow_bufffers( )函数分分配新缓存页页失败;2)creeate_eempty_buffeers( )调用的allloc_ppage_bbufferrs( )函函数为页面分分配临时的bbufferr headd失败;3)_ _allocc_pagees( )函函数在给定内内存区里分配配一组连续的的页面帧失败败。周期性回收涉及及的两种内核核线程:1)Kswwapd内核核线程在内存存区中检测空空闲页面是否否低于pagges_hiigh的门槛槛值;2)预定义义工作队列中中的事件内核核线程,P

54、FFRA周期性性调度该工作作队列中的ttask回收收slab分分配器中所有有空闲的sllab;所有用户空间进进程和页面缓缓存的页面被被分为活动链链表和非活动动链表,统称称LRU链表表。每个区描描述符中包括括activve_lisst和inaactivee_listt两个链表分分别将这些页页面链接起来来。nr_aactivee和nr_iinactiive分别表表示这两种页页面数量,llru_loock用于同同步。页描述述符中的PGG_lru用用于标志一个个页面是否属属于LRU链链表,PG_activve用于标志志页面是否属属于活动链表表,lru字字段用于把LLRU中的链链表串起来。活活动链表和非

55、非活动链表的的页面根据最最近的访问情情况进行动态态调整。PGG_refeerenceed标志就是是此用途。处理LRU链表表的函数有:add_pagge_to_activve_lisst()、aadd_paage_too_inacctive_list()、acttivatee_pagee()、lrru_cacche_addd()、llru_caache_aadd_acctive()等,这些些函数比较简简单。shrink_activve_lisst ( )用于将页表表从活动链表表移到非活动动链表。该函函数在shrrink_zzone()函数执行用用户地址空间间的页面回收收时执行。5交换分区:系统可

56、以有MAAX_SWAAPFILEES的交换分分区,每个分分区可放在磁磁盘分区上或或者普通文件件里。每个交交换区由一系系列页槽组成成。每个交换换区有个swwap_heeader结结构描述交换换区版本等信信息。每个交交换区有若干干个swapp_exteent组成,每每个swapp_exteent是连续续的物理区域域。对于磁盘盘交换区只有有一个swaap_exttent,对对于文件交换换区则由多个个swap_extennt组成,因因为文件并不不是放在连续续的磁盘块上上的。mksswap命令令可以创建交交换分区。图 交换分区结结构图 交换页结构构swp_typpe()和和swp_ooffsett()函

57、数根根据页槽索引引和交换区号号得到typpe和offfset值,函函数swpp_entrry(typpe,offfset)得得到交换槽。最最后一位总是是清0表示页页不在RAMM上。槽最大大224(64GG)。第一个个可用槽索引引为1。槽索索引不能全为为0。一个页面可能被被多个进程共共用,它可能能被从一个进进程地址空间间换出但仍然然在物理内存存上,因此一一个页面可能能被多次换出出。但物理上上仅第一次被被换出并存储储在交换区上上,接下来的的换出操作只只是增加swwap_maap的引用计计数。swaap_dupplicatte(swpp_entrry_t eentry)的功能正是是用户尝试换换出一个

58、已经经换出的页面面。6交换缓存:多个进程同时换换进一个共享享匿名页时或或者一个进程程换入一个正正被PFRAA换出的页时时存在竞争条条件,引入交交换缓存解决决这种同步问问题。通过PPG_loccked标志志可以保证对对一个页的并并发的交换操操作只作用在在一个页面上上,从而避免免竞争条件。7.页面回收收算法描述:下图是各种情况况下进行页面面回收时的函函数调用关系系图。可以看看出最终调用用函数为caache_rreap()、shriink_sllab()和和shrinnk_lisst()。ccache_reap()用于周期期性回收sllab分配器器中的无用sslab。sshrinkk_slabb()

59、用于回回收磁盘缓存存的页面。sshrinkk_listt()是页面面回收的核心心函数,在最最新代码中该该函数名改为为shrinnk_pagge_lisst()。下下面会重点讲讲解。图中shhrink_cachees()最新新函数名为sshrinkk_zonees()、sshrinkk_cachhe()最新新函数名为sshrinkk_inacctive_list()。其他函函数不变。图PFRA函函数结构调用用关系低内存回收页面面:如上图所示,当当内存分配失失败时,内核核调用freee_morre_memmory(),该函数首首先调用waakeup_bdfluush( )唤醒pdfflush内内核

60、线程触发发写操作,从从磁盘页面缓缓冲中写10024个diirty页面面到磁盘上以以释放包含缓缓冲、缓冲头头和VFS的的数据结构所所占用的页表表;然后进行行系统调用ssched_yieldd( ),以以使pdfllush线程程能够有机会会运行;最后后该函数循环环遍历系统节节点,对每个个节点上的低低内存区(ZZONE_DDMA和ZONE_NORMAAL)调用ttry_too_freee_pagees( )函函数。try_to_free_pagess(struuct zoone *zoness, gfpp_t gffp_massk)函数的的目标是通过过循环调用sshrinkk_slabb()和shh

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论