道生一ChinaUnix博客专业IT技术博客.docx

上传人:b****6 文档编号:8435993 上传时间:2023-01-31 格式:DOCX 页数:6 大小:25.65KB
下载 相关 举报
道生一ChinaUnix博客专业IT技术博客.docx_第1页
第1页 / 共6页
道生一ChinaUnix博客专业IT技术博客.docx_第2页
第2页 / 共6页
道生一ChinaUnix博客专业IT技术博客.docx_第3页
第3页 / 共6页
道生一ChinaUnix博客专业IT技术博客.docx_第4页
第4页 / 共6页
道生一ChinaUnix博客专业IT技术博客.docx_第5页
第5页 / 共6页
点击查看更多>>
下载资源
资源描述

道生一ChinaUnix博客专业IT技术博客.docx

《道生一ChinaUnix博客专业IT技术博客.docx》由会员分享,可在线阅读,更多相关《道生一ChinaUnix博客专业IT技术博客.docx(6页珍藏版)》请在冰豆网上搜索。

道生一ChinaUnix博客专业IT技术博客.docx

道生一ChinaUnix博客专业IT技术博客

狗拿耗子道生一———狗拿耗子第十一篇道生一,一生二,二生三,三生万物,linux的启动过程还真有点这个味道。

从kernel的入口stext到process1切换到用户模式执行,其中坎坎坷坷的漫长道路鲜为人知。

如果这条路有一千里,我们先来看看前面五十里到底是什么样子的。

u-boot给kernel传递了一些参数后,跳转到kernel所在的起始地址开始运行。

一般来说kernel会被压缩成zImage格式的压缩包,这个压缩包会自解压。

