你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

如何加快Linux的启动,你知道吗?

[复制链接]
gaosmile 发布时间:2020-3-20 14:31
01工具链/应用程序优化

9 ?" E6 k, V2 q% T- z2 a$ h
导读:嵌入式Linux在应用中往往希望系统能在尽量短的时间内启动,以提高用户体验。而且在有的应用场合,对启动时间具有严格的时间要求,尤其在工业或者医疗器械应用领域。此时如何加快Linux的启动,将成为一个挑战,对于大多数应用开发人员而言,由于Linux系统的复杂性,对于如何提高启动速度,往往无从下手。那么阅读完本文,将获得清晰完整的解决思路。
: K! f9 P1 h% u" s6 \; h
1.降低启动时间的一般思路
  n- S# S2 V) I# @& J& n
在准备降低系统的启动时间时,思路上应建立以下的切入点:
  • 最快的代码是未执行的代码。
  • 引导操作本质上的很大一部分工作实际上是将代码和数据从存储设备加载到RAM。如所需加载内容越少则意味着加载操作越快。
  • 如果根文件系统越大,则安装时间可能会越长。
  • 因此,即使未执行的代码也会延长启动时间。
  • 另外在硬件方案设计时尽量选择读写速度快的存储介质。例如,从SD卡启动实际上比从NAND FLASH启动快。

    ' {3 Q2 d8 M; ]! o- z5 k& v: i
    & t$ ~5 C3 t: c! D8 z/ Z
