震惊!这个操作系统的应用加载只需要“毫秒级”耗时

震惊!这个操作系统的应用加载只需要“毫秒级”耗时

1、背景

AliOS ThingAliOS家族旗下面向IoT领域的、高可伸缩的物联网操作系统,AliOS Thing v3.2[1]以后的版本提供了内核和应用程序分离的功能,内核和应用分别运行在不同的虚拟地址空间,即使应用程序出现问题也不会影响到内核的运行。内核和应用程序的隔离,不仅可以达到安全的目的,还可以有效降低应用开发的成本,并且应用程序以标准的ELF (Executable and Linkable Format)[2]文件存在,系统需要运行哪一个应用程序,只需要加载该应用程序的ELF文件即可。

本文主要介绍AliOS Things应用程序加载如何做到“毫秒级”耗时。

震惊!这个操作系统的应用加载只需要“毫秒级”耗时

图1-1 AliOS Things

2、ELF 文件格式

    ELF文件格式提供了两种不同的视角,在汇编器和链接器看来,ELF文件是由Section Header Table描述的一系列Section的集合,而执行一个ELF文件时,在加载器Loader看来它是由Program Header Table描述的一系列Segment的集合。

震惊!这个操作系统的应用加载只需要“毫秒级”耗时

图2-1 不同视角看ELF

    左边是从汇编器和链接器的视角来看这个文件,开头的ELF Header描述了体系结构和操作系统等基本信息,并指出Section Header TableProgram Header Table在文件中的什么位置,Program Header Table在汇编和链接过程中没有用到,所以是可有可的,Section Header Table中保存了所有Section的描述信息。右边是从加载器的视角来看这个文件,开头是ELF HeaderProgram Header Table中保存了所有Segment的描述信息,Section Header Table在加载过程中没有用到,所以是可有可无的。注意Section Header TableProgram Header Table并不是一定要位于文件开头和结尾的,其位置由ELF Header指出,上图这么画只是为了清晰。

    我们在汇编程序中用.section声明的Section会成为目标文件中的Section,此外汇编器还会自动添加一些Section(比如符号表),Segment是指在程序运行时加载到内存的具有相同属性的区域,由一个或多个Section组成,比如有两个Section都要求加载到内存后可读可写,就属于同一个Segment。有些Section只对汇编器和链接器有意义,在运行时用不到,也不需要加载到内存,那么就不属于任何Segment。目标文件需要链接器做进一步处理,所以一定有Section Header Table;可执行文件需要加载运行,所以一定有Program Header Table;而共享库既要加载运行,又要在加载时做动态链接,所以既有Section Header Table又有Program Header Table

3、AliOS Things应用程序ELF格式

震惊!这个操作系统的应用加载只需要“毫秒级”耗时

图3-1 AliOS Things ELF

    目前的AliOS Things应用程序ELF文件暂不支持类似于.so文件的动态加载和地址重定位,ELF文件一旦编译生成之后,该ELF加载之后运行的虚拟地址就固定(由链接脚本确定)下来了,如图3-1中的Program Header中的LOAD对应的虚拟地址起始地址是0x10000000,相较于整个ELF文件的偏移量是0x010000

4、ELF文件加载方式思考

以前的传统加载方式,分成个步骤,如图4-1所示:

  1. 打开ELF文件,并把整个ELF读取到内存,然后解析ELF的格式,找到需要加载的LOAD Program Header;
  2. 一次申请所有的内存,并完成该ELF虚拟地址空间映射到对应的物理内存,需要的内存大小为app_text_end - app_text_start;
  3. ELF文件中.text,ARM.exidx,.ctors,.rodata,.ARM.extab,.ARM.extab.text.u,.data,.got,.FSymTab部分搬运到 虚拟地址范围app_text_start ~ app_text_end对应的存储区,同时把虚拟地址范围app_zero_start ~ app_zero_end对应的存储区全部清零。
  4. 创建进程执行该ELF的代码;

震惊!这个操作系统的应用加载只需要“毫秒级”耗时

图4-1 传统加载方式示意

通过上述步骤可以发现如下2个问题:

  1. 无论是否需要把整个ELF文件都全部加载,加载器都首先把全部文件都读取到内存,导致加载的耗时较长;真实的情况可能只需要加载ELF文件的一部分内容应用程序就可以运行了;
  2. 无论整个虚拟地址空间(app_text_start ~ app_zero_end)是否都需要映射到物理内存,整个虚拟都一次全部映射,导致不必要的内存浪费;地址空间,到内存打开ELF文件,并把整个ELF读取到内存,然后解析ELF的格式,找到需要加载的LOAD Program Header;

上述2个方面也是我们新的加载器(分段快速加载引擎)重点优化的两个方面;

5、分段快速加载引擎

震惊!这个操作系统的应用加载只需要“毫秒级”耗时

图5-1 AliOS Things分段加载引擎

  • ELF预先读取程序入口,后续按需缺页中断加载;
  • 虚拟地址空间预先映射一部分,后续按需缺页中断映射,预先映射的部分可以根据缺页中断统计信息调整,提高加载的速度;

震惊!这个操作系统的应用加载只需要“毫秒级”耗时

图5-2 AliOS Things示意图

    基于IP Camera客户的应用程序测试发现,整个虚拟地址空间只需要映射1/4部分程序就可以正常运行,整个ELF文件只需要读取ELF HeaderProgram Header Table获取LOAD segment字段描述信息,根据这些信息设置好缺页中断即可运行,应用程序完全运行起来,只需要加载和映射大概1/4内容,借助于分段快速加载引擎(bengine_dload)加载2M Bytes大小的ELF文件的速度可以从500 ms优化到80 ms左右部分,内存开销从3.5 MB (2MB code + 1.5MB BSS)降低到1.25 MB (256KB code + 1MB bss)分段快速加载引擎可以对应用程序的加载速度提高6倍以上,内存开销降低2.8

 

开发者支持

如需更多技术支持,可加入钉钉开发者群,或者关注微信公众号。

震惊!这个操作系统的应用加载只需要“毫秒级”耗时

更多技术与解决方案介绍,请访问HaaS官方网站https://haas.iot.aliyun.com

 

上一篇:linux编程查看某个系统调用、库函数所属的头文件


下一篇:ELF文件格式第三讲,节头(section header)