版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
操作系统OperatingSystem第7章内存管理Chapter9:MemoryManagement内存的基本目的是什么?再回到那个恒久的话题执行程序是计算机的基本任务intmain(intargc,char*argv[]){inti,to,sum=0;to=atoi(argv[1]);for(i=1;i<=to;i++){sum=sum+i;}printf(“%d”,sum);}让程序执行起来就成了最重要的事!intmain(intargc,char*argv[]){inti,to,sum=0;to=atoi(argv[1]);for(i=1;i<=to;i++){sum=sum+i;}printf(“%d”,sum);}源代码第一步:编译——从C到汇编汇编代码.text_entry://入口地址
movax,[8+sp]mov[_environ],axcall_mainpushax//main返回值
call_exit_main:mov[_sum],0subsp,4mov[sp+4],[_environ+4]call_atoimov[_to],ax//atoi返回axmov[_i],11:j>2f,[_i],[_to]movax,[_sum]addax,[_i]mov[_sum],axadd[_i],1jmp1b2:subsp,8mov[sp+4],[_sum]call_printfret.data:_environ:.long0_i:.int0_to:.int0_sum:.int0许多东西有待明确…第二步:链接——从汇编到可执行程序s.cgccs.sass.olda.outf.cgccf.sasf.o_printf.o链接器tttddd.text.data1.oTTDD.text.data2.otttddd.text.dataa.outTTDD前面的程序经过链接以后…_entry://入口地址
call_main_main:mov[_sum],01:j>2f,[_i],[_to]2:call_printfret04080.data:…_sum:.int0
012_printf:…
0ldprintf.osum.o_entry://入口地址
call40_main:mov[300],01:j>80,[_i],[_to]2:call100ret_printf:…
…_sum:.int0
04080100300现在还是程序,不是进程_entry://入口地址
call40_main:mov[300],01:j>80,[_i],[_to]2:call100ret_printf:…
…_sum:.int0
04080100300loadercode0000ffffdataheapstack内存地址进程程序可以执行了吗?…_printf:…ret2:call1001:j>80,[_i],[_to]_main:mov[300],0call40010080300_sum:.int0…codedataheapstack288403041000将代码段放在内存中从0开始的地方程序怎么能正确开始?设置PC=0将数据段放在内存中从288开始的地方如果内存中从0开始的一段内存有专门的用途怎么办?
如存放中断处理需要重定位!…_printf:…ret2:call1001:j>80,[_i],[_to]_main:mov[300],0call401000110010801300_sum:.int0…12881040假设内存从地址1000以后是可以使用的1000是由硬件和操作系统决定的!…_printf:…ret2:call11001:j>1080,[_i],[_to]_main:mov[1300],0call10401000110010801300_sum:.int0…12881040重定位重定位:
为执行程序而对其中出现的地址所做的修改重定位可以执行的时机第一种时机:在编译链接时ld408012_entry://入口地址
call_main_main:mov[_sum],01:j>2f,[_i],[_to]2:call_printfret0.data:_sum:.int00_printf:…0printf.osum.o1000绝对代码:
这样的代码只能放在事先确定的位置上…_printf:…ret2:call11001:j>1080,[_i],[_to]_main:mov[1300],0call1040_sum:.int0…这样的代码叫绝对代码!并发多个程序同时在内存中分别用1000,2000,…吗?第二种时机:载入时3000…_printf:…ret2:call31001:j>3080,[_i],[_to]_main:mov[3300],0call3040_sum:.int0……_printf:…ret2:call1001:j>80,[_i],[_to]_main:mov[300],0call40_sum:.int0…loader装载时的重定位仍然存在缺点…可重定位代码!一旦载入不能移动移动也是很有必要的!一个重要概念:交换(swap)常驻程序(操作系统)进程3内存磁盘进程1进程1睡眠换出进程2换入进程1进程3进程3睡眠换出进程1进程2程序1仍应该是可重定位的!装载后仍需移动!能让更多的进程并发重定位最合适的时机运行时重定位内存中的代码总是可重定位的!基地址base…_printf:…ret2:call1001:j>80,[_i],[_to]_main:mov[300],0call40_sum:.int0…在内存中任何地方300物理地址base+300(偏移)逻辑地址关键在于从逻辑地址算出物理地址:
地址翻译运行时重定位还有一个好处:进程保护780:1(进程1的数据)……
物理内存1260:0(进程2的数据)……
mov[100],ax进程1100:1StackHeapDataCode0000ffffmov[100],ax进程2100:0StackHeapDataCode0000ffff进程1映射表780进程2映射表1260支持移动和保护,因此运行时重定位最常见整理一下思路…进程1进程2操作系统内存逻辑地址0逻辑地址0movax,[100]movax,[100]movax,[100]IR2000基址寄存器PC20001000switchPC1000movax,[100]IR每条指令都要执行,效率很重要。硬件支持(MMU)。进程切换时修改该寄存器现在的问题集中在左边:内存怎么分配?内存分配方案!从最简单的分配方案开始!连续内存分配分区给你一个面包,n个孩子来吃,怎么办?等分…操作系统初始化时将内存等分成k个分区boolAvailSec[k];intSecSize;内存请求算法//进程创建时
1.if(reqSize>SecSize)exit2.找出AvailSec[i]为假的i3.如果有,返回分区i的基址
4.否则,将current加入请求队列等待分区1操作系统分区2分区3分区4请求队列等长分区到变长分区孩子有大有小,进程也有大有小…初始化时将内存分成k个大小不同的分区structSectionAvailSec[k];内存请求算法//进程创建时
1.if(re qSize>MaxSize)exit2.找出AvailSec[i].Size>reqSize且AvailSec[i].Size最小的空闲分区i3.如果有,返回分区i的基址
4.否则,将current加入请求队列等待分区1操作系统分区2分区3分区4请求队列固定分区到可变分区合理的方法应该是根据孩子饥饿程度来分割根据reqSize进行动态分割内存请求算法//进程创建时
1.if(reqSize>内存大小)exit2.if(reqSize>空闲空间总尺寸)将current加入请求队列等待
3.从空闲分区划出一个reqSize,并返回其基地址//哪个空闲分区
4.修改分区数据结构进程1操作系统进程2请求队列空闲可变分区的数据结构进程1操作系统进程2空闲
空闲分区表已分配分区表始址长度250K250K始址长度标志0K100KOS100K100KP1200K50KP20K100K200K250K500K可变分区数据结构的变化(1)始址长度350K150K进程1操作系统进程2空闲
空闲分区表已分配分区表始址长度250K250K始址长度标志0K100KOS100K100KP1200K50KP20K100K200K250K500K进程3空闲
内存请求:reqSize=100K始址长度标志0K100KOS100K100KP1200K50KP2250K350KP3350K可变分区数据结构的变化(2)始址长度350K150K进程1操作系统进程2空闲分区表已分配分区表始址长度350K150K200K50K始址长度标志0K100KOS100K100KP1250K100KP30K100K200K250K500K进程3空闲
进程2执行完毕,释放内存始址长度标志0K100KOS100K100KP1200K50KP2250K100KP3350K空闲
再谈分配找到合适的空闲分区进程1操作系统空闲分区表始址长度350K150K200K50K0K100K200K250K500K进程3空闲
350K空闲
发起请求reqSize=40K怎么办?有2个空闲分区,选哪一个?首先适配:(350,150),快速最佳适配:(200,50),会产生许多小的空闲分区最差适配:(350,150),没有大的空闲分区仍然需要根据应用的特点来决定选取哪种策略再谈分配内存紧缩进程1操作系统空闲分区表始址长度350K150K200K50K0K100K200K250K500K进程3空闲
350K空闲
发起请求reqSize=160K怎么办?总空闲空间>160,但没有一个空闲分区>160,怎么办?内存紧缩:将空闲分区合并在一起,需要移动进程3(复制内容)内存紧缩需要花费大量时间,如果复制速度1M/1秒,则1G内存的紧缩时间为1000秒
17分钟该值表明连续分配技术不合适!分区方案中还有一个问题没有搞清楚reqSize值怎么确定?reqSize=code+data+stack+heapcode和data不难处理,stack和heap难处理:动态增长,预先不知道进程1操作系统预留空间空闲
进程3预留空间怎么办?预留空间预留空间用完了怎么办?找一个更大的空闲空间,移动该进程实际上移动code和data纯粹是浪费怎么办?各个段区别对待,分别分配还有利于建立合适的保护策略!分段(Segmentation)!程序员眼中的程序由若干部分(段)组成,每个段有各自的特点、用途!主程序main函数库sin栈stack动态数组array变量集data程序员眼中的一个程序程序员怎么定位具体指令(数据):<段号,段内偏移>如mov[es:bx],ax00000分段符合用户观点:用户可独立考虑每个段(分治)将段放入内存分段制造了二维空间,而内存是一维的021301230K70K180K330K360K420K460K500K一个进程需要记录多个基址…基址长度保护段号180K150KR0360K60KR/W170K110KR/W2460K40KR3进程段表仍有内存分区表,内存分配算法等等引入段表分段的地址翻译来看一个例子!基址长度保护段号0x40000x0800R00x48000x1400R/W10xF0000x1000R/W20x00000x3000R3OffsetSeg#0141315逻辑地址格式可以有多种格式,如es:bx等movax,_var
… …_var dw0x3141590x40500x2400x240PC=段号:0,偏移2400x4000+240<异常取出指令0x4240逻辑地址物理地址800>240同样可算出_var应该在0x4850处分段技术总结实现机理程序员将程序按含义分成若干部分,即分段ld从0开始编址每个段(链接速度会很快)创建进程(分别载入各个段)时,建立进程段表PC及数据地址要通过段表算出物理地址,到达内存进程切换时,进程段表也跟着切换进程、内存、编译环境、编程思想被扭结在一起了,这正是操作系统的复杂之处!内存仍用可变分区进行管理,载入段时需调分配算法分段技术优缺点分析优点:不同的段有不同的含义,可区别对待每个段独立编址,编程容易(如果是一个大的一维地址空间,程序员一会儿就糊涂了!)(分治)符合人的习惯,程序员感觉舒服缺点:靠近了我们,必然会远离…空间低效空间预留;空闲空间很大却不能分配;内存紧缩著名的碎片概念:空闲的却用不上的空间内部碎片外部碎片分页(Paging)!从连续到离散只有吃到最后才能知道到底有多饿!对于进程的堆栈段,只有运行完才知道嵌套深度一次分配给一点(没有外部碎片,内部碎片有上界)将面包切成薄片,将内存分成页页0页1页2页框0页框1页框2页框3页框4页框5页框6页框7页3分页机制中的页表和分段类似,分页依靠页表结构OffsetPage#01211?逻辑地址格式页面尺寸(4K)多少页页框号保护页号5R01R/W13R27R3进程页表页0页1页2页框0页框1页框2页框3页框4页框5页框6页框7页3分页的地址翻译一个实例!OffsetPage#01211?逻辑地址格式页框号保护页号5R01R/W13R/W27R3movax,_var
… …_var dw0x3141590x40500x240物理地址:0x5240页表指针权限检查访问错误0x00逻辑地址0x240页号偏移2405PCB中应有此值执行该指令需要查两次页表!来考虑一些细节问题和段表不一样,页表可能会很大!页框号保护页号5R01R/W13R3应该是连续的!页是用来解决碎片问题的
页面尺寸应尽量小页面尺寸通常为4K,而EIP是32位,有220个页面
每条指令都需要查几次页表,因此查表效率很重要如果页号不连续,需要查找,折半log(220)=20页框号保护页号5R01R/W127R3有效1101可以删掉!页表项数据结构页基本数据结构PageTableEntry(PTE)页框号(物理页号)ppn保留0LDAPCDPWTUWP01234567811-931-12Intelx86结构的PTEunsignedtranslate(unsignedva,intwr){structpte*pte=&page_table[va>>12];
if(!pte->valid||(wr&&!pte->writeable)) throwaddress_fault; return(pte->ppn<<12)|(va&0xfff);}页框号保护页号5R01R/W127R3有效1101可以删掉!多级页表32位地址空间+4K页面+页号连续
220个页表项220个页表项都得放在内存中,需要4M内存系统中并发10个进程,需要40M内存实际上大部分逻辑地址根本不会用到32位:总空间[0,4G]!引入多级页表,顶层页表常驻内存,不需要映射的逻辑地址不需要建立页表项Offset页号0121132位逻辑地址格式(多级页表)21页目录号2231多级页表时的地址翻译10bits10bits12bits逻辑地址Offset页号页目录号4bytes页表指针4bytesOffset物理页号物理地址4KB页目录驻留内存(4K)程序构成:4M代码+4M数据+4M栈3个页表驻留内存(12K)多级页表使得地址翻译效率更低2级页表访存3次,速度下降到33%3级页表访存4次,速度下降到25%需要注意的事实:内存相比CPU本来就很慢!DRAM9%/year1980198119831984198519861987198819891990199119921993199419951996199719981999200019821101001000“Moore’sLaw”处理器-内存Gap(50%/year)µProc60%/year1级页表访存2次,速度下降50%一次地址访问提高地址翻译的效率多级页表的地址翻译效率很低,要提高效率提高效率的基本想法:硬件支持要很快:这个硬件访在哪里?
页表小寄存器可行,但如果页表很大呢?寄存器TLB(TranslationLook-asideBuffer)是一组相联快速内存页框号保护页号56R14023R/W2029R/X1943R21有效1101修改0100采用TLB后的地址翻译逻辑地址Offset页号Offset物理页号物理地址页框号保护页号56R14023R/W2029R/X1943R21有效1101修改0100TLBTLB命中页表TLB未命中(失效)相联!不是还要查页表吗,似乎更慢了!TLB得以发挥作用的原因TLB命中时效率会很高,未命中效率会降低,平均后仍表现良好。用数字来说明:有效访问时间=HitR(TLB+MA)+(1-HitR)(TLB+2MA)命中率!内存访问时间!TLB时间!有效访问时间=80%(20ns+100ns)+20%(20ns+200ns)=144ns有效访问时间=98%(20ns+100ns)+2%(20ns+200ns)=122ns慢了22%!TLB要想发挥作用,命中率应尽量高TLB越大越好,但TLB价格昂贵,通常[64,1024]为什么TLB条目数在64-1024之间?相比220个页,64很小,为什么TLB就能起作用?程序的地址访问存在局部性某内存引用模式空间局部性(LocalityinSpace)访问概率内存地址程序多体现为循环、顺序结构局部性又是计算机的一个基本特征TLB条目少,页表项多TLB动态变化如果TLB未命中,可将查到的页表项载入TLB如果TLB已经满了,需要选择一个条目来替换进程切换后,所有的TLB表项都变为无效(flush)有些时候希望某些条目固定下来(如内核代码),某些TLB的设计有这样的功能,不被选择替换如果进程马上又切换回来,则这种策略就很低效。有的TLB设计中条目项保存ASID(Address-spaceidentifier)(通常就是PID),此时不需要flush,但查找TLB中还要匹配ASID域。许多东西都在决定操作系统的样子仍然是页表问题yetanothersolution页表可以很大,大的地址空间造价很低;内存却不可能很大,内存是要付费的!页表从物理地址向逻辑地址方向构造:反向页表逻辑页号保护ASID5R41R/W127R6有效1101Offset物理页号物理地址逻辑地址Offset页号ASIDi内存较小反向页表较小;反向页表查找费时可采用哈希页表思想分页技术总结地址空间和内存都分开大小相等的片(页和页框)每个进程用页表(多级、反向等)建立页和页框的映射逻辑地址通过页表算出物理地址,到达内存进程切换时,页表跟着切换进程创建时申请页,可用表、位图等结构管理空闲页实现机理优点:靠近硬件,结构严格,高效使用内存缺点:不符合程序员思考习惯分页更适合于自动化(硬件实现)!段、页结合!让段面向用户、让页面向硬件页框号保护5R1R/W3R/W7R常称为线性地址,以示区别基址长度保护段号0x40000x0800R00x48000x1400R/W10xF0000x1000R/W20x00000x3000R3段号+偏移(cs:ip)逻辑地址页号偏移偏移物理地址物理页号段页式内存管理的基本视图cs:ip逻辑地址0x00345008线性地址内存操作系统段04G用户代码段用户数据段用户栈段地址空间物理地址0x7008对用户是透明的段页结合技术总结线性地址空间和内存被分开大小相等的片(页和页框)进程用页表建立页和页框的映射逻辑地址通过段表加页表算出物理地址,到达内存程序的段划分的是线性地址空间(如0-4G)进程创建申请段(线性地址空间),段申请页(物理内存)实现机理优点:符合程序员习惯,并可高效利用内存缺点:复杂,访问一次地址需要查表好多次…进程切换时,段表和页表都跟着切换一个实际系统的内存管理!Intelx86的内存管理硬件!操作系统课讲硬件,大家可能觉得奇怪段页结合时,进行一次地址翻译需要:(1)找到段表;(2)查段表;(3)找TLB;(4)找到页目录表;(5)查找页目录项;(6)找到页表;(7)查找页表;(8)形成物理地址;(9)需要段越界检查;(10)需要进行段保护权限检查;(11)需要进行页保护权限检查…如此多的事情都用软件实现,其效率会很低物理地址CPUMMU逻辑地址Intelx86的分段硬件选择符偏移值:逻辑地址031015选择符:CS,SS,DS,ES,FS,GS(16位寄存器)全局段描述符表(GDT)局部段描述符表(LDT)GDTRLDTR偏移值:EIP,ESP,ESI,EDI(32位寄存器)GDT:全局段表(所有进程共享,描述OS各段)LDT:局部段表(各进程独有,描述进程各段)GDTR和LDTR:32位段表基址(线性地址)+16位段表长度。需用特权指令LLDT和SLDT等。Intelx86的分段硬件CS段选择符(CS等)的内容格式015123描述符表索引TIRPL13位,213=8K个段TI=0,查GDTTI=1,查LDT请求特权级(用于保护)为提高效率,段寄存器(CS等)还有隐藏部分可见部分段选择符隐藏部分段基址,限长,属性等选择符改变时,隐藏部分自动加载,以后就可以直接用了,不需要查段表了!Intelx86的分段硬件LDT、GDTGDTLDT2Data2Code2进程2LDT1Data1Code1进程1Codeos段描述符:LDT(GDT)中的表项段基址15..0段限长15..004段基址31..24段基址23..16段限长19..16PDPLGIntelx86的分页硬件页目录基址:PDBR,Intelx86称为CR30线性地址3110bits10bits12bits页内偏移页面页目录页目录CR3页表0311211页目录表基地址保留页目录基址需是4K的倍数页目录(页表)表项格式0311211页框(frame)地址1PRW页表占1页内存,连续排放Intelx86的段、页保护硬件前面RPL和DPL的作用015123描述符表索引TIRPL段基址15..0段限长15..004段基址31..24段基址23..16段限长19..16PDPLG处理器保护环0123OS核心OS服务应用程序CPL(当前特权级)CS的位0,1中数据段访问时的特权级检查CPL(CS)RPL(DS)DPL检查DPLCPLDPLRPL其他:Limit,TYPE、页RW略去(容易想象)Intelx86上的Linux内存管理!从哪里开始我们的旅程?内存管理我们最关心什么?给进程提供地址空间从进程的创建开始吗?fork()拷贝地址空间从第一个进程(进程0)开始…sched_init()//在sched.c中
set_tss_desc(gdt+FIRST_TSS_ENTRY,&(init_task.task.tss));set_ldt_desc(gdt+FIRST_LDT_ENTRY,&(init_task.task.ldt));ltr(0);lldt(0);//在include/linux/head.h中
externgdt;//linux/boot/head.s中
.globl_gdt//linux/boot/head.s中
_gdt:.quad0x0000000000000000.quad0x00c09a0000000fff.quad0x00c0920000000fff.quad0x0000000000000000.fill252,8,0256*8=2K//在include/asm/system.h中
#defineset_tssldt_desc(n,addr,type)__asm__(“movw$104,%1\n\t”“movw%%ax,%2\n\t”“rorl$16,%%eax\n\t”“movb%%al,%3\n\t”“movb$”type”,%4\n\t”“movb$0x00,%5\n\t”“movb%%ah,%6\n\t”“rorl$16,%%eax”::”a”(addr),”m”(*(n)),”m”(*(n+2)),”m”(*(n+4)),”m”(*(n+5)),”m”(*(n+6)),”m”(*(n+7)))段基址15..0段限长15..004段基址31..24段基址23..16段限长19..16PDPLGset_tss(ldt)_desc//在include/asm/system.h中
#defineset_tss(ldt)_desc(n,addr)_set_tssldt_desc(((char*)(n)),addr,“0x89(0x82)”)addr是段基址n是描述符位置head.s之后描述符NULLGDTRhead.s中有指令lgdt内核代码段内核数据段未用段(NULL)在include/linux/sched.h中#definelldt(n)=__asm__(“lldt%%ax”::“a”(_LDT(n)))#define_LDT(n)(((unsignedlong)n<<4)+(FIRST_LDT_ENTRY<<3))在include/linux/sched.h中#defineFIRST_TSS_ENTRY4#defineFIRST_LDT_ENTRY(FIRST_TSS_ENTRY+1)set_tss(ldt)_desc之后gdt+FIRST_TSS_ENTRY,&(init_task.task.tss)head.s之后描述符NULLGDTR内核代码段内核数据段未用段(NULL)sched之后描述符NULLGDTR内核代码段内核数据段未用段(NULL)TSS0描述符LDT0描述符在kernel/sched.c中staticuniontask_unioninit_task={INIT_TASK,};ltr(0),lldt(0)在include/linux/sched.h中structtask_struct{//PCBstructdesc_structldt[3];
structtss_structtss;...}GDT的结构sched之后描述符NULLGDTR内核代码段内核数据段未用段(NULL)TSS0描述符LDT0描述符TSS1描述符LDT1描述符TRLDTR进程0LDT表进程0tss段说说tss段tss:taskstatesegment在include/linux/head.h中typedefstructdesc_struct{unsignedlonga,b;}//8字节在include/linux/sched.h中structtss_struct{
longesp0;longss0;//各级栈
longesp1;longss1;longesp2;longss2;
longcr3;//指向进程页目录
longeip;longeflags;longesp,longss;longldt;}tss_struct与特权级栈还记不记得:
用户进程中断时要进入内核栈不同的特权级应该分开访问不同的内存应该有4个栈,其中一个是用户栈不同特权级之间调用(转移)时的栈切换原SS原ESP参数原CS原EIP调用参数调用者原栈Linux用了ring0和ring3特权级一页内存task_struct在include/linux/sched.h中#defineINIT_TASK{{{0,0},{0x9f,0xc0fa00},{0x9f,0xc0f200}}/*ldt[3]*/}{PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,0,0...,0x17,...//6个0x17,cs..gs
_LDT(0),
}/*tss*/INIT_TASK代码段和数据段,基址都是0x0,DPL=3内核栈在kernel/sched.c中uniontask_union={structtask_structtask;charstack[PAGE_SIZE];};已经初始化好的页目录0x17=0b00010111查LDT,第2个段在linux/boot/head.s中.globl_pg_dir_pg_dir:....org0x1000//第1个页表pg0:.org0x2000pg1:.org0x3000pg2:.org0x4000pg3:.org0x5000..._idt:_gdt:页表初始化(pg_dir)4页
1K表项4K=16M页目录表页表pg0页表pg1页表pg2页表pg3head.s部分IDTGDThead.s代码main.ckernel模块mm模块…system模块0x00000x10000x20000x30000x40000x5000就是内存地址物理空间在linux/boot/head.s中after_page_tables:pushl$0pushl$0pushl$0pushl$_main//页初始化后执行main()初始化
jmpsetup_paging
页表初始化setup_paging:movl$1024*5,%ecxxorl%eax,%eaxxorl%edi,%edicld;rep;stoslmovl$pg0+7,_pg_dirmovl$pg1+7,_pg_dir+4movl$pg2+7,_pg_dir+8movl$pg3+7,_pg_dir+12pg0是页表基址,7是属性:页存在,可读写movl$pg3+4092,%edimovl$0xfff007,%eaxstd1:stoslsubl$0x1000,%eaxjge1bxorl%eax,%eax
movl%eax,%cr3movl%cr0,%eaxorl$0x80000000,%eaxmovl%eax,%cr0ret//执行main()函数线性地址=物理地址启动分页,cr0第31位进程0开始执行了吗?当然没有,因为此时是关中断的!在linux/init/main.c中main(){...mem_init(main_memory_start,memory_end);sched_init();...
sti();//开启中断
move_to_user_mode();if(!fork()){init();}for(;;)pause();}在include/asm/system.h中#definesti()__asm__(“sti”::)在linux/boot/head.s中lss_stack_start,%esp该代码的特权级0,此时中断不改变栈在linux/kernel/sched.c中longuser_stack[PAGE_SIZE>>2];struct{long*a;shortb;}stack_start={&user_stack[PAGE_SIZE>>2],0x10};选择符进程0的堆栈move_to_user_mode();页目录表4个页表其他IDTGDT核心模块进程0结构*task[]其他部分user_stack…在linux/kernel/sched.c中staticuniontask_unioninit_task={INIT_TASK,};structtask_struct*task[NR_TASKS]={&(init_tsak.task),};longuser_stack[PAGE_SIZE>>2];指针esp在include/asm/system.h中#definemove_to_user_mode()__asm__(“movl%%esp,%%eax\n\t”“pushl$0x17\n\t”“pushl%%eax\n\t”“pushfl\n\t”“pushl$0x0f\n\t”“pushl$1f\n\t”“iret\n”“1:\tmovl$0x17,%%eax\n\t”“movw%%ax,%%ds\n\t”“movw%%ax,%%es\n\t”“movw%%ax,%%fs\n\t”“movw%%ax,%%gs”:::”ax”)栈段ssmove_to_user_mode()产生的栈页目录表4个页表其他IDTGDT核心模块进程0结构*task[]其他部分user_stack…原SS原ESP原EFLAGS原CS原EIPss=0x17=0b10111描述符2,LDT,RPL=11ss=0x0f=0b01111描述符1,LDT,RPL=11EIP=标号1(1f)//完成ds,es,fs,gs的设置,都是0x17iret指令:特权级3执行(用户模式),user_stack成了用户栈,cs:eip完成ds等设置后执行该函数的下面指令开始fork()在linux/init/main.c中static
inline_syscall0(int,fork)staticinline_syscall0(int,pause)在include/unistd.h中#define_syscall0(type,name)typename(void){long__res;__asm__volatile(“int$0x80”:“=a”(__res):”0”(__NR_##name));if(__res>=0)return(type)__res;展开成为:staticinlineintfork(void){int$0x80:输出__res,输入ax=2}在include/unistd.h中#define__NR_fork2int$0x80应该非常熟悉了…staticinline告诉gcc将fork()代码直接并入main()中,所以此时用户栈还是空的!fork()展开成int$0x80以后…int会调用中断门何为中断门?想一下调用(call,jmp)的参数:call选择符,偏移将选择符、偏移值、参数、DPL等内容放在一起,形成调用门?中断门是调用门的一种!int指令怎么工作?中断向量(0x80)选择符偏移值IDT中断处理中断门描述符段基址GDT或LDTint$0x80段选择符处理函数入口点偏移04处理函数入口点偏移PDPL01110中断门类型中断门描述符在linux/boot/head.s中setup_idt:leaignore_int,%edxmovl$0x00080000,%eaxmovw%dx,%axmovw$0x8E00,%dxlea_idt,%edimov$256,%ecxrp_sidt:movl%eax,(%edi)movl%edx,4(%edi)addl$8,%edidec%ecxjnerp_sidtlidtidt_descr
idt_descr:.word256*8-1.long_idt.align3_idt:.fill258,8,0_gdt:页目录表4个页表其他IDTGDTIDTRint$0x80真正完成的工作在linux/init/main.c中
trap_init();sched_init();在linux/kernel/traps.c中
voidtrap_init(void){set_trap_gate(14,&page_fault);没找着!在linux/kernel/sched.c中void
sched_init(void){set_system_gate(0x80,&system_call);}在include/asm/system.h中#defineset_system_gate(n,addr)_set_gate(&idt[n],15,3,addr)DPL=3!#define_set_gate(gate_addr,type,dpl,addr)__asm__(“movw%%dx,%%ax\n\t”“movw%0,%%dx\n\t”“movl%%eax,%1\n\t”“movl%%edx,%2”:“i”((short)(0x8000+(dpl<<13)+(type<<8))),”o”(*((char*)(gate_addr))),”o”(*(4+(char*)(gate_addr))),”d”((char*)(addr)),”a”(0x00080000))段选择符0x8,DPL=0!fork()展开成int$0x80后需用核心栈调用fork()前,代码在用户态(特权级3)执行此时cpl=3CPL(3)中断门DPL(3)目的段DPL(0)检查CPL中断门DPLCPL目的段DPL由于cpl=3,而目的段的DPL=0,所以是不同特权级之间的调用(到0级),会引起栈切换进程0结构*task[]其他部分user_stack…task_struct原ss原esp内核栈…TR进程0tss段开始system_call进程0内核栈原ss原esp原eflags原cs原eipINT指令在linux/kernel/system_call.s中_system_call:push%dspush%espush%fspushl%edxpushl%ecxpushl%ebxmovl$0x10,%edxmov%dx,%dsmov%dx,%esmovl$0x17,%edxmov%dx,%fscall_sys_call_table(,%eax,4)pushl%eax系统调用返回值!在include/linux/sys.h中fn_ptrsys_call_table[]={sys_setup,sys_exit,sys_fork,...};在include/linux/sched.h中typedefint(*fn_ptr)();sys_call_table+4*eax开始sys_fork()在linux/kernel/system_call.s中.globl_system_call,_sys_fork_sys_fork:call_find_empty_processtestl%eax,%eaxjs1fpush%gspushl%esipushl%edipushl%ebppushl%eaxcall_copy_processaddl$20,%esp1:ret在linux/kernel/fork.c中intcopy_process(intnr,longebp,...){structtask_struct*p;p=(structtask_struct*)
get_free_page*();task[nr]=p;p->tss.esp0=PAGE_SIZE+(long)p;p->tss.ss0=0x10;
p->tss.eip=eip;
p->tss.ldt=_LDT(nr);
copy_mem(nr,p);set_tss_desc(gdt+(nr<<1)+FIRST_TSS_ENTRY,&(p->tss));set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&(p->ldt));父子进程同一位置执行!如何区分父子进程?在linux/kernel/fork.c中intcopy_process(intnr,longebp,...){p->tss.esp0=PAGE_SIZE+(long)p;p->tss.eip=eip;p->tss.eax=0;p->pid=last_pid;...p->state=TASK_RUNNING;returnlast_pid;子进程fork()返回0!父进程fork()返回此值!在linux/kernel/fork.c中
longlast_pid=0;intfind_empty_process(void){inti;repeat:if((++last_pid)<0)last_pid=1;for(i=0;i<NR_TASKS;i++)if(task[i]&&task[i]->pid==last_pid)gotorepeat;for(i=1;i<NR_TASKS;i++)if(!task[i])returni;return-1;}子进程对内存的需求:get_free_page在linux/mm/memory.c中#defineLOW_MEM0x100000//220(1M)#definePAGING_MEMORY(15*1024*1024)//15M#definePAGING_PAGES(PAGING_MEMORY>>12)//页面数staticunsignedcharmem_map[PAGING_PAGES]={0,};unsignedlongget_free_page(void){registerunsignedlong_resasm(“ax”);__asm__(“std;repne;scasb\n\t”“jne1f\n\t”“movb$1,1(%%edi)\n\t”“sall$12,%%ecx\n\t”“addl%2,%%ecx\n\t”“movl%%ecx,%%edx\n\t”“movl$1024,%%ecx\n\t”“leal4092(%%edx),%%edi\n\t”“rep;stosl\n\t”“movl%%edx,%%eax\n”“1:”:“=a”(_res):“”(0),"i”(LOW_MEM),“c”(PAGING_PAGES),“D”(mem_map+PAGIG_PAGES-1):“di”,“cx”,“dx”);return_res;}Linux物理内存的分配内核模块0end640K1M显存和BIOSROM主内存区16M在linux/init/main.c中
mem_init(main_memory_start,memeory_end);memory_end=(1<<20)+(EXT_MEM_K<<10)if(memory_end>16*1024*1024)
memory_end>16*1024*1024;main_memory_start=1*1024*1024;setup.s获取的硬件参数!在linux/mm/memory.c中voidmem_init(longstart_mem,longend_mem){inti;for(i=0;i<PAGING_PAGES;i++)mem_map[i]=USED;//设为100i=MAP_NR(start_mem);end_mem-=start_mem;end_mem>>=12;while(end_mem-->0)mem_map[i++]=0;}子进程对内存的需求:copy_mem在linux/kernel/fork.c中intcopy_mem(intnr,task_struct*p){unsignedlongold_data_base,new_data_base,data_limit;data_limit=get_limit(0x17);old_data_base=get_base(current->ldt[2]);new_data_base=nr*0x4000000;//64M*nrset_base(p->ldt[1],new_data_base);set_base(p->ldt[2],new_data_base);copy_page_tables(old_data_base,new_data_base,data_limit);}Linux早期版本代码、数据段是一个!在include/linux/sched.h中
#define
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 安全生产总监法律责任警示讲解
- 车间安全竞赛方案讲解
- 拉链项目可行性研究报告
- 公路安全知识竞赛讲解
- 2026年中国建筑科学研究院秋招面试题及答案
- 专项05-命题(练习)
- 万有引力与航天 -2025年新高二物理暑假复习(人教版)
- 口语交际+综合实践-部编版三年级语文下册期末专项训练(含解析)
- 浙江金华市永康市2025-2026学年人教PEP版第二学期三年级期末素养评价英语试题
- 救护队自身伤亡分析
- 尼得科电机(大连)扩建项目环境影响评价报告表
- 小学语文口语训练案例分析范文
- 感恩教师节主题班会
- 人教版二年级全册《体育与健康》全套课件
- GJB3243A-2021电子元器件表面安装要求
- 第一单元第一课我们走在大路上(课件)-初中美术湘美版七年级上册
- 海湾-gst-qkp01控制器说明书fas ver
- 【部编人教版】道德与法治二年级上册全册完整课件
- 《汽车文化》中职配套教学课件
- 有机光化学之光催化剂参与的反应课件
- 全国中小学生转学申请表(样表)
评论
0/150
提交评论