翻译的比较烂,自己记录下。

 

启动时间最优化

Alexander belloniMichael Opdenacker Free Electrons

 

 

 

简单的进程信息

简单的最优化分为如下方面讲:

原则

如何测量

用户区域

内核

启动 bootloader

笔记:完成样本镜像,测量启动时间,最优化启动脚本,最优化内核

 

原则:

1.       减少启动时间意味着首先要测量启动时间

2.       你需要选择启动和停止的参考点,即启动时间开始到结束的点。

 

 

一些减少启动时间的意见:

1.       最快的代码是代码没有被执行

2.       Boot的一大部分时间是将代码和数据从存储器搬到ram。读取少的代码和数据就会更快。I/O操作是耗时的。

3.       文件系统越大加载时间越长。

4.       所以没有运行的代码会让你的启动时间更长

5.       当然不同的存储器都不一样,一般sd卡要比nand快。

6.       使用gcc编译的时候用-0s的参数会让代码更小,但是代码将失去一些特性,这也行是一个办法。

 

学习开发板的影响

学习如何实现它

 

测量 measuring

1.       最好的仪器是示波器。

2.       测试上电启动时间,这是非常精准的测试方式。

3.       系统启动时写GPIO口和存储器是非常简单的。

4.       一些示波器能够负担得起。

5.       通常你不想用示波器,或者不想冒硬件连接的风险。

6.       通常我们通过串口反馈启动时间信息。必须用一些软件连接。

7.       需要实时串口,可以通过启动进程实现。

8.       限制:不能检测到上电时间POWER ON。但是可以保证启动开始第一阶段时间不变。

9.       串行端口使用USB转换的端口连接。

10.   使用USB转串口会丢掉一些时间精度。

11.   所有开发板都有这个标准的usb串行端口。

来着 Tim Bird  grabserial   这个命令http://elinux.org/Grabserial

一个python脚本加入时间戳信息从串口控制台输出。

按键优势:开始计数非常早bootstrapbootloader

另外一个优势:不用上层调试直接运行在主设备。

缺点:精度不够,不能测量上电时间。

 

 

 

多方面的时间组成

初始化脚本  init scripts

这有多种方法测量这个时间在初始化脚本里面重开始到应用启动。

1.       开启应用的同时尽可能的满足应用必须要的条件。

2.       简化shell脚本

3.       启动应用和init一起或在它之前。

如果你需要比grabserial更详细的描述你可以使用bootchart

你可以使用bootchartdbusybox  CONFIG_BOOTCHARTD=y

启动你的板子在启动命令行加入 init=/sbin/bootchartd

拷贝/var/log/bootlog.tgz从你的目标板到你的主机分析

普通的timechart

Cd bootchart-<version>

Java –jar bootchart.jar bootlog.tgz

Bootchart 网站:http://www.bootchart.org/

 

如果你在初始化项目中用了systemd,你可以使用systemd-analyze看网站

http://www.freedesktop.org/software/systemd/man/systemd-analyze.html

 

启动尽可能快的在所有依赖服务启动后:

1.       依赖服务在init进程,这是一个慢的sysV init脚本。

2.       Init脚本启动用字母命令开始用letter

3.       你可以用低数字在你的应用上。

4.       你可以替换init使用你的应用。

当我们最先启动应用还能怎么快?

 

开始所有的服务使用启动脚本,消除大多数使用/bin/sh

使用mdev代替udevMdevbusybox的一部分。它不是一个守护进程,所以你需要手动加载你的热插拔驱动。

去除掉udev如果你指示需要驱动文件,使用devtmpfs (CONFIG_DEVTMPFS),内核自动管理,使用资源更少。

使用fork/exec系统调用是非常好的。这是因为,使用shell执行调用,所以更慢。

相等的使用echobusybox shell中的结果是一个系统调用

选择 Shells->Standalone shell busybox配置中。这个配置让busybox脚本调用应用在任何时候都可以。

管道是一种返回调用,当然他也是用fork/exec调用的。你可以改进他们或尽量少用他们在脚本中。

减少大小

1.       减少执行文件和库。去除elf段和需要的开发和调试。这个脚本命令已经提供在交叉编译工具BR2_STRIP_strip在创建文件系统的时候。

2.       Superstrip 更深入的去除不用的执行文件。

3.       使用mklibsMklibs程序缩小共享库他包含常规的必须和特别的执行文件。实际使用像一些大的共享库像 OpenGLQT。他们经常工作在没有源码的时候,所以必须小心一些时候裁剪得太多。

 

