嵌入式Linux体系小型化技能
发布日期:2011-05-08
先容了Linux在嵌入式范畴中的应用和宿主机、目标机开辟模式,过细地给出了精简内核的实现进程。阐发了glibc体系库和ELF文件格局的结讨论此中的共享库裁剪技能的原理,提出并实现了一种库裁剪方案。
关键词 嵌入式;Linux;小型化
一、 概述
嵌入式Linux一样通常是指对标准Linux发行版本举行小型化裁剪处理惩罚处罚之后,得当于特定嵌入式应用场合的专用Linux利用体系。嵌入式体系通常是资源受限的体系,无论是处理惩罚处罚器谋略本领还是RAM或其他存储器容量都比较“小”。因此,怎样创建一个小型化的Linux作为利用体系开辟成为起首须要思量的标题。嵌入式Linux体系中遍及采取三层布局:内核层紧张是Linux内核和模块;调用接口层因此glibc库为主的体系库;应用层是根据用户需求筹划的应用步调。为了实现资源的高利用率,后两层都以ELF文件情势存在,在运行进程中对外部结果代码动态加载。
一样通常来说,创建交错平台开辟环境是举行嵌入式软件开辟的第一步。宿主机与目标机硬件平台的异构(处理惩罚处罚器体系布局差别)是采取交错开辟的源头头底子因。别的,由于资源有限,直接在嵌入式体系的硬件平台上开辟软件不方便、以致不大概。因此,通常采取Host/Target开辟模式,如表l。
宿主机(Host)
目标机(Target)
硬件
PC 大概变乱站,此中x86CPU占上风
嵌入式体系硬件,处理惩罚处罚器多样化(x86,ARM,PowerPC,MIPS,68K等)
软件
Windows、Linux等桌面利用体系,丰富的集成开辟环境(如WindRiver 的Tornado)
软件资源有限,开辟阶段通常从宿主机下载
|
宿主机(Host) |
目标机(Target) |
硬件 |
PC 大概变乱站,此中x86CPU占上风 |
嵌入式体系硬件,处理惩罚处罚器多样化(x86,ARM,PowerPC,MIPS,68K等) |
软件 |
Windows、Linux等桌面利用体系,丰富的集成开辟环境(如WindRiver 的Tornado) |
软件资源有限,开辟阶段通常从宿主机下载 |
表1 交错平台发环境的特点
交错平台开辟环境包括交错编译器、交错调试器和体系仿真器,比如嵌入式Linux开辟通常用的GNU东西链。开辟者须要根据目标平台来选择切合的GNU交错编译器,然后在宿主机上面重新编译内核和其他软件,如许得到的目标代码才华拿到目标机上面运行。这个进程相称繁琐且容易堕落。宿主机和目标机一样通常通过以太网大概串口连接。如今,天下上出现了数以百计的嵌入式Linux开辟操持和发行版本,比如:ETLinux,LPR,μC-Linux,ThinLinux等开开端代码的项目,如表2所示。
名称
特点
ETLinux
筹划用于在小型财产谋略机,尤其足PC/104模块上运行
Linux
Router
Project
LPR 的目标是用于路由器、接入办事器、瘦办事器等网络没备和嵌入式体系,可以摆设在一张软盘上。雷同的项目另有Linux On A Floppy(LOAF)
μC-Linux
在没有MMU 的体系L运行的Linux。同前支持Motorola DragonBall (M68EZ328), M68328,M68EN322, ColdFire, QUICC, ARM7TDMI,MC68EN302,Axis ETRAX,Inte]i960,PRISMA,Atari 68k等微处理惩罚处罚器
ThinLinux
一个为嵌入式和特定应用制作的Linux发行版,运行在Intel和PC兼容硬件上
名称 |
特点 |
ETLinux |
筹划用于在小型财产谋略机,尤其足PC/104模块上运行 |
Linux
Router
Project |
LPR 的目标是用于路由器、接入办事器、瘦办事器等网络没备和嵌入式体系,可以摆设在一张软盘上。雷同的项目另有Linux On A Floppy(LOAF) |
μC-Linux |
在没有MMU 的体系L运行的Linux。同前支持Motorola DragonBall (M68EZ328), M68328,M68EN322, ColdFire, QUICC, ARM7TDMI,MC68EN302,Axis ETRAX,Inte]i960,PRISMA,Atari 68k等微处理惩罚处罚器 |
ThinLinux |
一个为嵌入式和特定应用制作的Linux发行版,运行在Intel和PC兼容硬件上 |
表2 几种开放源代码的嵌入式Linux发行版
别的,另有:Coventive XLinux,LineoEmbedix,LynuxWorks BlueCat,MontaVista Linux等贸易公司的发行版。同时,针对及时环境,有RT-Linux、RTAI等及时扩展。比年来,越来越多的目标体系选择了性价比不绝进步的x86处理惩罚处罚器和成熟的PC架构作为硬件平台。LinuxDevices.com网站举行的观察表现,嵌入式体系开辟者在已往2年和将来2年选择x86处理惩罚处罚器作为目标平台的比例分别为3l%和35%,高居首位。
敷衍宿主机和目标机都是PC兼容平台的开辟者来说,除了相沿上述模式之外,有更大抵的创建小型化Linux体系的要领:以一个惯例的Linux发行版为底子,编译内核、复制所需的文件,并利用初始化RAM盘(initrd:INITial Ram Disk)机制创建根文件体系,就可以快速实现一个小型化Linux体系。
二、 小型化技能
Linux已经越来越广泛地应用于种种嵌入式配置中。但是一样通常的Linux发行版都非常巨大,很难用于只有有限存储空间的嵌入式配置。以是我们必须对Linux体系举行裁剪。Linux体系大抵有以下4种紧张的裁剪技能,利用这些技能可以有效地减小体系的尺寸且不会影响体系的性能。① 删除冗余文件。一样通常的Linux发行版中都包括很多资助文档、资助步调、配置文件和数据模板,在嵌入式体系中这些文件都是不须要的,完全可以删除。以致连配置文件中的大量表明也都可以被去失。② 共享库裁剪。嵌入式体系的应用步调是有限的,共享库中就大概有很多永世不会被用到的冗余代码,这些代码就可以被删除。③ 采取具有同样结果的调换软件包。Linux上有很多具有相似结果的软件包,可以选择此中占存储空间较小的软件包并其移植到嵌入式配置上,用来代替原来占空间较大那些的软件包。④ 修改源码。包括重新配置、编译软件包,去失不须要的结果;增长软件的模块性,从而有利于进步裁剪屈从;重新配置内核,去失不须要的驱动和模块。
1、精简内核
与传统嵌入式利用体系的微内核(Micro-kerne1)体系布局差别,Linux内核采取的是团体式布局(Monolithic),整个内核是一个单独的、非常大的步调。其长处是可以大概使体系的各个部分直接雷同,有效地紧缩任务之间的切换时间,进步体系相应速率。缺点也是明显的,即内核尺寸比较大,由于Linux内核不但包括如任务调理、内存办理、克制处理惩罚处罚等底子的利用体系结果,同时还包括文件体系、网络协议、没备驱动步调等结果。
Linux内核是高度模块化、可配置的,通过配置使内核具有差别的结果,从而减小内核的大小。比喻,Linux支持的文件体系种类很多,包括ext2、ext3、FAT、Reiserfs、JFS等。可以根据实际环境选择所需的文件体系,比如仅仅把ext2文件体系编译进内核。编译内核的紧张步调如下(“#”代表下令提示符):
# cd/usr/src/1inux-2.4
# make menuconfig
# make dep;make clean;make bzlmage
编译告成的内核文件为arch/i386/boot/bzlmage。细致要领参考内核源代码包中的README文件。为了进一步增长机动性、减小内核尺寸,Linux还提供了可加载内核模块机制,内核中的很多结果可以编译为模块,在内核运行时动态加载,而不是直接编译进内核。然而在嵌入式Linux体系中更方向于根据须要编译一个独立的内核,较少利用模块机制。如许得到的内核通常在几百kB以致1MB左右,相对传统的嵌入式利用体系内核来说是比较大的(比如包括文件体系和网络支持的VxWorks内核约莫250kB)。在举行内核配置时,开辟者要比较相识各结果模块之间的依赖干系,不然有大概导致编译失败。而在VxWorks内核的配置进程中,要是粉碎了依赖干系,有比较明白的指示,从而克制这种错误。
2、共享库裁剪
在小型化技能中,共享库裁剪容易用软件实现,做成主动裁剪东西,结果最明显。下面重点先容共享库小型化技能,共享库小型化的底子头脑是通过提取和解析体系库内目标文件、标记的依赖干系,通过对这些依赖布局干系模型举行干系演算,根据应用步调中的标记信息,在库目标文件一级实现体系库的小型化.实现上分为四步:a、确定待调函数集。在ELF文件内部,存在一个Elf32-Sym 数组布局的标记表,用于内部标记定义和外部标记引用,通过对这个标记表的阐发可以将ELF应用步调中待调标记(体系函数)抽取出来,从而创建一个应用步调-待调函数标记的多对多干系。b、确定体系库函数与目标文件的映射干系。体系库逻辑上分成:库、目标文件、标记三个层次,库和目标文件都是ELF格局,通过对库的映像文件*_pic.a和每个目标文件中的标记表阐发得到库。目标文件的定义干系、目标文件-标记定义干系和目标文件-标记调用干系。c、确定体系库目标文件之间的相互依赖干系。通过对步调b中干系干系的干系演算得到目标文件-目标文件的完全依赖干系。d、天生小型化体系库。通过映射用步调-待调标记表和目标文件-目标文件依赖表的干系演算得到待调函数所依赖的目标文件聚集,将它们举行重新链接即可得到最小化的库文件。
2.1、共享库裁剪技能的原理
共享库中生存着预先编译好的目标代码,一样通常是被应用步调重复利用的公用代码。在Linux体系中,应用步调与库之间可以静态链接或动态链接。静态链接时,链接器从库中选取应用步调须要的代码,然后复制到天生的可实行文件中。显然,当静态库被多个步调利用时,磁盘上、内存中都是多份冗余拷贝。动态链接时,链接器并不真的把库代码复制到可实行文件中;仅当可实行文件运行时,加载器才查抄该库是否已经被别的可实行文件加载进内存,要是内存中不存在才从磁盘上加载该库。如许多个应用步调就可以共享库中的代码的同一份拷贝,节流了存储空间。这也是嵌入式Linux体系利用共享库的紧张缘故因由。
当利用静态链接库时,链接器会主动地只把库中被利用的模块链接到可实行文件中。但是这种要领没有效在共享库中,紧张是由于在应用步调实行之前链接器并不知道应用步调终极用到了库中的哪些部分。因此要对共享库举行裁剪必须先阐发动态链接的原理。
共享库和可实行文件中都有多少个标记表,此中定义了一些外部标记,分为导出(export)标记和导入(import)标记这两种。导出标记是指在该文件中定义但可以被别的文件利用的标记,一样通常是可以由别的文件调用的函数;导入标记是指被该文件利用了但并没有定义的标记,一样通常是被该文件调用的函数,并且导入标记一样通常指明白定义该标记的共享库。加载器在加载可实行文件或共享库之前会先遍历它的每个导入标记,查抄该标记的干系代码是否已在内存中,不然先查找并加载定义该标记的共享库。由于嵌入式Linux体系中的应用步调和共享库一样通常都是确定的,共享库中就大概存在永世不会被别的文件调用到的导出标记,将这些标记的相应代码从共享库中删除不会影响到体系的正常运行。
现有裁剪技能都因此上述原理实现的。下面则细致阐发它的实现要领。
2.2、ELF文件标记提取
ELF格局是UNIX实行室作为应用步调二进制接口而开辟和颁发的,ELF是如今广泛应用于Linux体系中的一种文件格局。
2.2.1、 ELF文件进程映像加载
ELF文件开头部分是一个ELF Header布局,它包括两个指针,分别指向两个数组布局:Program header table和Section headertable,Program header table中的数组元素对文件内部的可实行代码段举行定位;Section header table中的数组元素生存干系重定位和动态链接信息.装载器通过控制这两类数组实现进程映像的加载。
2.2.2、 ELF文件的标记表和重定位进程
ELF文件的Section header table中有一个典范为SHT_DYNSYM的Section,该Section记录了创建进程映像所须要的全部标记。
a、标记值确定和标记定位,ELF文件中字符串ection(.shstrtab)用于生存全部字符串,ELF头通过e_shstrndx域生存节头名字字符串表(.shstrtab)的节索引。ELF文件中标记名字域值是.shstrtab节的一个字符索引:Syrnbol布局中St_name映射相应的字符串表一个索引,在相应的字符串表中映射其标记值,St_value映射两类差别地点:敷衍文件内部定义标记,映射该标记内容的文件内部相对地点;敷衍外部调用标记,映射待调标记的地点(已阐发)或重定位表中的一个入口(未阐发)。St_info生存标记的典范和相应的属性。
b、被调标记重定位。标记表中,STT-SEC-TION映射重定位入口信息表。重定位入口以数组的情势存在于ELF文件中,此中的R_offset生存着应用于重定位活动的地点,而R_addend映射一个偏移用于谋略要存储于重定位域中的值。R_info中给出受重定位影响的标记索引和重定位应用的典范。比喻:当典范为R_386_JMP_SLOT时,标记值就映射一个.plt(进程连接表)入口的位置.
c、外部标记装载。敷衍外部标记代码的装载,装载器通过lazy MODE装载要领将外部标记代码加载到进程映像中:初次调用外部标记通过PLT[0]中的装载代码和PLT[1]中出栈参数将待调标记代码加载到.got表中;以后对此待调标记的调用通过映射.got表入口举行控制传输。
2.2.3、 ELF文件标记提取实现
对每个参加动态链接的共享目标文件来说,其步调头表(Program header table)有一个典范为PT_DYNAMIC的入口元素。该入口所指向的段.dynamic section是一个Elf32_Dyn的布局数组.Elf32_Dyn布局中有一个属性标记d_tag和一个连合布局d_un,d_tag控制d_un中的表明。数组中下标为DT_SYMTAB的入口指向标记表。通过对标记表、重定位表、进程连接表、全局进程表的干系控制布局举行阐发,完成文件定义标记和待调标记的疏散提取,算法如下:
symtab=.dynamic[DT_SYMTAB]->d_un. d->ptr
∥根据DT_SYMTAB找到标记表的地点
for(int i=0;symtab[i]!=NULL;i++)
∥对标记表中全部入口举行扫描
{swith((symtab[i] ->st_info)>>4) ∥根据标记典范举行利用
}…
case STB_WEAK:
case STB_GLOBOL:/*对全局性的和弱标记可用于外部文件调用*/
if(映射入口指向进程连接表入口)/*要是其指向的地点为.plt入口则在创建进程映像的时间须要重定位*/
loadrequest(symtab[i]->st_name);/*重定位并将该标记的名字在字符表找到relocate(symtab[i]->st_value)相应的值,并将其名字放人相应的干系表*/
else
loadprovide(symtab[i]->st_name);/*要是是内部定义,那么此标记名是供别的的应用步调调用*/
… }}
ELF文件中定义标记和待调标记由其st_value所指的目标举行区分:映射于.plt表的待调标记须要重定位;映射于内部标记,要是是弱典范(WEAK)或全局典范(GL0BOL)则用于别的文件调用。通过对ELF格局中的待调标记的提取,创建起应用步调和标记的依赖干系。
2.3、嵌入式体系小型化结果与阐发
对各个表举行连接得到应用步调依赖的目标文件聚集。将聚会合的目标文件无重复记录并重新连接从而得到最小化库。前后库中各数据相比见表3。小型化后,体系库内各构成部明白显淘汰,库被缩减近50%。敷衍日益巨大的嵌入式体系中的应用步调,根据库文件内部的依赖干系,在其底子上映射用步调举行优化淘汰,对一样通常性应用可以使体系库减小40%~50%。
小型化前
小型化后
目标文件(个)
1183
544
标记(个)
9118
4861
显性依赖(条)
3819
1887
间接依赖(条)
120273
55425
库大小
1242480byte
685032byte
小型化前 |
小型化后 |
目标文件(个) |
1183 |
544 |
标记(个) |
9118 |
4861 |
显性依赖(条) |
3819 |
1887 |
间接依赖(条) |
120273 |
55425 |
库大小 |
1242480byte |
685032byte |
表3 体系库小型化前后比较
三、 结束语
比年来嵌入式Linux技能敏捷生长,种种贸易和开放源码的Linux发行版为差别的硬件平台、差别的应用环境提供了多种选择。Linux的文件体系原形上非常的巨大。布局一个嵌入式的Linux文件体系是一个很巨大的进程。怎样让文件体系在包管沉寂的条件下精简得更紧凑、运行得更有屈从,是须要深入探索的一个课题。分外是,共享库裁剪技能能将库中大部分冗余代码裁剪失,但恳求库的源码编写比较典范,差别体系布局须要有差别的处理惩罚处罚等。但终究库裁剪范畴才生长不久,还不是很成熟。颠末对该技能永劫间的测试,信托我们可以大概补充它的不敷,使它可以大概在嵌入式Linux范畴广泛利用。通过以上要领,我们布局了一个精简的嵌入式版本的Linux文件体系,它使得内核在体系只管即便精简的环境下可以大概运行起来.并餍足产品和体系各方面的恳求。