然后跳转到符号stext处执行,经过一段晦涩的汇编代码后,到达第一个C函数start_kernel(。

本文介绍的正是start_kernel(之前发生的事情。

硬件环境仍然是那块smdk_2410的板子,不过所有代码均在skyeye-1.2.8上运行。

u-boot版本为1.3.4,linux版本为2.6.31,busybox版本为1.15.2,编译工具为eldk-2008.11.24。

本文主要参考网友byte_x所写《armlinux从入口到start_kernel》,正所谓“闻道有先后,术业有专攻”,另“千江水有千江月”,本文的视角主要从分析功能出发。

仍然要感谢网友bytex所做的工作,以及其他朋友们的分享。

1、u-boot的工作rootfs用的是ramfs,依据《buildingembeddedlinuxsystems》用busybox生成initramfs。

用u-boot加载initramfs压缩包到内存,并告诉kernelrootfs的位置。

ramfs与ramdisk是两种不同的fs,前者出道时间晚,比后者先进。

在u-boot的环境变量中的initrd的全称是initramdisk,在下面用initrd=uInitramfs来告诉kernelrootfs的位置,是老瓶装新酒的行为。

1.1mkimage$UBOOT_PATH/tools/mkimage-n'linux-2.6.31'-Aarm-Olinux-Tkernel-Cnone-a0x30008000-e0x30008040-d$KERNEL_PATH/arch/arm/boot/zImage$TFTP_PATH/uImage$UBOOT_PATH/tools/mkimage-n'initramfs'-Aarm-Olinux-Tramdisk-Cgzip-a0x30800000-e0x30800040-d$BASE_PATH/initramfs.cpio.gz$TFTP_PATH/uInitramfszImage是自解压kernel压缩包,u-boot在它前面增加0x40字节生成uImage,用来记录自己需要暂时保存的信息。

同样u-boot在initramfs前面增加了0x40字节。

1.2bootkernelsetenvbootargsconsole=ttySAC0,115200mem=64Minitrd=0x308000401

狗拿耗子tftp0x30008000uImagetftp0x30800000uInitramfsbootm0x300080000x30800000内存布局为:

zzImage被加载到0x30008040;zinitramfs压缩包被加载到0x30800040;zparameterlist(即u-boot的环境变量,格式可参考《bootingarmlinux》)被写到0x30000100。

运行上下文为:

zcpu工作于svc模式,irq与fiq被禁止;zmmu与数据cache被关闭;z指令cache可以打开;(可参考zzzr0为0;r1为machinetype;r2为parameterlist的地址。

parameterlist:

zconsole用ttySAC0;zram大小为64M;zinitramfs压缩包位于0x30800040处。

2、zImage自解压zImage由位置无关的自解压代码,以及压缩后的kernel链接而成。

自解压代码解压kernel压缩包至0x30008000处,最后跳转到该位置运行。

显然该地址对于不同的ARM芯片是不相同的,在arch/arm/mach-s3c2410/Makefile.boot可知smdk_2410的起始物理地址为0x30008000。

zImage的生成与自解压过程挺复杂的,因为与kernel关系不大,这里就只介绍这么多。

3、kernel入口参考链接脚本arch/arm/kernel/vmlinux.lds.S可知,inputsecton*.text.head被放入位于kernel最前面的outputsection.text.head中。

zImage自解压代码最后跳转到0x30008000处执行,即跳转到第一个inputsection*.text.head起始处执行。

另外kernel的程序入口为stext。

...OUTPUT_ARCH(armENTRY(stext...SECTIONS2

狗拿耗子{#ifdefCONFIG_XIP_KERNEL.=XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR;#else.=PAGE_OFFSET+TEXT_OFFSET;#endif.text.head:

{_stext=.;_sinittext=.;*(.text.head}...而第一个inputsection*text.head位于arch/arm/kernel/head.S,可见kernel的程序入口stext正好是该section的起始地址。

....section".text.head","ax".typeENTRY(stextmsrcpsr_c,#PSR_F_BIT|PSR_I_BIT|SVC_MODE@ensuresvcmode@andirqsdisabledmrcp15,0,r9,c0,c0bl__lookup_processor_typemovsr10,r5beq__error_pbl__lookup_machine_typemovsr8,r5beq__error_ablbl...__vet_atags__create_page_tables@getprocessorid@r5=procinfor9=cpuid@invalidprocessor(r5=0?

@yes,error'p'@r5=machinfo@invalidmachine(r5=0?

@yes,error'a'stext,%function4、structproc_info_list该结构体定义于arch/arm/include/asm/Procinf.h,虽然被起名为list却不是list,称之为proc_info更为合理。

structproc_info_list{unsignedintunsignedintunsignedlongunsignedlongunsignedlongcpu_val;cpu_mask;__cpu_mm_mmu_flags;__cpu_io_mmu_flags;__cpu_flush;/*usedbyhead.S*//*usedbyhead.S*//*usedbyhead.S*/3

狗拿耗子constcharconstcharunsignedintconstcharstructprocessor*arch_name;*elf_name;elf_hwcap;*cpu_name;*proc;*user;*cache;structcpu_tlb_fns*tlb;structcpu_user_fnsstructcpu_cache_fns};4.1proc_infoarray显然不同的cpu有着不同的proc_info实例,这些实例分别被定义在不同的section*.proc.info.init中。

链接脚本vmlinux.lds.S将它们组成了一个proc_infoarray,并用符号__proc_info_begin、__proc_info_end分别指向array的起始、结束地址。

OUTPUT_ARCH(armENTRY(stext...SECTIONS{....init:

{...__proc_info_begin=.;*(.proc.info.init__proc_info_end=.;...}/*Initcodeanddata*/4.2arm920t的proc_infoarm920t的proc_info实例化于arch/arm/mm/proc-arm920.S,在本文中我们需要关心它的四个域(field:

zcpu_val与cpu_mask,是该实例的标识;z__cpu_mm_mmu_flags,用于初始化临时内核页表;z__cpu_flush,指向__arm920_setup(,该函数用于复位cache与TLB。

.section".proc.info.init",#alloc,#execinstr.type.long.long__arm920_proc_info,#object0x410092000xff00fff0__arm920_proc_info:

4

狗拿耗子.longPMD_TYPE_SECT|\PMD_SECT_BUFFERABLE|\PMD_SECT_CACHEABLE|\PMD_BIT4|\PMD_SECT_AP_WRITE|\PMD_SECT_AP_READ.longPMD_TYPE_SECT|\PMD_BIT4|\PMD_SECT_AP_WRITE|\PMD_SECT_AP_READb__arm920_setupcpu_arch_namecpu_elf_nameHWCAP_SWP|HWCAP_HALF|HWCAP_THUMBcpu_arm920_namearm920_processor_functionsv4wbi_tlb_fnsv4wb_user_fnsarm920_cache_fnsv4wt_cache_fns__arm920_proc_info,.-__arm920_proc_info.long.long.long.long.long.long.long.long#else.long#endif.size#ifndefCONFIG_CPU_DCACHE_WRITETHROUGH5、structmachine_desc该结构体定义于arch/arm/include/asm/mach/Arch.h。

它与structproc_info_list非常类似,不同的machine(processor对应于920t,machine对应于s3c2410)有着不同的实例,这些实例组成了一个array,链接脚本vmlinux.lds.S用符号__arch_info_begin、__arch_info_end分别指向该array的起始、结束地址。

smdk2410相应的实例定义在arch/arm/mach-s3c2410/Mach-smdk2410.c中。

在本文中,我们只需要关心archnumber即可,它是相应实例的标识。

structmachine_desc{/**Note!

Thefirstfourelementsareused*byassemblercodeinhead.S,head-common.S*/unsignedintunsignedintunsignedintnr;/*architecturenumber*/phys_io;/*startofphysicalio*/io_pg_offst;/*byteoffsetforio*pagetabeentry*/5

狗拿耗子constcharunsignedlongunsignedintunsignedintunsignedintunsignedintunsignedintunsignedintvoid*name;/*architecturename*/*/*/*/*/*/*/*/boot_params;/*taggedlistvideo_start;/*startofvideoRAMvideo_end;/*endofvideoRAM/*neverhaslp0/*neverhaslp1/*neverhaslp2/*softrebootreserve_lp0:

1;reserve_lp1:

1;reserve_lp2:

1;soft_reboot:

1;(*fixup(structmachine_desc*,structtag*,char**,structmeminfo*;voidvoidvoid};(*map_io(void;/*IOmappingfunction(*init_irq(void;*timer;/*systemticktimer*/*/structsys_timer(*init_machine(void;MACHINE_START(SMDK2410,"SMDK2410"/*@TODO:

requestanewidentifierandswitch*toSMDK2410*//*Maintainer:

JonasDietsche*/.phys_io=S3C2410_PA_UART,.io_pg_offst=(((u32S3C24XX_VA_UART>>18&0xfffc,.boot_params=S3C2410_SDRAM_PA+0x100,.map_io=smdk2410_map_io,.init_irq=s3c24xx_init_irq,.init_machine=smdk2410_init,.timerMACHINE_END=&s3c24xx_timer,6、临时内核页表6.1section临时内核页表只用一级分页,页大小为1M,s3c2410称之为section。

一级页表需要4096个表项,每个表项需要4个字节,整个一级页表共占用16k字节。

每个表项最后两个bit为0b10,表示使用一级分页。

6.2几个重要的宏定义zzzPAGE_OFFSET,内核起始线性地址0xc0000000;PHYS_OFFSET,内核起始物理地址0x30000000;TEXT_OFFSET,内核入口偏移0x8000;6

狗拿耗子zzKERNEL_RAM_VADDR,内核入口线性地址0xc0008000;KERNEL_RAM_PADDR,内核入口物理地址0x30008000。

6.3内核页表的位置内核页表位于线性地址0xc0004000、物理地址0x30004000处。

变量swapper_pg_dir记录着它的线性地址,head.S给出了swapper_pg_dir的定义。

这是一个重要的变量,但在本文中不会引用它。

.globlswapper_pg_dir.equswapper_pg_dir,KERNEL_RAM_VADDR-0x40006.4更新内核页表表项设符号pg_tbl指向内核页表起始物理地址(即0x30004000),待映射物理地址为p_addr,线性地址为v_addr,则有:

zzzsection起始物理地址sect_p_addr=p_addr&0xfff00000;待更新表项索引entry_index=(v_addr&0xfff00000>>20;待更新表项起始物理地址为entry_p_addr=pg_tbl+entry_index*4。

表项内容entry_content=sect_p_addr|PROCINFO_MM_MMUFLAGS,其中后者为structproc_info_list的域__cpu_mm_mmu_flags的值。

将长度为四个字节的entry_content写入到entry_p_addr即可完成内核页表表项的更新。

7、cache7.1三种类型的cache假设cache共有32line,每个line包含16bytes。

ram中的数据以line为单位映射到cache中,下文中用ramn表示ram中(16*n到(16*n+15bytes,称之为ram块,假设共包含1024个ram块。

zDirectMappedCache:

ram(32*m+n映射到linen,其中m、n取0、1、2…。

即ram0、32、64映射到line0,ram1、33、65映射到line1,以此类推。

ramN只能映射到某个line中,并与其它(1024/32-1个ram块竞争line(N%32。

FullyAssociativeCache:

ramN可以映射到任何一个line,并与所有其它的ram块竞争。

N-WaySetAssociativeCache:

将所有line分成大小为N的set,即line0、1、…N-1为set0,lineN、N+1、…2N-1为set1,以此类推。

ramN可以映射到某个set,并与其它(1024/32*N-1个ram块竞争同一个set。

zz7

狗拿耗子最后一种类型的cache是前两种的中和产物,令set大小为1,就变成了DirectMappedCache,令set大小为32,就变成了FullyAssociativeCache。

这是从工作效率与工作时间两个方面综合考虑后,设计出来的cache。

7.2cache的回写机制zWritethrough,cpu待写入的arm块已经映射到了cache,则直接写入cache相应的line,并同时写入ram块。

Writeback,cpu待写入的arm块已经映射到了cache,则直接写入cache相应的line,并做上标记。

不是立即写入ram块,而是在一个合适的时候再将数据写入ram块,比如说在相应的line被其它ram块映射时。

z8、使能MMU在使能MMU前流水线按物理地址寻址,在使能MMU生效之后流水线按线性地址寻址,需要通过MMU获得相应的地址或数据。

另外在使能MMU之前的代码必须是“位置无关”(PIC)的,必须按相对地址寻址,即相对于pc寻址;如果按直接地址寻址,则相应地址是链接器重定位后的地址,即线性地址,必然导致流水线运行异常。

另外arm902t采用的是五级流水线,使能MMU指令的下两条指令的取指会发生在使能MMU生效之前。

综合考虑这两点,在使能MMU之前采用“flattranslation”来映射linux内核前1M代码,使其线性地址与物理地址相等,显然使能MMU的代码位于linux内核的前1M之内。

这样流水线按相对地址寻址得到的地址与数据,以及取指得到的指令,均是所期望的。

9、process0process0是kernel的第一个进程,也是所有进程的“祖先”,是它创建了process1,而process1创建了其它所有进程。

直接用《understandingthelinuxkernel》中的原文来介绍吧:

Theancestorofallprocesses,calledprocess0,theidleprocess,or,forhistoricalreasons,theswapperprocess,isakernelthreadcreatedfromscratchduringtheinitializationphaseofLinux(seeAppendixA.Thisancestorprocessusesthefollowingstaticallyallocateddatastructures(datastructuresforallotherprocessesaredynamicallyallocated:

•••Aprocessdescriptorstoredintheinit_taskvariable,whichisinitializedbytheINIT_TASKmacro.Athread_infodescriptorandaKernelModestackstoredintheinit_thread_unionvariableandinitializedbytheINIT_THREAD_INFOmacro.Thefollowingtables,whichtheprocessdescriptorpointsto:

oinit_mm8

狗拿耗子ooooinit_fsinit_filesinit_signalsinit_sighandThetablesareinitialized,respectively,bythefollowingmacros:

ooooo•INIT_MMINIT_FSINIT_FILESINIT_SIGNALSINIT_SIGHANDThemasterkernelPageGlobalDirectorystoredinswapper_pg_dir(seethesection"KernelPageTables"inChapter2.在本文中我们只需要关心两个地方:

z内核

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 党团工作 > 思想汇报心得体会

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1