2.启动时间测量方法
要降低系统的启动时间,则首先需要选择一个可靠的启动时间的测量方法:
  • 在Linux代码中加入对某一个GPIO脚的逻辑电平控制,利用示波器测量GPIO状态。后面将介绍如何在代码中加入对GPIO的控制。
  • 监控串口控制台报文以测量时间,可以使用grabserial。
    " p: t9 c7 d4 A. W

    : h4 B( z, s9 w& \$ E
3. 工具链优化( |4 `8 a/ h) P9 J. u, f8 k# V$ }
3.1 从工具链入手
选择使用合适的工具链,应是第一个入手点,因为所有的运行加载固件都是由工具链编译而成。如果尚未进行其他优化,则更改工具链的好处将更加明显,并且更容易度量。
您可以在工具链中进行以下更改,这可能会影响启动时间,性能和大小:
  • 编译器版本:gcc和binutils的版本,最新版本往往可以具有更好的优化功能。
  • C库:glibc,uClibc,musl。使用uClibc和musl库编译的根文件系统更小
  • 指令集变量:ARM或Thumb2,是否支持硬浮点。
  • 可能会影响代码性能和代码大小(Thumb2编码与ARM相同的指令,但以更紧凑的方式,至少会显着减小大小)。
    ) q  U  G5 l: S. U0 I6 Q  I

    ; }* ]+ I9 c' g: j- B) e
C库在创建工具链时进行了硬编码,可供选择的C库:

( u) {% @4 M7 I
可以对glibc/uclibc-ng /musl进行对比测试:
, j' L9 a. Q# Y# l* Y& I
1.静态编译hello.c程序并比较大小
  • 使用gcc 6.3, armel, musl 1.1.16:           7300   字节
  • 使用gcc 6.3, armel, uclibc-ng 1.0.22 :  67204  字节
  • 使用gcc 6.2, armel, glibc:                    492792 字节

    ( N5 F5 \& j1 R8 B% e/ h9 @, P
2. 静态编译BusyBox 1.26.2并比较大小
  • 使用gcc 6.3, armel, musl 1.1.16:         183348   字节
  • 使用gcc 6.3, armel, uclibc-ng 1.0.22 : 210620   字节
  • 使用gcc 6.2, armel, glibc:                    755088   字节

    * g7 Y' P0 `" M+ ^! C
( z: `' j4 t: B7 R
3.2 指令集选择% C" L* O2 z0 U5 W
编译rootfs进行测试对比:
  • 用gcc 7.4编译,生成ARM代码:

    1 i; p! g* D# }  u+ o
       根文件系统总大小:3.79 MB
  • 用gcc 7.4编译,生成Thumb2代码:
    根文件系统总大小:3.10 MB(-18%)
    性能方面:Thumb2的性能明显改善(大约少于5%,但是从一次运行到另一次运行,测量的执行时间略有变化)。

    ; n0 A$ g2 j9 N5 K

    % }% L1 r; d- Z
4. 应用软件优化
1 r) V/ U0 H9 r" z# O' P
4.1 测量strace
- _- S' I6 o3 }; @1 Y4 S3 Q2 E
strace允许跟踪应用程序及其子级进行的所有系统调用。对于开发非常有用:
  • 了解如何在用户空间上花费时间
  • 例如,轻松查找打开尝试(open()),文件访问(read() /write() )和内存分配(mmap2() )。无需访问源代码即可完成!
  • 寻找耗时最长的开销应用
  • 查找在应用程序和脚本中完成的不必要的工作。例如:多次打开同一文件,或尝试打开不存在的文件。
  • 局限性:您无法跟踪init进程!

    5 g9 ?- L5 P4 ?' H* R& P9 ?* Q

2 n/ O  I$ J# j. Y* _* F
关于strace 参见
http://sourceforge.net/projects/strace/:
  • 在所有GNU / Linux系统上可用,可以由您的交叉编译工具链生成器构建。
  • 更简单的办法:直接拷贝一个现成的静态二进制文件。 参见

    / K- k# U9 ^" j: t
http://github.com/bootlin/static-binaries/tree/master/strace
  • 可以查看进程的操作情况:
    1. 访问文件,分配内存...
    2. 通常足以发现简单的错误。
  • 用法:
    1.strace <命令>(开始一个新进程)
    2.strace -p <pid>(跟踪现有进程)strace -c <command>(统计进程的系统开销时间)
    # V. `2 w" H. ^  I& M# D4 y" |; g

( i' o* k& H+ @: b  Q6 l" b0 _
如查看cat操作:
7 S/ u- |# Z  p/ m

- y" g7 a* \2 J9 s8 @2 n9 \
微信图片_20200320142217.jpg / C) C0 p8 n% o- L& q( K

( y5 X. C6 k' q6 j5 y
又如统计进程的调用时间:

. Q1 h- }! X5 A  h0 J
微信图片_20200320142214.jpg
1 o+ H* ]7 {1 W8 \: r( @

, r6 Z) w- f& g( [% N4 s
4.2 Linux上的性能监测工具oprofile
Oprofile是linux上的性能监测工具:
  • 具有两种工作方式:legacy模式和perf_events模式
  • legacy模式:
    1.精度低,请使用内核驱动程序进行配置
    2.使用CONFIG_OPROFILE进行编译配置
    3.用户空间工具:opcontrol和oprofiled
  • perf_events模式:
    1.使用硬件性能计数器
    2.使用CONFIG_PERF_EVENTS和CONFIG_HW_PERF_EVENTS编译配置
    3.用户空间工具:operf
    4 L- x$ w- e5 D; v( V
; G: D; J4 y8 i' m! [2 R# F
其使用方法:
  • legacy 模式:
    opcontrol --vmlinux=/path/to/vmlinux # optional step
    opcontrol --start
    /my/command
    opcontrol --stop
  • perf_events 模式
    operf --vmlinux=/path/to/vmlinux /my/command
  • 利用opreport获取结果
    , ~% N: I( L, `1 v( r8 l. L& x
7 a3 D3 f7 M! I2 i) ~
例如:
3 Q1 P2 _. X3 {. ~
微信图片_20200320142211.png 4 @. h; t6 Z# S( M4 B
4.3 perf工具
Perf 是内置于Linux 内核源码树中的性能剖析(profiling)工具。它基于事件采样原理,以性能事件为基础,支持针对处理器相关性能指标与操作系统相关性能指标的性能剖析。可用于性能瓶颈的查找与热点代码的定位。linux2.6及后续版本都自带该工具,几乎能够处理所有与性能相关的事件
  • 使用硬件性能计数器
  • 使用CONFIG_PERF_EVENTS和CONFIG_HW_PERF_EVENTS进行配置
  • 用户空间工具:性能。它是内核源代码的一部分,因此始终与您的内核同步。
  • 用法:perf record /my/command
  • 通过以下方式获得结果:perf report

    6 E9 _5 A$ f7 K

0 ~  `8 `( p7 M1 `: |( \: q
例如:

6 R+ H6 }; @! z
微信图片_20200320142207.jpg
; j  T: D* m! m

! P. s# \; i* u
4.4 连接器优化
启动时使用的应用程序组代码:
  • 查找启动期间调用的功能,例如使用
    -finstrument-functions gcc选项。
  • 创建一个自定义的链接描述文件,以按调用顺序重新排列这些函数。可以通过将每个函数放在各自的部分中来实现:
    -ffunction-sections gcc选项。
  • 特别对于具有较大MTD读取块的闪存存储特别有用。因为读取整个读取块后,极有可能读取不必要的数据。
  • 通过如下方法,可以找到有望被优化的地方:
    1.启动一次应用程序并测量其启动时间。
    2.再次启动应用程序并测量其启动时间。由于它的代码应仍在Linux文件缓存中,故其代码加载时间将为零。
  • 从而知道第一次加载应用程序代码(及其库)所花费的时间。链接器优化节省的时间应少于此上限。
  • 然后据此可以决定是否有必要这样对该应用进行链接优化。由于链接优化必须修改应用程序的编译方式,因此此类优化的成本很高。
    + {" Y* s# h; p, K  q2 b

    / e/ y/ o( P3 B
4.4.1 Prelink 预链接工具1 q- W  f0 z3 X
Prelink是Red Hat 开发者 Jakub Jelinek 所设计的工具,正如其名字所示,Prelink利用事先链接代替运行时链接的方法来加速共享库的加载,它不仅可以加快起动速度,还可以减少部分内存开销,是各种Linux架构上用于减少程序加载时间、缩短系统启动时间和加快应用程序启动的很受欢迎的一个工具。
  • 预链接减少了启动可执行文件所需的时间
  • 在Android上广泛使用
  • 必须配置为知道哪些库需要进行预链接,并将为每个可用符号分配一个固定的地址,从而消除了在启动可执行文件时重新定位符号的需要。
  • 请注意安全性,因为可执行代码始终加载在同一地址。
  • 代码以及文档参见
    http://people.redhat.com/jakub/prelink/
  • 支持ARM,但自2013年以来未发布。Buildroot也不支持。但是,x86比较容易实现。
    ! u6 D5 A& }9 g
) h3 X$ F7 z2 n* p) g# U6 ?" T
02文件系统
& ^. T- N& o9 t1 e

* H! N/ o3 F  u( k( E% q. e4 J
1. 文件系统+ d7 {2 c# t6 r2 }8 ^9 k/ j0 v7 A) @
不同的存储介质会采用不同的文件系统:
1)块存储介质 (包括存储卡, eMMC):
  • ext2, ext3,ext4
  • xfs, jfs,reiserfs
  • btrfs
  • f2fs
  • SquashFS

    . W/ {* E: r" [, J3 n/ w

: c5 m  \6 K) N% B5 R/ u
2)Raw 闪存:
  • JFFS2
  • YAFFS2
  • UBIFS
  • ubiblock +SquashFS
    9 U8 I2 e  z/ D
  Z4 ~! @( v$ a/ p* Q
对于块文件系统,特性各异:' A/ f+ m  g7 l9 Z5 a1 T$ Q
  • ext4:最适合较大的分区,良好的读写性能。
  • xfs,jfs,reiserfs:在某些读或写场景中也可能很好。
  • btrfs,f2fs:利用闪存块设备的特性,可以实现最佳的读写性能。
  • SquashFS:对于只读分区,最佳挂载时间和读取性能。非常适合需要只读的根文件系统。

    4 j. {1 Y. E( o/ b
下面分别介绍各文件系统的详细特性。
1.1. JFFS2
用于RAW Flash:
  • 挂载时间取决于文件系统的大小:内核必须在挂载时扫描整个文件系统,以读取属于每个文件的块。
  • 需要使用CONFIG_JFFS2_SUMMARY内核选项将此类信息存储在Flash中。这大大减少了安装时间。
  • ARM基准:对于128 MB分区,从16 s到0.8 s。
  • 与YAFFS2和UBIFS相比,读写性能相当差。

    3 W. }; G8 q! }- r3 z

0 t, b2 H" F  X$ j
1.3 YAFFS2
用于RAW Flash:
  • 良好的安装时间
  • 良好的读写性能
  • 缺点:不压缩,不在主线Linux内核中
    + K/ G' s+ b$ _  f
) }7 R9 t7 [' ]" r
1.4. UBIFS8 q( Y0 e( w1 y. b, {; x
用于RAW Flash:
  • 优势:
    % [9 X) a' c8 j8 r
    " t7 ^6 @) ~7 v* X4 U
    • 良好的读写性能(类似于YAFFS2)
    • 其他优点:更好的磨损均衡(不仅可以在单个分区内,而且可以在整个UBI空间中使用)。
      & R% `: \& d8 Y  R  r
  • 缺点:

    2 ~% Z: ~( {! S7 f$ K
    2 o# Y9 l( J; i: J/ _
    • 不适用于小型分区(元数据开销过多)。请改用JFFS2或JAFFS2。
    • 挂载时间不是很好,因为初始化UBI需要时间(UBI Attach:在引导时或在用户空间中运行ubi_attach)。
    • 由Linux 3.7中引入的UBI Fastmap解决。

      % N! W+ V& [/ c, L" y+ T5 K

! [! ~/ `7 p8 g! e0 L1 i% O3 I
1.5. UBI Fastmap如何工作- V" J$ e' p0 i  o" R
  • UBI 加载:需要通过扫描所有擦除块来读取UBI元数据。时间与存储空间成正比。
  • UBI Fastmap将此类信息存储在几个闪存块中(通常在系统关闭期间在UBI分离时),并在引导时找到该信息。
  • 这样可使UBI附加时间恒定。
  • 如果Fastmap信息无效(例如,不正常的系统关闭),它将退回到扫描状态(速度较慢,但能保证正确,Fastmap在下次启动时将恢复)。
  • 详细信息:Thomas Gleixner的ELCE 2012演讲:
    http://elinux.org/images/a/ab/UBI_Fastmap.pdf

    ; z; I8 R- }' H* c* ?) S* _
使用步骤:
  • 使用CONFIG_UBI_FASTMAP配置编译内核
  • 使用ubi.fm_autoconvert = 1内核参数至少引导一次系统。
  • 以干净的方式重启系统
  • 保证如上启动一次后可以删除ubi.fm_autoconvert = 1

    ' k% z$ e/ E- B3 m+ d5 ]8 f
UBI Fastmap性能测试举例:
  • 在Linux 3.10的Microchip SAMA5D3 Xplained板(ARM)上测得
  • UBI空间:216 MB
  • 根文件系统:已使用80 MB(Yocto)
  • 平均加载时间:

    : C, d" |3 `3 k/ x7 q: X
    3 K) W/ @/ [. Z2 V- N
    • 无UBI Fastmap,加载时间:968ms
    • 有UBI Fastmap,加载时间:238 ms
      可见UBI Fastmap 改善非常显著!

      ! _: O4 T' r& m& `0 V3 s. d

# n0 d! n. o# v$ |4 ^+ s' g' L
1.6. ubiblock + SquashFS: Y5 I- D# p) z# [7 R
对RAW Flash :
  • ubiblock:位于UBI顶部的只读块设备
    利用CONFIG_MTD_UBI_BLOCK配置编译。
  • 允许将SquashFS放在UBI卷上。
  • 引导时间和读取性能不错。非常适合于只读根文件系统。
    / ]5 m5 ~) V/ y0 w$ k; f

* }8 I' N: m( K9 _0 n: |; \
2. 选取合适的文件系统
  • RAW Flash :带有CONFIG_UBI_FASTMAP的UBIFS可能是最佳解决方案。
  • 块存储:SquashFS是根文件系统的最佳解决方案,它可以是只读的。Btrfs和f2fs可能是读/写文件系统的最佳解决方案。
  • 更改文件系统类型非常容易,并且对应用程序完全透明。只需尝试几个文件系统选项,看看哪个最适合!
  • 不要只关注启动时间。
    对于读写性能至关重要的系统,我们建议使用单独的根文件系统(以加快启动时间)和数据分区(以实现良好的运行时性能)。

      m: H( G% O) p& _7 {5 C) ?

    " ], s. a5 d6 P: P. \; |
2.1 Initramfs一个很好的方案是使用非常小的initramfs,以启动关键应用程序,然后切换到最终的根文件系统。

' N  x5 _5 p# c' z6 w
微信图片_20200320142204.png " W( }4 T% `1 ?/ Z+ `9 }
initramfs机制:将根文件系统集成到内核映像中,因此它与内核一起被加载到内存中:
  • 它将文件系统的压缩存档集成到内核映像中
  • 变种:压缩的initramfs固件也可以由bootloader单独加载。
    3 S5 ^7 }# k" n9 Z, I2 |
initramfs在下面两种情况下非常有用:
& V/ }- K. T8 R1 f) |% K9 ?
  • 快速启动且非常小的根文件系统。由于文件系统在启动时已完全加载,因此应用程序启动也非常快。
  • 作为切换到实际根文件系统之前的中间步骤,该文件位于需要其驱动程序不属于内核映像的设备(存储驱动程序,文件系统驱动程序,网络驱动程序)上。始终在桌面/服务器发行版的内核上使用此选项,以保持内核映像大小合理。
    9 I) _0 |0 h" u
( x9 Y. w4 h# V) P) A0 Z
2.2 内存中的initramfs
# J5 W& I! e' [! C+ d
+ T! @" d5 m1 V; \9 L1 P
微信图片_20200320142200.png 0 d/ o8 h6 p0 e1 D( {7 C9 D
  • 使用CONFIG_INITRAMFS_SOURCE选项在内核配置级别定义initramfs的内容
    # y: T* s& C. L2 a
    # q! w5 g4 B$ S! V
    • 可以是包含根文件系统内容的目录的路径
    • 可以是cpio归档文件的路径
    • 可以是描述initramfs内容的文本文件

      ' g! f! J' Q. ~) Q/ r) R# k; M# ]
  • 内核构建过程将自动获取CONFIG_INITRAMFS_SOURCE选项配置的内容,并将根文件系统集成到内核映像中
  • 详细信息(在内核源文件中):
    Documentation/filesystems/ramfs-rootfs-initramfs.txt
    Documentation/early-userspace/README
    5 B# |2 J9 `6 _' C

+ P7 R4 a; o, y" p
2.3 用initramfs启动过程$ |8 ?% d$ ?2 e. w
4 _2 L+ N+ \2 g6 y/ X, F
微信图片_20200320142157.png
. R- ~& Z8 M) l2 E
2.4 initramfs 降低启动时间
创建尽可能小的最小初始化文件,足以启动关键应用程序,然后使用switch_root切换到最终根文件系统:
  • 使用轻量级的C库以减小固件大小,建议使用uClibc。
  • 将BusyBox裁剪到最小。甚至可以不用BusyBox直接在C中实现/init。
  • 使用静态链接的应用程序(较少的CPU开销,较少的库加载,较小的initramfs(如果根本没有库))。Buildroot中用BR2_STATIC_LIBS配置。

    # J2 Z" U# d- ?$ l, c
2.5 静态链接可执行文件
* q+ b. c* h. C3 g6 p
  • 静态链接的可执行文件对于减小大小(特别是在小型initramfs中)非常有用,并且启动工作量较少。
  • 如果您将initramfs放在压缩的内核映像中,请不要对其进行压缩(启用CONFIG_INITRAMFS_COMPRESSION_NONE)。
  • 否则默认情况下,您的initramfs数据将被压缩两次,内核将更大,并且将花费更多的时间来加载和解压缩。
  • 在Linux 5.1上的示例在Beagle Bone Black上具有1.60 MB的initramfs(tar存档大小):这可以将内核大小从4.94 MB减少到4.74 MB(-200 KB),并节省大约170毫秒的启动时间。
    * A7 a$ U0 ^5 u# E6 V( u0 N# V

/ N  b/ N6 D( S  @
03内核
- I# R' U. w' |9 s
1.有用的调试手段
1. 1 内核初始化度量函数
- u% ^5 m/ F, w* p- j* w+ U2 N
要找出最长执行时间的内核初始化函数,请在内核命令行中添加initcall_debug。将内核日志中得到如下日志:

( j) H, [# {4 e% v6 i! |5 e
微信图片_20200320142021.png 9 t* Y. h0 R" e
如使用initcall_debug可能需要在内核配置中使用CONFIG_LOG_BUF_SHIFT增加日志缓冲区的大小。还可能需要使能CONFIG_PRINTK_TIME和CONFIG_KALLSYMS。
, b8 `0 c2 p4 @/ I* C
1.2 使用内核启动图进行有目的的优化
使用initcall_debug可以生成启动图,从而轻松查看哪些内核初始化函数需要最多时间来执行。
  • 复制dmesg命令的输出并将其粘贴到文件中(我们将其称为启动日志boot.log)
  • 在开发工作站上,在内核源代码中运行scripts / bootgraph.pl脚本:scripts / bootgraph.pl boot.log> boot.svg
  • 现在可以使用矢量图形编辑器(例如inkscape)打开启动图:

    # o( }4 ]1 c7 R! w

. i8 D4 ~1 [. w( S4 X* D
微信图片_20200320142024.png
8 U7 m" J8 ?+ c4 w( m/ P. A
首先从花费最长时间的功能开始尝试优化。对于每个功能:
  • 在内核源代码中查找其定义。
    可以使用Elixir(参考http://elixir.bootlin.com)。
  • 注意:某些函数名称可能不存在,名称与modulename_init相对应。然后,在相应的模块中查找初始化代码。
  • 删除不必要的功能:
  • 通过查看相应源目录中的Makefile,找到哪个内核配置参数可编译代码。
  • 延后加载处理:
    , Y# |; ?) T  s) D" s/ b
    • 查找功能所属的模块(如果有)。如果可能,请稍后加载此模块。
      5 A4 }8 L" a/ I! B5 m7 [: |: v
  • 优化必要的功能:

    . t  B4 P( Q+ d4 r. q# V
    • 查找可以用于减少探测时间的参数,并查找module_param宏。
    • 查找延迟循环和对名称中包含delay的函数的调用, 可以减少此类延迟,并查看代码是否仍然有效。
      ( e% U9 H% k! q/ A; A# @% e. u
3 {) s. F; D  j& R7 |
1.3 减小内核尺寸
首先,我们专注于在不删除功能的情况下缩小尺寸
  • 主要机制是使用内核模块
  • 将启动时不需要的所有内容编译为模块
  • 有两个好处:内核更小且加载速度更快,初始化代码更少
  • 删除用户空间不需要的功能:
    CONFIG_KALLSYMS,CONFIG_DEBUG_FS,CONFIG_BUG
  • 用专为嵌入式系统设计的功能:CONFIG_SLOB,CONFIG_EMBEDDED

    7 g& M9 @0 ^; j% X! v

# C: @9 D4 e8 K" x
然后考虑内核压缩的方式:
根据存储读取速度和CPU解压缩内核之间的平衡,需要对不同的压缩算法进行测试。还建议在内核优化过程结束时尝试压缩选项,因为结果可能会因内核大小而异。

, z' I# X4 I$ n: |" @! s" O1 S
微信图片_20200320142027.png 0 A3 G. D) G  m7 |; c' b, M8 f3 `
在基于TI AM335x (ARM), 1 GHz, Linux 5.1测试:

7 K: T8 ~3 k! k# d7 y) E
gzip
( x. n, z" @, r9 v9 B7 I
lzma
( E. ], N- r: s4 e# K/ h- j
xz
8 S/ e- z+ y9 {( u7 L4 }* K4 `6 Q& q; E
lzo
; m6 [8 B- {: r( H0 w. z; ~& c  W5 l
lz4
. L( E4 p7 B: \8 ]1 O! _0 c
大小# Y, q" ?" n# W  w" M) ]
2350336 1777000172012025338722716752
拷贝9 `+ w# N2 \( K" B0 O3 L3 L
0.208 s    0.158 s0.154 s0.224 s0.241 s
启动时间
! h; i2 o, A7 i4 m& }0 d8 k/ r
1.451 s    2.167 s1.999s1.416 s1.462 s
Lzo和Gzip似乎是最好的解决方案。 但这结果取决于存储和CPU性能,故在决定方案是务必进行测试。

# ^9 w) k' Z: R+ H' Z! u: a
另外内核的编译选项也有可以优化的可能:
  • CONFIG_CC_OPTIMIZE_FOR_SIZE:可以使用gcc -Os而不是gcc -O2编译内核。
  • 这样的优化会优先考虑代码大小,但会牺牲代码速度。
  • 结果:初始引导时间更好(较小的启动时间),但是较慢的内核代码可能会使性能降低。系统运行速度会变慢!
    4 R1 ]4 T+ ~  ]; W

- n# b& J8 t% a0 b: P
1.4 延迟驱动程序和初始化调用
如果有点功能无法编译为模块(例如,网络或模块子系统),可以尝试推迟执行。内核不会缩小,但某些初始化将被推迟,所以启动变快。通常,您可以修改probe()函数以返回-EPROBE_DEFER,直到它们准备好运行为止。
有关支持此功能的详细信息,请参见
http://lwn.net/Articles/485194/。

  b7 W; y( h9 [8 h
1.5 关闭控制台输出
控制台输出实际上要花费很多时间(非常慢的设备)。 产品中可能不需要。通过在内核命令行中传递quiet参数来禁用它。但仍然可以使用dmesg获取内核消息。这一步一般建议等最后一步再做,否则将损失控制台进行调试。

, ~% S+ q( p: i5 T) ^' o5 r
1.6 预置jiffy
* ~7 k; a" m' w
每次引导时,Linux内核都会校准延迟循环(用于udelay()函数)。这将测量每个jiff y(lpj)值的循环次数。只需要测量一次!在内核启动消息中找到 lpj值:
; b# B5 Z! P2 p2 w8 S5 }; i
Calibrating delay loop... 996.14 BogoMIPS (lpj=4980736)
然后将lpj = <value>添加到内核命令行:
Calibrating delay loop (skipped) preset value.. 996.14 BogoMIPS (lpj=4980736)

7 K3 g! H3 Y/ m  b) h  Z7 U
1.7 多处理器
SMP初始化很慢,即使您只有一个核心CPU,通常也会在默认配置中启用它(默认配置应支持多个系统)。因此,如果只有一个CPU内核,请确保将其禁用。BeagleBone Black上的结果:压缩内核大小:-188 KB

! r7 p0 K, n1 u( M8 a( J
要节省最后的毫秒数,您可能需要删除不必要的功能:
  • CONFIG_PRINTK = n与quiet命令行参数具有相同的效果,但是您无权访问内核消息。但是,您将拥有一个非常小的内核。
  • 在Thumb2模式下编译内核:CONFIG_THUMB2_KERNEL(任何ARM工具链都可以做到)。
  • 模块装卸
  • 块层(Block layer)
  • 网络堆栈
  • USB堆栈
  • 电源管理功能
  • CONFIG_SYSFS_DEPRECATED
  • 输入:键盘/鼠标/触摸屏
  • 减少CONFIG_LEGACY_PTY_COUNT的值或设置pty.legacy_count内核参数

    / }3 {, S' j) o, F) J9 x2 O# Z& j

; ]/ }5 o4 u7 N/ [; `2 R3 C
04启动脚本
( x! L4 i! n: e  K% A
1.优化初始化脚本和系统启动
2 o! V* }0 y, }1 M7 t9 k4 `
有多种方法可以减少启动应用程序之前执行启动脚本中花费的时间:
  • 仅在启动必要的依赖项之后,尽快启动应用程序。
  • 简化shell脚本
  • 可以尝试执行启动脚本之前启动应用程序
    2 l" r2 {* e# ]  y8 }# C$ V9 q
1.1.bootchart
如果想更详细地了解用户区的引导顺序,则可以使用使用grabserial中的bootchart来实现,例如:
, d/ Y9 E( n; C: q
微信图片_20200320142030.png
  l& |+ ^9 }9 q0 f3 I% }  l
那么如何配置并使用bootchart 呢?
  • 在busybox中配置使用bootchartd(CONFIG_BOOTCHARTD = y)
  • 通过命令行init = / sbin / bootchartd 引导您的开发板
  • 将/var/log/bootlog.tgz从目标复制到开发主机
  • 生成时间表:
    cd bootchart-<version>
    java -jar bootchart.jar bootlog.tgz
    bootchart 参见 http://www.bootchart.org
    0 M; X: c$ H% r1 Y/ u$ }
' `% S, k, O+ L6 f* P! v1 Q
1.2. systemd
如果将systemd用作初始化程序,则可以使用systemd-analyze。
http://www.freedesktop.org/software/systemd/man/systemd-analyze.html

/ n9 c- S4 c7 m+ L" w: y
微信图片_20200320142033.png . O* ]( k( X+ z
1.3. init 进程
在所有依赖项启动之后,应尽快启动:
  • 取决于您的init进程。在这里,我们假设使用sysV init脚本。
  • init脚本按字母数字顺序运行,并以字母开头(K表示停止(杀死),S表示开始)。
  • 将应用程序启动脚本使用最小的号码。
  • 甚至可以用应用程序替换init!如果可以成为第一个启动的应用程序,启动速度无疑大大加快!
  • 直接通过一个启动脚本启动所有服务(例如/etc/init.d/rcS)。这消除了对/ bin / sh的多次调用。
  • 甚至可以直接在应用程序的C代码中挂载文件系统:如

    2 f3 y9 W) N5 S0 O* m* y
  [0 I7 g8 u- E% |4 w# \
微信图片_20200320142035.png
1 U3 x4 |7 s. y1 W

% w8 O0 ^( U6 Y4 |1 o1 W
1.4 减少fork的使用
  • fork/exec系统调用开销很大。故从Shell调用可执行文件的速度很慢。
  • 即使BusyBox实现的shell中echo也会导致fork 系统调用!
  • 在BusyBox配置中选择Shells-> Standalone shell,以使Shell尽可能调用小程序。
  • 管道和反引号也由fork/exec实现。应减少它们在脚本中的使用。例:
    cat /proc/cpuinfo | grep model
    应修改为:
    grep model /proc/cpuinfo
    更详细,请参考:
    http://elinux.org/Optimize_RC_Scripts
    8 W- B* _% q% @) W2 Z5 F6 x5 @
    又例如:
    微信图片_20200320142038.png
  • 做如下修改更佳:
  • 微信图片_20200320142046.png
    仅此一项优化就可以在ARM AT91SAM9263系统(200MHz)上节省87毫秒!

    1 H  N# c' k5 `6 W) c! @5 E, j
1.5 减小固件的尺寸0 K0 G3 j1 d7 _; P; G% v1 c* p8 d: u
  • 剥离可执行文件和库,删除仅用于开发和调试的ELF部分。strip命令由交叉编译工具链提供。默认情况下在Buildroot中完成的。
  • superstrip超级剥离
    http://muppetlabs.com/~breadbox/software/elfkickers.html。
    使用strip可以剥离出Linux未用于启动可执行文件的更多位。Buildroot停止支持它,因为它可能破坏可执行文件。 仅在保存一些字节至关重要时才尝试。
  • 还可以尝试https://packages.debian.org/sid/mklibs上提供的mklibs:
    ( J+ M# k, w  J8 j
    mklibs产生精简的共享库,其中仅包含一组特定的可执行文件所需的例程。对于像OpenGL和QT这样的大型库确实很有用。它甚至可以在没有源代码的情况下工作。
    在Yocto中可用,但在Buildroot中不可用(2019.02状态)。
    限制:mklibs可以删除倾斜的库(由应用程序“手动”加载),因为它看不到它们。

    : V, g: g; n! [( T5 C' H8 M& E3 h

4 j0 N% m( j1 Z& T3 g9 S
3.5.5 快速启动画面显示
  • 可以使用fbv显示启动画面,
    http://freshmeat.sourceforge.net/projects/fbv
  • 使用armel,可以只使用我们的静态编译二进制文件:
    http://github.com/bootlin/static-binaries/tree/master/fbv/
  • 但是在MicrochipAT91SAM9263系统上很慢为878毫秒!
  • 为了更快地执行此操作,可以转储帧缓冲区framebuffer中的内容:
    fbv -d 1/root/logo.bmp
    cp /dev/fb0/root/logo.fb
    lzop -9/root/logo.fb
  • 然后尽早在initramfs中将其复制回:
    lzopcat/root/logo.fb.lzo > /dev/fb0
    0 {4 W: a; ^) N
2 Q3 N* d, H. a% R

4 }. y9 @' c8 j5 ^; x% i05Bootloader
9 p; i# R) G/ h& \3 N) U( O' S
' L# U, x2 K. B( j
1. 裁剪
  • 删除不必要的功能。
    通常引导加载程序包括许多仅用于开发所需的功能。用较少的功能编译您的bootloader。
  • 优化所需的功能。调整引导加载程序以获得最快的性能。

    2 b" v8 d- G0 x. o3 ]1 k/ b

$ m9 K1 S6 J. J1 w9 X& G: |
U-Boot是事实上的嵌入式bootloader标准,对于U-Boot而言,以下一些功能在产品中可能不需要:
1 q% }( l- N. g+ g3 a. B8 g3 M
  • 在include / configs / <soc>-<board> .h中禁用尽可能多的功能
  • 示例:MMC,USB,以太网,dhcp,ping,命令行版本,命令完成提示...
  • 较小且更简单的U-Boot加载速度更快,初始化速度更快。
  • 移除启动延时:

    8 G; D7 L; e2 M' T+ L
    • 消除启动延迟,这样通常可以节省几秒钟!:
      setenv引导延迟0
    • 在执行此操作之前,请先使用
      CONFIG_ZERO_BOOTDELAY_CHECK重新编译U-Boot,
      参考文档在doc / README.autoboot中。即使启动延迟设置为0,它也可以通过敲键来停止自动启动过程。

      * L3 E- c; X  G3 o) B
% {/ l2 B/ W$ u* k6 U) K% f; h
2. 简化脚本
% s5 N7 U$ ]* z1 y: x" g
有些情况,脚本很复杂,如:; p" e& `" t# P6 G( e/ z

4 _  s' f9 H7 Q- C& K
微信图片_20200320142051.png
+ k6 E# q/ |3 L& A
可以修改为:

! Q) m3 b' q5 \8 n" H# [
微信图片_20200320142054.png 6 U8 W2 I7 j: n/ r7 q
在ARM9(400MHz)可节省大约56ms

4 I1 x, V2 O  O* w
3. 拷贝准确大小的内核
  • 将内核从ROM复制到RAM时,仍然看到许多系统复制了太多无用字节,而没有考虑确切的内核大小。
  • 在U-Boot中,使用nboot命令:
    nboot ramaddr 0 nandoffset
  • U-Boot使用存储在uImage标头中的内核大小信息来知道要复制多少字节。
    6 E: o" `) ^. q1 D# I  H7 g

$ k* X' v' c3 L( i
4. 优化内核加载: O0 e; f3 ]8 d7 @) {* x' a
将内核uImage复制到RAM之后,U-Boot始终将其移动到uImage标头中指定的加载地址。还执行CRC检查。
  • 可以通过将uImage直接加载到正确的地址来使U-Boot跳过memmove操作。
  • 计算此地址:
    Addr = Load Address - uImage header size
    Addr = Load Address - (size(uImage) - size(zImage))
    Addr = 0x20008000 - 0x40 = 0x20007fc0
  • 在生产中时数据一般不会损坏将内核复制到RAM时,可以考虑禁用CRC检查
  • 禁用带有U-boot环境变量的CRC检查:
    setenv verify no
  • 关闭U-Boot控制台输出。需要使用以下命令编译U-Boot
    CONFIG_SILENT_CONSOLE和setenv silent yes.
    有关详细信息,请参见doc / README.silent。
    / j$ Y6 D# Q. i) ~& _
, M  i' E' e& z* _
5. 跳过bootloader直接启动内核
  • 原理:立即加载内核,而不是先加载引导加载程序,再加载内核!
  • 例如在Microchip AT91上,使用at91bootstrap v3即可轻松实现。% T# o+ T0 p8 f  ]( S
    您只需要使用linux或linux_dt配置之一进行配置:
    make at91sama5d3xeknf_linux_dt_defconfig
    make
      J5 v" D/ T- Y3 V+ k3 |
对于 U-Boot Falcon 模式:- A, a5 o7 o5 z# p8 {4 ^" q
  • U-Boot分为两部分:SPL(二级程序加载程序)和U-Boot映像。 然后,U-Boot可以配置SPL来直接加载Linux内核,而不是U-Boot映像。
  • 有关详细信息,请参见doc / README.falcon和
    http://schedule2012.rmll.info/IMG/pdf/LSM2012_UbootFalconMode_Babic.pdf进行原始演示。
  • 所有支持SPL的U-Boot板上均以相同的方式支持此功能。

    / a7 F6 ?: G  ]& H2 F$ f
微信图片_20200320142048.png
赞 收藏 1 评论0 发布时间:2020-3-20 14:31

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版