使用Buildroot生成这个文件系统

使用bootchart测量启动时间

 

C

Glibc

执照:LGPL

C库来自GUN项目

设计性能,标准编译和可移植性能

创建所有在GUN/Linux主系统

当然,积极的维护

合适大小的嵌入式系统。

uClibc

执照:LGPL

轻量级的嵌入式系统C

  高可配置性:许多特征可以失能和使能通过menuconfig配置界面

  智能工作在Linux/uClinux,工作在许多嵌入式架构

  没有stable(牛棚,马厩) ABI,不同于ABI依赖库配置

  重点是大小和性能

  更少的编译时间

 

uClibc2

多种库的介绍

几种库的对比最后uClibc with Thumb-2最小,编译出来的文件最小。

 

一个好的主意是用一个小的initramfs,刚刚足够启动应用的最小需求,启动最终的文件系统使用switch_root

         使用最小的C库文件。uClibc使用它,如果他还没有用在你的文件系统

         使用静态链接应用, BR2_PREFER_STATIC_LIB Buildroot

不要压缩你的initramfs如果你的内核已经压缩了。

 

简化用户空间脚本

应用

允许使用应用和应用子进程跟踪所有系统调用

有用的方法:

1.       知道时间是在哪里消耗了。

2.       例如:很容易知道文件打开open(),文件操作read(),write(),和存储空间申请消耗的时间,而不需要借助源码。

3.       找到最大的时间消耗。

4.       找到不必要的工作在应用和脚本里面,例如:打开同一个文件多次,或是试着打开一个不存在的文件

限制:你不能跟踪init进程。

 

笔记:strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通 过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。

 

Strace不能再目标板上面编译。

相对简单容易的:下载一个准备模式的静态二进制代码到你的目标板,所以你需要他

http://git.free-electrons.com/users/michael-opdenacker/static-binaries/tree/strace

推荐使用命令:

strace –f –tt –o strace.log <program> <arguments>

-f 跟踪子进程

-tt 显示时间戳使用微秒为精度。

两种工作模式:遗产模式和穿孔模式

遗产模式:低精度,使用内核驱动实现

                      CONFIG_OPROFILE

                      用户区工具:opcontroloprofiled

穿孔模式:使用硬件性能计数

                      CONFIG_PERF_EVENTSGONFIG_HW_PERF_EVENTS

                      使用工具operf

使用硬件性能计数

CONFIG_PERF_EVENTSCONFIG_HW_PERF_EVENTS

用户区工具:perf。这个软件是内核源码的一部分,所以必须和内核同步。

使用方法: perf record /my/command

获取这个结果命令: perf report

 

工具优化:

使用一个最优化的工具在你的项目上。这个是你整个项目的基准。

使用gcc版本4.6.3  9.63s

使用gcc版本4.7.3  9.26s

这个结果非常依赖系统文件大小和特性。这里,我们大体的标准时内核特性和文件系统不会占用太多的时间。笔记:不要编译文件系统使用glibc/eglibc而不用uClibc这个启动文件系统大小区别非常大。。。。。。

 

应用代码组使用启动:

 找到启动中启动的功能,例如:使用 –finstrument-functions gcc选项。

 建立一个习惯的连接脚本,重新整理这些功能使用一个调用队列。你能够实现他们各自的功能使用他们各自的块代码通过 –ffunction-sections gcc选项。

特别的应用如大的MTD存储块读取。他们读取块时,停止在读取无用数据之前。

预连接功能:

预连接可以减少时间需求在开始一个执行块里面。

这个经常在android里面使用

这个功能是配置知道的库是必须要预连接的,在启动应用时分配和维护需要的库地址和符号。

小心对安全的影响

代码在 http://people.redhat.com/jakub/prelink/

 

跟踪和描述主应用使用strace

 

内核最优化

想知道那个初始化需要的时间最长,通过initcall_debug配置放到内核启动命令 kernel command line

最好的主意是在kernel配置文件里面增加log配置CONFIG_LOG_BUF_SHIFT。你还需要CONFIG_PRINTK_TIME,CONFIG_KALLSYMS.

首先我们最重要的是减少这个时间而不去除特性。

1.       这个最主要的原理是用内核模式

2.       编译任何启动的时候不需要的东西为模块。

3.       两个好处:内核变得更小加载更快,更少的初始化代码执行

4.       去除一些没有用到的特性:CONFIG_KALLSYMS,CONFIG_DEBUG_FS,CONFIG_BUG

