第二章引导和初始化_第1页
第二章引导和初始化_第2页
第二章引导和初始化_第3页
第二章引导和初始化_第4页
第二章引导和初始化_第5页
已阅读5页,还剩51页未读 继续免费阅读

下载本文档

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

文档简介

第二章导和初始AT&T汇编语言格Linux系统所采用的汇编语言遵循AT&T汇编语言语法,该语法与In 汇编语言语法稍有不同:1、指令操作数的顺序是先源后目的,与 moww%bx,%ax //movax,bxxorl%eax,%eax //xoreax,eaxmovw$1,%ax //movax,1movbX, //movah,byteptrmovwX, //movax,wordptrmovlX, //moveax, //movsx,短到长 //movzx,短到长 补这里,S是代表源操作数长度的后缀,Dmovswl%ax, //movsxecx,(AX(EAXc(EDX:EX,EDXlcallfarljmp //jmpfarlret //retfar6、操作码的前缀(rep)要单独写一行,不要和它修饰的指令(movsb、stosb) disp(base,index, [base+index*scale+movl4(%ebp), //moveax,addl(%eax,%eax,4),%ecx //addecx,[eax+eax*4]movb$4,%fs:(%eax) //movfs:eax,4movl_array(,%eax,4), //moveax,[4*eax+movw_array(%ebx,%eax,4),%cx//movcx,[ebx+4*eax+带上后缀,b表示向前,f表示向后。orw%bx,%bxjz1fmovlmovl%eax,%cr3 /*setthepagetablepointer..*/movl%cr0,%eaxorl movl%eax,%cr0 /*..andsetpaging(PG)bit*/jmp1f /*flushtheprefetch-queue*/movljmp /*makesureeipisrelocated9、实模式下的语法与 10asmvolatileasm:::registers-inputs每个参数前加一个引号括起来的标志,告诉编译器把该参数装入到何处。可用的gaax/eaxb“ccx/ecxddx/edxDdi/ediS“qarip=+&%#*0~9goutputs==g(0~9,“axmemory “inti=0,j=1,asmvolatile("pushl%%eax\nmovl%1,%%eax\naddl%2,%%eax\nmovl%%eax,%0\npopl%%eax":"=g":"g"(i),"g":"ax", //k=i+开机过Table2-1.32-BitIn ArchitectureProcessorStatesFollowingPower-up,Reset,orINITP6FamilyPentiumHHHHHH HHHSelector=F000HBase=FFFF0000HLimit=AR=Present,R/W,Selector=F000HBase=FFFF0000HLimit=AR=Present,R/W,Selector=F000HBase=FFFF0000HLimit=AR=Present,R/W,SS,DS,ES,FS,GSSelector=0000HBase= Limit=AR=Present,R/W,Selector=0000HBase= Limit=AR=Present,R/W,Selector=0000HBase= Limit=AR=Present,R/W,000 HHHBase Limit=AR=Present,Base Limit=AR=Present,Base Limit=AR=Present,Selector=0000HBase= Limit=AR=Present,Selector=0000HBase= Limit=AR=Present,Selector=0000HBase= Limit=AR=Present, HHHHHHPoweruporReset:0HINIT:UnchangedPoweruporReset:Not CodeCache,控制寄存器CR0的值为 (PE=0,PG=0FFFF0000H,界限为FFFFH(64K大小EIP0000FFF0H可编地址FFFFFH仅16个字节。该处是驻留在ROM中的BIOS的地址,处理器从此处开始执行指令。因此最先获得机器控制权的是BIOS。BIOS完成对整个机器系统的检测,并将有关系统配置的基本信息记录在内存的BIOS数据区,然后,从引导盘(软盘、硬盘、光驱等)上将一个引导扇区(如GDT、IDT、页、页表等。这些数据结构必须在切换前后建好。真正的切换非常简单,用MOV指令向控制寄存器CR0中写入一个数,将它的PE标志置1即可。切换过程4、紧接着MOVCR0指令的应是一条FARJMPFARCALL指令,通常是9、开中断。STI指令可以打开可硬中断,做必要的硬件操作打开NMI中断。注:第3、4步之间指令,将可能产生不可预料的错误。内核的组真正的Linux内核是被打包压缩的可执行代码,因此在它的前面必须附加一段没有压缩的解压缩代码。利用该段解压缩代码展开压缩的Linux内核映象。但这两部分又必须由(SetupLinux引导扇区movax,#BOOTSEG/*0x07C0*/movds,axmovax,#INITSEG/*0x9000*/moves,axmovcx,#256 /*256个字,即512*/subsi,sisubdi,dijmpigo,INITSEG/*0x9000go20x9400012go:movdi,#0x4000-12movds,axmovss,ax !putstackatINITSEG:0x4000-12.movsp,di xor !resetxordl,dlint0x13xordx, !drive0,headmov !sector2,trackmovbx,#0x0200 !address=512,inINITSEGmovah,#0x02 !service2,nrofsectorsmoval,setup_sects!扇区数(assumeallonhead0,track0)int0x13 !readitjnc !ok-push !dumperrorcallprint_nl !CR+LFmovbp,spcallprint_hex !printthenumberinds:sp,thatisaxpopaxjmpload_setup将操作系统内核读入到处。内核大小不超过(7F000在到F000,离引导程序(Bootsect,90000)还有4K的距离。400Ksread:.word0!sectorsreadofcurrenttrackhead:.word0!currentheadtrack:.word0!current