5.       用一些特性设计你的嵌入式系统:CONFIG_SLOB,CONFIG_EMBEDDED

平衡下存储器读取速度和cpu解压的速度,你需要找到不同的压缩方式之前的区别选择最快的压缩方式。

如果你不能编译一个功能成一个模块,试试 deferred_initcalls .你的内核将收缩但是它不能执行相同的初始化。如果你确认你的应用已经准备好,再开始重新初始化之前的调用。网站:

http://elinux.org/Deferred_Initcalls

 

调整命令行

在各自的引导,内核有一个校正延时时间(为这个udelay功能)。这个意思是一个值是一小会的值,你需要测量它一次。在启动日志里找到lpj的值。

现在你可以直接使用这个lpj的参数了。这里可以节约180ms

这个直接加到u-boot里面 cmd lineok了如下:

console=ttyS0,115200 mtdparts=atmel_nand:256k(bootstrap)ro,512k(uboot)ro,256k(env),256k(env_redundant),256k(spare),512k(dtb),6M(kernel)ro,-(rootfs) rootfstype=ubifs ubi.mtd=7 root=ubi0:rootfs rw lpj=1314816

经过测试,有效果。

 

控制台输出实际上需要一部分时间。你一般在产品中不需要它。它可以通过quiet参数来失能在内核命令行里。你可以使用dmesg命令查看获得这些信息。

 

多重处理器提供SMP

SMP在初始化的时候是相当慢的。

UP系统启动得更快一点

你可以试着在你的应用起来以后再热加载其他核。

 

实用的库减少内核启动时间

重新编译内核,选择initramfs

使用initcall_debug去查找消耗时间最长的地方

减少这种模块的数量

调整内核命令行参数。

 

内核:最后几个毫秒

优化最后几个毫秒,你需要去除不必要的功能

CONFIG_PRINTK=n和使用quiet是同样的效果。

试试 CONFIG_CC_OPTIMIZE_FOR_SIZE=y 这将影响系统性能,你需要有一个标准,有一个对比。

试着减少初始化的RAM使用mem参数,更少的RAM被初始化将减少启动时间。

模块的加载,卸载

块层

网络块

USB

去除使用a.out格式

电源管理

CONFIG_SYSFS_DEPRECATED

输入:键盘/mice话筒/触摸屏

CONFIG_LEGACY_PTY_COUNT或者pty.legacy_count内核参数。

 

 

启动时间优化

通常,bootloader的一些功能只有开发的时候需要

1.       多种不同的bootloaders在你的开发板。试试他们

2.       评估这些功能是否都真的需要。是否需要升级功能

3.       去掉启动延时时间。U-boot他们配置为CONFIG_ZERO_BOOTDELAY_CHECK参数。Doc/README.autoboot允许你进入启动shell

4.       也许你可以跳过这个启动AT91的例子:                                http://free-electrons.com/blog/starting-linux-directly-from-at91bootstrap3/

 

 

现在,让我们来限制启动bootloader的一些功能

我们可以使用这个io口来实现返回有这个功能的boot。比如使用gpio_direction_inputgpio_get_value命令在脚本里当开始升级启动解救内核的时候。

笔记:这个内核不能做实际的改变但是我们不能获得精确的时间通过串口当我们选择这个引导内核的方式的时候。

警告:有时候,内核依赖bootloader来初始化硬件,所以要小心当我们移除掉这些功能的时候。

 

 

 

你可以试着使用AT91bootstrap来启动linux内核,这样可以去掉第二阶段启动。但是你将失去一下主要的barebox的优势。它是使用CPU的高速缓存来加载和解压内核的。

启动内核是容易的对于AT91bootstrap3.你只需要配置它成linuxlinux_dt就可以了

make at91sama5d3xeknf_linux_dt_defconfig

make

详细资料看网站:

http://free-electrons.com/blog/at91bootstrap-linux/

 

减少启动时间使用barebox启动

优化barebox

 

硬件初始化

硬件需要时间初始化:

1.       电压稳定,晶振稳定

2.       变稳定需要200ms

3.       对于一个软件工程师,你不能做任何事情在这段时间

4.       你能做的只是测量时间和问硬件工程师是否有改进的可能。然而,这会延长CPU启动时间,这个不可能缩短。

 

替代选择

大部分使用在手机和PC。都不能接受设备被闲置大部分时间或更长的时间。

也叫暂停到磁盘。

使用SONY数字摄像头。

 

 

 

 

 

 

 

 

Logo

更多推荐