moval,setup_sectsincalmov !now,wehaveread5sectors:1bootsect+4movtest jne !esmustbeat64kBxor !bxisstartingaddresswithin

movsubcmpax,syssize!haveweloadedallyet?jbeok1_readmovax,sectorssubax,sreadmovcx,axshlcx,#9addcx,bxjncok2_readjeok2_readxorax,axsubax,bxshr !readaxsectorscanfillupthecurrentcallmovcx,ax !thesectorsreadbyeread_trackaddax,sreadcmpax,sectorsjneok3_readmovax,#1subax,headjneok4_readinctrackmovhead,axxorax,axmovsread,axshlcx,#9addbx,cxjncrp_readmovax,esaddah,#0x10moves,axxorbx,bxjmprp_read !readatrackpertimemovax,#0xe2e!loading...message2e=bx,!saveforerrorsp,!popjmpi !SETUPSEG=此后,引导程序已经完成了其使命,它的这块内存区域将被Setup4个扇区的SetupSetup程序的结尾是0xAA555A5A,检查该标志是否存在就可知道它是否完整。如果不完整,剩余的部分肯定在movax,cs !aka#SETUPSEGsubax,#DELTA_INITSEG !aka#INITSEGmovds,axxormov !getsetupsectsfrombootsub !LILOloads4sectorsofshlbx,#8 !converttowords(bx*256)movcx,bxshr converttosegment(字节数/16addbx,#SYSSEGsegcsmov newstartpointofsysseg(Setup中的变量!Moverestofsetupcode/datatomovdi,#2048 !foursectorsloadedbyLILOsubsi,simovax,cs !aka#SETUPSEGmoves,axmovax,#SYSSEGmovds,ax !DS:SIES:DI,字数在cxmovah,#0x88int0x15mov !AXisthenumberofkilobytesofextendsmovxorbx,bx !clearbxint0x16获得有关显示器的所有信息,即添一个大小为64字节的structscreen_info数据 (90002 0x9008(hd0(NMI !nointerruptsallowedmoval,#0x80 !disableNMIforthebootupsequenceout#0x70,al10001000 !loadidtwith !loadgdtwithwhatever !idt !idt !gdtlimit=2048,256GDT !gdtbase= 0,0,0,0! 0,0,0,0! 0xFFFF!4Gb-(0x100000*0x1000= 0x0000!base 0x9A00!code 0x00CF!granularity=4096,386(+5thnibbleof 0xFFFF!4Gb-(0x100000*0x1000= 0x0000!base 0x9200!data 0x00CF!granularity=4096,386(+5thnibbleof08(4K(4Kmov !protectedmode(PE)setPEbitofCR0to1,thenintoprotectedmodewax !Thisisit!(把AX的低4位放到CR0的低4位)jmp !Enterprotectedxor !Flagtoindicatea , !KERNEL_CS的值是该描述符定义一个代码段,它的基址为0指令寄存器EIP的值为1000,所以下一条要执行的指令的线性地址为0 movl$(KERNEL_DS),%eaxmovl%ax,%dsmovl%ax,%esmovl%ax,%fsmovllss#defineSTACK_SIZE(4096)longuser_stack[STACK_SIZE];struct{long*a;shortb;}stack_start={&user_stack[STACK_SIZE]

KERNEL_DS执行完lssSS18,ESP&user_stack[STACK_SIZEcallSYMBOL_NAME( orl%eax,%eaxjnzxorlljmp$(KERNEL_CS),内核真正的内核从100000(1M)设置 、页 定义在101000到101FFF处,共1024个页 项,描述1024个页表。 .long

USER_PGD_PTRSis0x300=768.long/*default:255entries,ff

/*default:767entries,2ff.fillKERNEL_PGD_PTRS- /*KERNEL_PGD_PTRSis0x100=256程序使用;C到FFFFFFFF为内核空间,由内核使用。编译程序将内核代码定位在C 处,即内核程序在系统代码段中的偏移量从 开始。相应地,页也分为两部分:页表的0到0x2FF为用户页部分,占0x300项(即768个页项页表的0x300到0x3FF为内核页部分,占0x100个(即256个页项。目前,只定义一个页表,用户页目录的第项和内核页的第一项都指向该页表其余定义为0因为内核的段基址为0,偏移量从0xC+0x100000开始,所以得到的线性地址的页目页表定义在102000到102FFF处,共1024个页表项,定义从0到4M之间的1024个 例如:内核逻辑地址0xC对应的线性地址是0xC;该线性地址的页部分为0x300,因此它所对应的页表在0x为0x105,所以它对应的页从物理地址0x为0x200,因此该线性地址对应的物理地址为0x。movlmovl%eax,%cr3 /*setthepagetablepointer..*/movl%cr0,%eaxorl$0x ,%eax/*设置控制寄存器CR0的PG位,以启动分页机制*/movl%eax,%cr0 /*..andsetpaging(PG)bit*/jmp /*flushtheprefetch-queuemovljmp /*makesureeipisrelocated/*Setupthestackpointerlss.long.long这里init_task_union是一个长度为8192的.data.init_tasksection,其中但因为一个任务结构的长度小于1K,所以剩余的7K多还可以用做栈。即init_task_unionlss指令执行以后,SS=KERNEL_DS,ESP指向init_task_union的结尾。uniontask_union{structtask_structtask;unsignedlonguniontask_unionattribute((section(".data.init_task")))={INIT_TASK将bsssection0xorlmovl$SYMBOL_NAME(bss_start),%edimovl$SYMBOL_NAME(_end),%ecxsubl%edi,%ecx重设IDTstructdesc_struct{unsignedlongstructdesc_structattribute((section(".data.idt")))={{0,0},因此可以保证IDT的开始地址是4096的倍数。这样做的目的是为了加快对IDT的速度。leamovl$(KERNEL_CS<</*thedefaultgatedescriptorisedx,eaxmovw /*selector=0x0010=csmovw$0x8E00,%dx /*interruptgate-dpl=0,present*/leaSYMBOL_NAME(idt_table),%edimov$256,%ecxmovl%eax,(%edi)movl%edx,4(%edi)addl$8,%edidec%ecxjnerp_sidt条指令在EDX中的DPL=0E(中断门。序,该程序什么也不做,只是显示信息“Unknowninterrupt息移到内核地址C处。该页前半部分是系统参数,后半部分是命令行参数(提structcpuinfo_x86u8 /*CPUfamilyu8 /*CPUvendoru8u8char /*Itdoesn'ton386'scharhlt_works_ok;/*Problemsonsome486Dx4'sandold386's*/charhard_math;charint /*umsupportedCPUIDlevel,-1=nou32charx86_vendor_id[16];/*GenuineIn*/charx86_model_id[64];int/*inKB-validforCPUSwhichsupportthiscallintfdiv_bug;intf00f_bug;intunsignedlongloops_per_sec;unsignedlong*pgd_quick;unsignedlong*pte_quick;unsignedlongpgtable_cache_sz;movb /*clearthe(TaskSwitched)TSflaginCR0 /*InitializeFPUwithoutcheckingforpendingunmaskedfloating-pointexceptions.*/fstsw%ax /*StoreFPUstatuswordinAXregisteraftercheckingforpendingunmaskedfloating-pointexceptions.cmpb$0,%alje1fmovl%cr0,%eax /*nocoprocessor:havetosetbits*/xorl$4,%eax /*setEM*/movl%eax,%cr01:movb.byte0xDB,0xE4 /*fsetpmfor287,ignoredby387*/(INITNULLnotused0x10kernel 0x18kernel0x23 0x2b notusednotused*TheAPMsegmentshavebytegranularityandtheir*andlimitsaresetatrun.quad0x0040920000000000/*0x40APMsetupforbadBIOS's.quad /*0x48APM code.quad /*0x50APMCS16code(16bit).quad0x0040920000000000/*0x58APM data.fill /*spaceforLDT'sandTSS'setc选择符1018选择符232B这4个段都是从0到4G。内核段的级为0,用户段的级为3。以下有4个APM(高级电源管理)段。GDT64K,可以放下819212放下8180个描述符。每个任务占两个描述符,因此系统中最多可以定义4090个任务或进程。NR_TASK的定义一般在512到4090之间。将GDT的址、长度界限装入GDTR中IDT表的位置、大小、内容都没有变化,将IDTIDTRLDT没有定义,在LDTR0lgdtgdt_descrlidtljmp$(1:movl$(KERNEL_DS),%eax#reloadallthesegmentregistersmovl%ax,%ds #afterchanginggdt.movl%ax,%esmovl%ax,%fsmovllssstack_start,%esp #Loadprocessorstackxorl%eax,%eaxlldt #gcc2wantsthedirectionflagclearedat

calljmp #mainshouldneverreturnhere,(Init1setup_arch(在;memory_end=(1<<20)+(EXT_MEM_K<<10);/*1M+Extendedmem*/memory_end&=PAGE_MASK; /*MASK=FFFFF000*/memory_start=(unsignedlong)&_end;其中_end是内核程序的代码的结束位置,也就是可用内存的开始位置(3G以上调整为该值。到此为止,memory_end的值仍然是内存实际大小,将其调整为内存的终止memory_end+=从memory_start到memory_end。为DMA、时钟、FPU等申请I/O空间。request_region(0x80,0x10,"dmapagereg");包括该设备I/OI/O设#defineIOTABLE_SIZEtypedefstructresource_entry_t{u_longfrom,num;constcharstructresource_entry_t}staticresource_entry_tiolist={0,0,"",NULL};staticresource_entry_tiotable[IOTABLE_SIZE];在Setup程序中只设置了前4M的页 到memory_end所对应的 把页的第00,使得用户不能再使用该页。实际上,第0个物理页设置从0xC到memory_end所对应的页和页表。根据机器性能的CPU 、 、 、当然,为了使用4MCR4PGE如果机器不支持4M页,那么只能采用页 了。因为此时第0x300处的页 、第x301:301ptaddr|0x67、 、x 、 写相应的页项。这些特殊的地址页建在0xFFFFF000以前,但又紧接着0xFFFFF000。在start_mem(紧接着上面建的正常的页表0x67放到相应的页中。一般来说,一个local_flush_tlb该函数的功能是:使TLB中除全局页表项外的所有内容均变为无效。这样以 unsignedlongasmvolatile("movl%%cr3,%0\n\tmovl%0,%%cr3":"=r":typedefstructpage/*thesemustbefirst(areahandling)*/structpage*next;structpage*prev;structinode*inode;unsignedlongoffset;structpage*next_hash;atomic_tcount;unsignedlongflags;/*atomicflags,somepossiblyupdatedasynchronously*/structwait_queue*wait;structpage**pprev_hash;structbuffer_head*buffers;}系统中共有MAP_NR(end_mem)=(end_mem-PAGE_OFFSET)>>PAGE_SHIFT个物理页,因此也需要MAP_NR(end_mem)个mem_map_t数据结构。在start_mem处为每个物理页分配一个mem_map_t数据结构,并填上相应的值,如count、flags(PG_DMA|PG_等同时调整start_mem这些连续的mem_map_t数据结构构成了一个数组:mem_map_t*mem_map。所以,目前mem_map数组中有MAP_NR(end_mem)个元素,每个元素都是一个mem_map_t数据结构,以 _area数组。_area数组是一组空闲内存块的队列,分别表struct_area_struct{structpage*next;structpage*prev;unsignedint*struct 其中的map域是一个位图_area数组的每一项都有一个位图用来反映与其大小相当的物理页的使用情况,每一个内存块对应位图中的一位。页数的1/2,_area[2]的位图长度是物理页数的1/4,……。此处为_area数组的每一个元素分配一个内存物理页的映象位图。所有的位图都分配在start_mem处。同时调整start_mem。mem_map_t(structpageInitdata(初始化程序数据区Inittext(初始化程序代码第0x300页3、trap_init(在系统中共有256个中断,其中前32个由处理器使用,其余224个由用户使用(irq31创建一个调用门描述符,其选择符是KERNEL_CS,偏移量是子程序lcall7,在IDT017 /*int3-5canbecalledfrom /*sameastrap_gateexcept述符的段选择符都是KERNEL_CS,偏移量是各中断处理程序的地址。DPL偏移量是system_call。将该门填入IDT的0x80处。这个门是Linux的系统软中断的,应用程序通指令INT80使用Linux系统提供的服务。inux系统提供的所有系统服务程序的地址都放在sys_call_tble中,个址占4字节,中最多可放256个服务子程序的 地。当用通过INT80调系统务时,其EA寄存器放上要求的 ,即该服务程序在表sys_calltable中的索引。系统在IDT的0x80处找到偏移量是systm_call的陷阱门,通过它进入systemcall子程序在systm_call保各寄存的值一必要的检而后根据EAX的值,在系统调用表(syscall_tale)中找到服务子程序的地址;转入服务程序。call创建初始任务的TSS段描述符,将其填入GDT的第一个TSS描述符处(选择符为12*8=0x60对一个任务或进程的管理需要许多信息,并且需要将所有任务或进程的这些TSSLinu(TSSTSStaskstruct个TSS段的数据结构表示。描述初始任务的task_structSection(即data.init_task)中,该Section的前部是描述初始任务的初始任务的TSS段描述符就描述该任务的thread_struct数据结构:其址是初始任务的tss域的开始地址,其长度为235字节,其级为0,类型为&00&LinuxTSSstructthread_structunsignedshortback_link,blh;unsignedlong unsignedshort ss0,ss0h;unsignedlong unsignedshort ss1,ss1h;unsignedlong unsignedshort ss2,ss2h;unsignedlong unsigned unsignedlong unsignedlong unsignedlong unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned

unsigned trace,unsignedlong unsignedlong unsignedlong cr2,trap_no,error_code; /*debugregisters debugreg[8];/*Hardwaredebuggingregisters/*floatingpointinfo*/unioni387_union /*virtual86modeinfostructvm86_struct*vm86_info;unsignedlong unsigned v86flags,v86mask,v86mode,unioni387_union{ /*80387*/structi387_hard_structhard;structi387_soft_structtypedefstruct{ /**/unsignedlongseg;}structtask_struct{/*thesearehardcoded-don'ttouchvolatilelong /*-1unrunnable,0runnable,>0unsignedlongflags; /*perprocessflags,definedbelow*/intsigpending;mm_segment_taddr_limit;/*threadaddress0-0xBFFFFFFFforuser-0-0xFFFFFFFFforkernel-structexec_*exec_;longneed_resched;/*variousfields*/longcounter;longpriority;cycles_tavg_slice;/*SMPandrunqueuestate*/inthas_cpu;intintint /*Lockdepth.Wecancontextswitchin*outofholdingasyscallkernellock...structtask_struct*next_task,*prev_task;structtask_struct*next_run,*prev_run;

/*taskstatestructlinux_binfmt*binfmt;intexit_code,exit_signal;int /*Thesignalsentwhentheparent/*???unsignedlong intdumpable:1;intdid_exec:1;pid_tpid;pid_tpgrp;pid_ttty_old_pgrp;pid_tsession;/*booleanvalueforsessiongroupleader*/intleader;/*pointersto(original)parentprocess,youngestchild,sibling,oldersibling, (p->fathercanwithp->p_pptr-structtask_struct*p_opptr,*p_pptr,*p_cptr,*p_ysptr,/*PIDhashtablelinkage.*/structtask_struct*pidhash_next;structtask_struct**pidhash_pprev;/*Pointertotask[]arraylinkage.*/structtask_struct**tarray_ptr;structwait_queue*wait_chldexit;/*forwait4()*/structsemaphore*vfork_sem; /*forvfork()*/unsignedlong,rt_priority;unsignedlongit_real_value,it_prof_value,it_virt_value;unsignedlongit_real_incr,it_prof_incr,it_virt_incr;structtimer_listreal_timer;structtmstimes;unsignedlongstart_time;longper_cpu_utime[NR_CPUS],/*mmfaultandswapinfo:thiscanarguablybeseen*eithermm-specificorthread-specificunsignedlongmin_flt,maj_flt,nswap,cmin_flt,cmaj_flt,cnswap;intswappable:1;/*processcredentialsuid_tuid,euid,suid,fsuid;gid_tgid,egid,sgid,fsgid;intngroups; cap_effective,cap_inheritable,cap_permitted;structuser_struct*user;/*limitsstructrlimitrlim[RLIM_NLIMITS];unsignedshortused_math;char/*filesysteminfo*/intlink_count;structtty_struct /*NULLifnotty/*ipcstuffstructsem_undo*semundo;structsem_queue*semslee;/*tssforthistask*/structthread_structtss;/*filesysteminformation*/structfs_struct*fs;/*openfileinformation*/structfiles_struct/*memorymanagementinfo*/structmm_struct*mm;/*signalhandlersspinlock_tsigmask_lock;/*Protectssignalandblocked*/structsignal_struct*sig;sigset_tsignal,structsignal_queue*sigqueue,**sigqueue_tail;unsignedlongsas_ss_sp;size_t其中的tss结构就是该任务或进程的TSS段。一个初始任务或进程的定义#define/*stateetc{\\/*counter\/*SMP0,0,0,-\binfmt pid /*proclinks*/ /*pidhash NULL, /*tarray /*chldwait*/NULL, /*timeout /*timer {NULL,NULL,0,0,it_real_fn /*utime /*perCPUtimes*/{0,},{0, /*flt /*swp /*processcredentials /*uidetc /*supplgrps*/0, /*caps /*user /*rlimits /*math /*comm /*fsinfo /*ipc NULL, /*tss /*fs /*files /*mm /*signals SPIN_LOCK_UNLOCKED, {{0}},{{0}},NULL,&init_task.sigqueue,0, }为初始任务定义一个LDT段描述符,将其填入GDT(紧接着初始任务的TSS段&01000&&7,DPL=02(LDT始任务的LDT段描述符装入LDTR。4init_IRQ(在设置IDT表的第32–255IRQLinuxirq_desc[]描述所有的IRQIRQirq_desc_tirq_desc[NR_IRQS]={[0...NR_IRQS-1]={0,&no_irq_type,其中:NR_IRQS224(256–32typedefstruct{unsignedint /*IRQstatus-IRQ_INPROGRESS,structhw_interrupt_type*handler;/*handle/enable/disablestructirqaction /*IRQactionlistunsignedint /*Disabledepthfornestedirq}structhw_interrupt_type{constchar*typename;void(*startup)(unsignedintirq);void(*shutdown)(unsignedintvoid(*handle)(unsignedintirq,structpt_regs*regs);void(*enable)(unsignedintirq);void(*disable)(unsignedintstructirqactionvoid(*handler)(int,void*,structpt_regs*);unsignedlongflags;unsignedlongmask;constchar*name;void*dev_id;structirqaction初始化irq_desc数组。将它每一项的status设为IRQ_DISABLED,action设为0,depth设为0。前16项的handler设为&i8259A_irq_type,用于处理来自8259的中断;后面各项的handler设为&no_irq_type。0到structhw_interrupt_typei8259A_irq_type={hw_interrupt_typeno_irq_type为每个IRQ创建一个中断门,并将其填入IDT中,填入的位置为IRQ+32。所有pushl$ret_from_intrjmpdo_IRQpushl0x01-jmpcommon_interruptpushl0x02-jmppushl0x0e-jmpcommon_interruptpushl0x0f-jmp 号查irq_desc数组,找到一个类型为irq_desc_t的元素,进而得到它的structhw_interrupt_typehandleirq_desc16个元素的hw_interrupt_type域是i8259A_irq_type,后面各元素的hw_interrupt_type为两个8259中断控制器申请I/O空间。 /*inkernel/resource.c*/100Hz和irq_desc5、sched_init(kernel/sched.c) 表 初始化Bottom_half基表bh_baseTIMER_BHTQUEUE_BH6time_init(在arch/i386/kernel/time.c)重设irq_descIRQ0,在其上挂上一个setup_x86_irq(0,irq0structirqactionirq0{timer_interrupt,SA_INTERRUPT,0,"timer",NULL,NULL因此当发生时钟中断时,处理程序timer_interrupt7parse_options(在分析内核令行参数。在内核启动时,可以给它一些命令行参数,这些参数可以请8console_init(在drivers/char/tty_io.c)9init_modules(在kernel/module.c)kernel_module.nsyms

stopksymtab

start10、kmem_cache_init(在mm/slab.c)检查结构kmem_slab_tkmem_cache_t设置cache_cache11、开中断12、mem_init(在在paging_init中已创建了用于内存管理的mem_map和_area数组,此处初始化mem_map数组,并将所有可用物理内存的mem_map_t数据结构连接到_area清除empty_zero_page105000在mem_mapmem_map_t构中,有一个flags域,用于表示一个物理页的状态。Flags域是一个32位位012正被345PG_6789该页用做在paging_init中,每个结构都已经设置了PG_DMA和PG_位将0x1000到0x9f000之间的物理(640K常规内存的PG_位清除,将start_mem(PAGE_OFFSET)end_mem(可用物理内存的终止位置+PAGE_OFFSET)之间的物理页(内核代码及内核数据结构以上的物理内存)的PG_位清除,表示这些页可用。因为DMA只能使用16M以内的物理内存,所以,将所有大于16M的物理页的PG_DMADMA将物理内存中所有保留页的count1清除其flagsPG_referenced将该页插 将该页 _area[]->map中的相应位置1 初始化以后的自由内存情况如下(640K大小=512大小=256大小=512大小=256大小=128大小=64大小=32大小=16大小=8大小=4大小=264--32--64--32--128--16--144--8--152--4--156--2、大小=1 大小=111表中摘下为两个大小相等的伙伴将小伙伴加入到_area中相应的链表(下一个。如大伙伴仍比所要的内存大,则再将其为两个伙伴、将13、kmem_cache_sizes_init(在mm/slab.c)LinuxUNIX步完善。Linux采用的内核内存管理算法是SUN公司在其Solaris2.5中首先、目slab在Slab中,对内核内存的管理以cache为基础,一个cache管理一组大小固定的内cache有一到多个slab内核内存管理系统通过cache在它的slab中分配和回收内存块,并通过slab与页管理器交互。分配一块内存,只是简单地从一个cache的slab中找到一的slab队列中。当一个slab中的所有块都变成空闲时,就可以将该slab还给页管理器cachestructkmem_cache_s /*firstslab objsconstantflags#ofobjsperslabkeptatzerofirstslabinchainlastslabinchainunsignedlongunsignedlongunsigneddynamicflagsorderofpgsperslab(2^n)void(*c_ctor)(void*,kmem_cache_t*,unsignedlong);/**/void(*c_dtor)(void*,kmem_cache_t*,unsignedlong);/**/unsignedlongc_align;/*alignmentofobjs*/ /*cachecolouringrange*/ /*cachecolouring*/unsignedlong constchar structkmem_cache_s*c_nextp; unsignedlong unsignedlong unsignedlong unsignedlong unsignedlong #endif/*SLAB_STATStypedefstructkmem_cache_s一个structkmem_cache_s数据结构描述,显然,这些structkmem_cache_s数据结构也的任务之一就是分配并初始化一个用于structkmem_cache_s数据结构管理的slab。statickmem_cache_tcache_cache=/*p,flags kmem_slab_end(&cache_cache),/*offset,num /*c_magic,c_inuse*/SLAB_C_MAGIC,/*firstp,lastp /*spinlock /*growing /*dflags /*org_size,gfp 0,/*ctor,dtor,align*/NULL,NULL,/*colour /*colour_next /*failures /*name /*nextp /*index kmem_cache_initcache_cachec_offset(structkmem_cache_sstructkmem_bufctl_s的大小规约为32对齐,减去structkmem_bufctl_s的大小,约为96–4个字节;c_num(–structkmem_slab_s)(structkmem_cache_s的大小+structkmem_bufctl_s的大小),即一页中可以放下多少个structkmem_cache_s+structkmem_bufctl_s结构,约为42;c_colourL1_CACHE_BYTES1;c_colour_next=c_colour。这些设置用于构造cacheslab /*couldmakethislargerfor64bittypedefstructkmem_slab_sstructkmem_bufctl_s*s_p;/*ptrtofirstinactiveobjinstructkmem_bufctl_s*s_index;unsigned unsigned s_inuse;/*numofobjsactiveinslab/*addroffirstobjinslabunsigned }typedefstructkmem_bufctl_sunionstruct *buf_slabp;/*slabforobj }}一个slabkmem_slab_t(小于512)slab,它放在一页的末尾,占用32个字节,Slab中的每一个对象的末尾加一个kmem_bufctl_s数据结构,它实际上是一个指针,占4调用函数kmem_cache_createslab_cache”的cache,用于大对象slab中kmem_slab_t数据结构的分配和回收。cache_slabp=kmem_cache_create("slab_cache",0,SLAB_HWCACHE_ALIGN,NULL,(1.1)函数kmem_cache_createkmem_cache_t*kmem_cache_create(constchar*name,size_tsize,size_toffset,unsignedlongflags,void(*ctor)(void*,kmem_cache_t*,unsignedlong),void(*dtor)(void*,kmem_cache_t*,unsigned在这里size=sizeof(kmem_slab_t32;flags=SLAB_HWCACHE_ALIGN,即 ctor=NULL,dtor=首先做检查,而后调用函数kmem_cache_alloc请求内核内存管理器分(1.1.1)函数kmem_cache_allocstaticinlinevoid*kmem_cache_alloc(kmem_cache_t*cachep,int这里:cachep=&cache_cache,即初始cache;flags=SLAB_KERNEL,即0x15。该函数要从cachep所指的cache的slab中分配一个对象。因为现在cache_cache中没有slabkmem_cache_grow生成一个slab()函数kmem_cache_growstaticintkmem_cache_grow(kmem_cache_t*cachep,int这里:cachep=&cache_cache;flagsSLAB_KERNEL,即0x15;ctor_flags=SLAB_CTOR_CONSTRUCTOR,即0x001;local_flags(flags&SLAB_LEVEL_MASK)0x15;offset=cachep->c_colour_next--;offset*=cachep->c_align,所以,offset=32;unsignedintdma,无初值;要建立slab就要请求页管理器为其分配物理页,因为一个kmem_cache_t数据结构所占用的内存小于512slab是一个小对象slab,只需要分配一objpkmem_getpages(cachepflags,&dma);由此进入函数kmem_getpages。(.1函数kmem_getpagesunsignedint其中:cachep=&cache_cache;flagsSLAB_KERNEL,即0x15;即是函数*dmaflags&SLAB_DMAaddr=(void*) _pages(flags,cachep-由此进入函数 (.1.1)函数 _pages的定义如下unsignedlong _pages(intgfp_mask,unsignedlong这里:gfp_mask=flags,0x15;order=0;该函数所做的工作是:在空闲物理页块队列_area+order中,找一structpage;改变该页所对应的位图,表示该页已经使用;将总空闲页数减1struct(.2)在kmem_getpages中,得到了所要的物理页,将它的首地址(在函数kmem_cache_growobjpslabp=kmem_cache_slabmgmt(cachep,objp+offset,(.1)函数kmem_cache_slabmgmtstaticinlinekmem_slab_tkmem_cache_slabmgmt(kmem_cache_t*cachep,void*objp,int这里:cachep=local_flags=(flags&SLAB_LEVEL_MASK),仍是0x15;在页的末尾建kmem_slab_t32slabp;填写该kmem_slab_t数据结构的下列域:slabp->s_inuse=slabp->s_dma=0;slabp->s_index=NULL;返回该slab()在函数kmem_cache_grow中,物理页上的slab结构已经建立,做以下善slabp->s_dma=让该slabmem_map_tnextcache_cache;让该slab对应的mem_map_t数据结构的prev指针指向slab;设置该slabmem_map_tflagsPG_Slabslabp->s_offset=objpoffset,slabp->s_memobjpkmem_cache_init_objs(cachep,slabp,objp,inlinevoidkmem_cache_init_objs(kmem_cache_t*kmem_slab_t*slabp,void*objp,unsignedlong这里:cachep=slabp=slabp,指向物理页的最后32字节;objp=objp,指向物理页的首地址+区大小;ctor_flagsSLAB_CTOR_CONSTRUCTOR,即该函数将物理页中除开始处的区和结尾处的kmem_slab_t数据结构以外kmem_cache_t(表示一个cache)和一个kmem_bufctl_t(一个指针kmem_bufctl_t数据结构按从小到大的顺序连接成一个链表,让slabs_p域指向该链表的头。()继续kmem_cache_growslabp->s_magic=的善后事宜。(.1)函数kmem_slab_link_end的定义如下:在这里调整4个指针:slabp->s_nextp=kmem_slab_end(cachep);slabp->s_prevp=lastp;即新slabs_nextps_prevpcache_cachec_offsetcachep->c_lastp=slabp;lastp->s_nextp=slabp;即cache_cachec_firstpc_lastpslab。如此以来,新slabcache_cacheslab返回到函数kmem_cache_grow。()继续kmem_cache_growcachep->c_p=slabp;表示cache_cache中第一个有空闲对象的slabcachep->c_failures=0;(1.1.2)继续函数kmem_cache_alloc如果cache_cache的slabp-将slab的第一个空闲对象从它的s_p链表中摘下bufp=slabp->s_slabp->s_p=bufp-bufp->buf_slabpslabp;objp=((void*)bufp)-cachep->c_offset;returnobjp;(1.2)继续函数kmem_cache_create置为一个slab_cache。此时:cachepslabkmem_cache_t计算size大小,因为一个kmem_slab_t占用32个字节,再加上一个kmem_bufctl_t占用4slab3632整,得到每个对象应占用的字节数为64;调用函数kmem_cache_cal_waste计算一个用做slab_cache的物理页将要浪wastage=kmem_cache_cal_waste(cachep->c_gfporder,size,flags,&left_over,&cachep-(1.2.1)函数kmem_cache_cal_wastestaticinlinekmem_cache_cal_waste(unsignedlonggfporder,size_tsize_textra,unsignedlongflags,size_t*left_over,unsignedlong*num)这里:gfpordersize64,一个kmem_slab_textra4,一个kmem_bufctl_tflags=SLAB_HWCACHE_ALIGN,即0x;left_over&left_over,是函数kmem_cache_create的一个变量;num=&cachep->c_num,是新的slab_cache的一个域;该函数算出一个物理页中可以分割的kmem_slab_t对象的个数,并填入c_num域;计算出浪费的字节总数,包括:一个kmem_slab_t数、num个kmem_bufctl_t(1.3)kmem_cache_create填写新cache的下面各域:cachep->c_alignunsignedlong)offset32;cachep->c_colour=(left_over/offset);即1;cachep->c_colour_next=cachep->c_colour;cachep->c_offset(unsignedlong)sizesizeof(kmem_bufctl_t);即60;cachep->c_p=kmem_slab_end(cachep);即c_offset处;cachep->c_firstpkmem_slab_end(cachep);即c_offsetcachep->c_lastp=kmem_slab_end(cachep);即c_offset处;cachep->c_flags=flags;即SLAB_HWCACHE_ALIGN,0x2000;cachep->c_ctor=ctor;即NULL;cachep->c_dtordtor;即cachep->c_magicSLAB_C_MAGIC;即SLAB_C_MAGIC,0x4F17A36D;cachep->c_name=name;即"slab_cache";下面语句将现有的两个cachecachep->c_nextp=cache_cache.c_nextp;cache_cache.c_nextp=cachep;将填写好的cache的执行。上面已经创建了cache_cache的一个slab,其中的每个对象的大小都一样,都可以用做kmem_cache_tslab做了一个slab_cache。现在再从该slab 的几个cache,以支持经常使用的小内存分配。将这些cache与cache_cache和slab_cache小对象,即对象大小为、 的kmem_cache_t数据结构中c_flags为SLAB_HWCACHE_ALIGN;c_num分别为 、、、;c_org_size分别为 、、 c_gfporder为0;c_alignc_colourc_colour_next0;c_index_cachep为NULL;c_offset分别 、 131072的kmem_cache_t数据结构中: c_num分别为、、、、、、、、;c_gfporderc_alignc_colour和c_colour_next为0;c_index_cachepcachesize_32;c_offset分别 cachecache_sizes便地找到分配相应大小内存块的cache。数组cache_sizes的定义如下:typedefstructcache_sizes{ }staticcache_sizes_tcache_sizes[]={{{{{ cache

S_

C_flags=FLAG00 区0区其中:FLAG0FLAG1=FLAG2=SLAB_HWCACHE_ALIGN|SLAB_CS_OFF_SLAB|SLAB_CS_BUFCTL后将其划分成c_num个对象。小对象的slab数据结构和各对象的kmem_bufctl_tslabslab请的物理页的个数由c_gfporder决定。大对象的slab数据结构和它的各对象的kmem_bufctl_tslab中,其中的kmem_bufctl_t是一个数组,由slab数据结构中的s_index指示。因为申请到的物理kmem_bufctl_t,算出其索引,根据该索引即可方便地定位对象在物理页中的13、proc_root_init(Linux/fs/proc/root.c)14、uidcache_init(Linux/kernel/fork.c)kmem_cache_create("uid_cache",sizeof(struct0,SLAB_HWCACHE_ALIGN,NULL,调用函数kmem_cache_create在cache_cache的slab中创建一个名为"uid_cache"的cache。该cache中对象的大小为sizeof(structuser_struct),它主要用于structuser_struct数据结构的动态分配和回收。该数据结构的定义如下:structuser_struct{atomic_tcount;structuser_struct*next,**pprev;unsignedintuid;表中,该#definePIDHASH_SZ(NR_TASKS>>2)#defineUIDHASH_SZ(PIDHASH_SZ>>2)staticstructuser_structfiles_cachep=sizeof(struct0,SLAB_HWCACHE_ALIGN,NULL,调用函数kmem_cache_createcache_cache的slab中创建一个名为"files_cachecache。该cache中对象的大小为sizeof(structfiles_struct),它主要用于structfiles_struct数据结构的动态分配和回收。该数据结构的定义如下:structfiles_struct{atomic_tcount;intmax_fds;structfile**fd; /*currentfdarray*/fd_setclose_on_exec;fd_set16、dcache_init(Linux/fs/dcache.c)dentry_cache=kmem_cache_create("dentry_cache",sizeof(struct0,SLAB_HWCACHE_ALIGN,NULL,kmem_cache_createcache_cache的slab"dentry_cache"cache。该cache中对象的大小为sizeof(structdentry),它主要用于数据结构structdentry的动态分配和回收。Dentry数据结构的定义如下:structdentry{intunsignedintstructinode*d_inode;/*Wherethenamebelongsto-NULLisstructdentry*d_parent; /*parentdirectory*/structdentry*d_mounts; /*mountinformation*/structdentry*d_covers;structlist_headd_hash; /*lookuphashlist*/structlist_headd_lru; /*d_count=0LRUlist*/structlist_headd_child; /*childofparentlist*/structlist_headd_subdirs;/*ourchildren*/structlist_headd_alias; /*inodealiaslist*/structqstrd_name;unsignedlongd_time; /*usedbyd_revalidate*/structdentry_operations*d_op;structsuper_block*d_sb;/*Therootofthedentrytree*/unsignedlongd_reftime; /*lasttimereferenced*/void*d_fsdata; /*fs-specificdata*/unsignedchard_iname[DNAME_INLINE_LEN];/*smallnames*/#define #define (1UL<<structlist_headstructlist_head*next,structlist_head17、vma_init(kmem_cache_createcache_cacheslabcache:第一个cache名叫"vm_area_struct",该cache中每个对象的大小都是sizeof(structvm_area_structstructvm_area_structvm_area_cachep=sizeof(struct0,SLAB_HWCACHE_ALIGN,NULL,structvm_area_structstructmm_struct*vm_mm; /*VMareaparameters*/uns

温馨提示

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

评论

0/150

提交评论