基于ARM嵌入式 Linux 快速启动

By Toradex 胡珊逢

ARM平台嵌入式Linux下有些应用对系统启动时间有着特殊的要求。在很多场合下,这些系统并不需要针对所有任务立即就位,但是针对某些关键任务(例如接收以太网命令或者显示用户界面)则必须能够应对。该博文将提供一些方法和简单的步骤,基于Toradex Colibri i.MX6 ARM系统模块上优化启动时间。

提示: 文中涉及到的部分方法需要重新编译 U-boot、内核以及文件系统。请参考文末所附Toradex开发者中心网站上的相关文章。

在我们开始动手优化之前, 我们需要一个合适的方法来测量启动时间。如果想要十分精准地测量启动时间,这甚至需要牵涉到硬件(例如 GPIO 和示波器)。在绝大多数场合下,通过监控系统串口控制台输出已经是相当准确了。Tim Bird 的 grabserial是一个广泛使用的工具,可以用于查看串口控制台输出的时间信息。这个工具能够为收到的每一行信息添加上时间戳,如下面所示:

--------------------------------------

[email protected]:~/software/grabserial-1.8.1$ sudo ./grabserial -d /dev/ttyUSB0 -t

[0.000003 0.000003]

[0.267171 0.267168]

[0.267267 0.000096] U-Boot 2015.04 (Dec 15 2015 - 16:07:42)

[0.271167 0.003900]

[0.271261 0.000094] CPU:   Freescale i.MX6DL rev1.1 at 792 MHz

[0.275565 0.004304] Reset cause: POR

[0.277501 0.001936] I2C:   ready

[0.278540 0.001039] DRAM:  512 MiB

……

--------------------------------------

第一列数字代表时间戳(从收到第一个字符算起),第二行代表的是收到当前一行和上一行信息之间的时间间隔。

Linux 系统的启动,主要可以分为以下 3 个阶段,文章将逐一讨论。

- Boot loader

- Linux kernel

- User space (init system)

Boot loader

实际上在 boot loader 启动之前,还有两个步骤:硬件初始化和 boot ROM。硬件初始化需要满足电源上电顺序以及总线和处理器芯片复位时序要求。这个阶段的耗时一般是固定的,在 10 ~ 200 ms。

图 1 boot ROM 启动时间

图1 中,黄色信号是核心板 VCC,蓝色信号是串口输出 TX。从核心板上电到串口输出第一个字符之间的时间大约为 102ms。这个就是 Colibri I.MX6D 的 硬件初始化和 boot ROM 运行的时间。由于这部分代码是固化在SoC 上面,所以用户一般无法对其进行优化。

ARM 处理器从位于内部的 ROM 上启动固件。该固件从启动介质上加载 boot loader。该阶段的时间一般很短,取决于 boot loader 的大小。除了减小 boot loader 体积,很难做其他的优化。实际上能够做的优化和调整还是在 boot loader (U-Boot)。

目前发布的 V2.5 Beta 3 版本,从第一个字符输出到内核启动的时间约为 1.7 秒。主要涉及以下过程:

- U-Boot 初始化(约 370 ms,从接收到的第一个字符算起)

- Autoboot delay(1s)

- 读取 device tree  和内核加载(127 ms)

- 加载device tree  和内核加载(约 100 ms)

- 最后跳转至内核起始地址

Uboot 启动到内核启动时间:~1700ms

最显著的优化就是降低 Autoboot delay 。这个值可以使用下面的命令设置为 0:

--------------------------------------

setenv bootdelay 0

saveenv

--------------------------------------

这个也可以使用 CONFIG_BOOTDELAY 将其配置为默认值。在目前的发布版本中,如果将 bootdelay 设置为0,那么将会没有办法直接进入 boot loader 的命令行模式。U-Boot 提供一个选项CONFIG_ZERO_BOOTDELAY_CHECK,在 bootdelay 为 0 的情况下,用于检测一个字符。我们已经将其添加到下一个发布版本的默认配置中。

Uboot 启动到内核启动时间:~700ms

移除一些功能有助于减少分配时间和初始化这些功能的时间。例如移除 USB Client 和 DFU 功能,相关Patch请从这里下载。

这可以将 U-Boot 尺寸减小到 289 KB,缩短 U-Boot 读取时间 。用同样的方法,可以结合项目实际的外设需求,裁剪不需要的外设支持和功能。显然内核大小和加载时间具有线性关系,因此,优化内核尺寸将可以进一步提高启动时间。

Kernel

为了只测量内核启动的时间,可以使用 grabserial 的匹配功能重置 boot loader 输出信息中的时间。

--------------------------------------

sudo ./grabserial -d /dev/ttyUSB0 -t -m "^Starting kernel*" -q "^\[ *[]0-9.]* Freeing unused kernel memory.*"

--------------------------------------

启动结束的时间多少有点难以确定,因为内核将会继续初始化硬件,即使文件系统已经挂载和第一个用户空间进程(init)开始运行(延时初始化)。 “Freeing unused kernel memory” 是 init 进程启动前发出的最后消息,因此将其标记为内核线性任务的结束(请查看 kernel_init in init/main.c)。我将会使用这个信息的时间戳信息来比较启动时间。我们模块上默认内核的压缩尺寸为 4.8MB,启动时间为 4.6 秒。

Kernel 启动到 init 启动时间:4.6秒。

同 U-Boot 一样,Linux 内核也是同步地将信息发送到串口。具体的方法取决于所使用的串口,其会同步等待直到字符在串口上发送完毕。这个的优点在于,当内核崩溃的时候,那个时候的所有信息都是可见的。假如信息是异步输出,最后输出的信息将不 会指示内核所崩溃的地方。

内核中有一个参数,可以最大限度减少输出的信息:“Quiet”。然而,这也将屏蔽我们测试启动时间的字符信息(“Freeing unused kernel memory”)。最简单的方法输出这些信息是利用日志级别输出特定的信息。在‘mm/page_alloc.c‘ 中搜索 “Freeing %s memory”。我将使用 ‘pr_alert’ 输出信息。这个方法起高了 3秒。

--------------------------------------

[0.925608 0.000178] Starting kernel ...

[0.002166 0.002166]

……

[1.432211 0.207636] [    1.203216] Freeing unused kernel [email protected]:~/software/grabserial-1.8.1$

--------------------------------------

Kernel 启动到 init 启动时间提高到 1.4秒。

进一步提高启动时间的另一个简单的方法是移除功能。Yocto 项目提供了一个方便的工具 ksize.py,这个需要在内核编译目录中运行。这个工具能够显示内核各个部分的大小。第一个表格显示了大致的概况(为了获得准确的概况, 编译之前使用 make clean)。

--------------------------------------

[email protected]:~/Toradex/webinar/linux-toradex$ ./ksize.py

Linux Kernel                          total |       text       data        bss

--------------------------------------------------------------------------------

vmlinux                             9321389 |    8589061     311188     421140

--------------------------------------------------------------------------------

drivers/built-in.o                  2973586 |    2810983     121247      41356

……

usr/built-in.o                          138 |        138          0          0

--------------------------------------------------------------------------------

sum                                 8174774 |    7475382     283956     415436

delta                               1146615 |    1113679      27232       5704

--------------------------------------

可以被安全移除的一般是应用相关的功能。浏览各个第一层目录,有助于快速确定最可能移除的对象。

另外一个可以帮助我们进一步分析,内核启动时各个功能所消耗的时间是使用 Linux 源码中scripts/bootgraph.pl 脚本,将启动日志进行图形化分析。为此,需要在 U-boot 中配置

--------------------------------------

setenv defargs ‘enable_wait_mode=off galcore.contiguousSize=50331648 initcall_debug printk.time=1 quiet‘

--------------------------------------

在 Linux 导出日志

--------------------------------------

[email protected]:~# dmesg > boot.log

--------------------------------------

在电脑上运行以下命令

--------------------------------------

cat ./boot.log | ./scripts/bootgraph.pl > boot.svg

--------------------------------------

图 2 Kernel boot

根据上面工具的分析,以及实际项目外设和功能的需求,将不必要的驱动从内核中移除。例如我们将移除无线网络驱动、CAAM(i.MX6硬件加密加速模块)、文件系统(ext4, nfs, cifs 等)、SPI等功能。重新编译后,新的内核缩减到 3.6 MB。

--------------------------------------

[0.818066 0.000252] Starting kernel ...

……

[0.909013 0.434209] [    0.723273] Freeing unused kernel [email protected]:~/software/grabserial-1.8.1$

--------------------------------------

Kernel 启动到 init 启动时间提高到 0.9秒。

User Space

Systemd

在 Linux 用户空间,初始化工作由 init 系统完成。 Toradex BSP 镜像使用 ?ngstr?标准启动 init 系统,其称为Systemd。Systemd 目前已成成为桌面 Linux的标准 init 系统,具有丰富的功能,特别是为动态系统所设计。Systemd 同样会影响启动时间。多个守护进程可以用同时启动(利用现在的多核系统); 支持在稍后的一个时间延时加载服务时激活 socket 以及支持按需启动的设备激活。并且,集成的日志守护进程 journald 由于使用二进制日志文件和完善的日志文件管理可以节省空间。

根据实际应用,一个嵌入式系统可能是相当静态的。因此,并不需要 Systemd 的动态功能。不幸的是,Systemd 并不是一个很模块化的系统,各个模块之间由相互依赖关系。这使得精简 Systemd 变得困难。这一节将分文两部分,第一部分使用 Systemd 启动优化技术,第二部分会使用 System V。

在这两部分中,我们使用 “Freeing unused kernel memory” 作为测量基准时间。

sudo ./grabserial -d /dev/ttyUSB0 -t -m "^\[ *[]0-9.]* Freeing unused kernel memory.*"

在默认的 BSP 中

User space 启动到Login 的时间为 ~5.3 秒。

--------------------------------------

[0.447125 0.447125] [FAILED] Failed to start Load Kernel Modules.

[0.463050 0.015925] See "systemctl status systemd-modules-load.service" for details.

……

[5.302394 0.016012] colibri-imx6 login:

--------------------------------------

由于采用了自己编译的内核,因此在加载模块驱动的时候提示错误。只需要将内核模块重新编译,放到目标板的 /lib/modules/ 目录中即可。当然,如果应用中需要相应的驱动,完全可以不加载这些模块驱动。

--------------------------------------

[email protected]:~# rm /etc/modules-load.d/libcomposite.conf

--------------------------------------

systemctl 工具可以查看所有的启动项目

--------------------------------------

[email protected]:~# systemctl status

--------------------------------------

Systemd 提供了 systemd-analyze 工具,当使用 “blame” 时,能够打印出各个服务以及其启动的时间。这个可以发现最消耗启动时间的服务。但是,其中的值可能具有迷惑性,因为测量的时间是实际流逝的时间。服务有可 能处于睡眠状态,这时的 CPU 其实在处理其他任务。所以在列表顶部的服务不一定是最耗时的,特别是在单核系统上。默认的 BSP 中并没有包含  systemd-analyze,可以通过下面的命令,在线安装。

--------------------------------------

[email protected]:~# opkg update

[email protected]:~# opkg install systemd-analyze

[email protected]:~# cat systemd.blame

3.188s dev-mmcblk0p2.device

1.080s systemd-journal-flush.service

……

178ms systemd-fsck-root.service

--------------------------------------

systemd-analyze plot 也可以用图形的形式输出启动项的情况。

--------------------------------------

[email protected]:~# systemd-analyze plot > systemd.svg

--------------------------------------

?

图 3 Systemd boot

启动服务可以使用 disable 命令来关闭。有些服务(特别是 Systemd 自身提供的)可能需要掩码才能关闭它们。另外有一些可能是系统运行所需的。因此,在关闭服务时需要特别小心,而且一次只能处理一个。

--------------------------------------

[email protected]:~# systemctl disable usbg.service

--------------------------------------

Toradex 的 BSP 中,采用 ConnMan 作为网络管理工具,可以很灵活的管理无线网络连接。 但 ConnMan 的启动会消耗较多的时间。对于不使用网络或仅用有线网络的情况,/etc/systemd/network/wired.network 配置可以有效降低启动时间。

Toradex 的 BSP 中,采用 ConnMan 作为网络管理工具,可以很灵活的管理无线网络连接。 但 ConnMan 的启动会消耗较多的时间。对于不使用网络或仅用有线网络的情况,/etc/systemd/network/wired.network 配置可以有效降低启动时间。

--------------------------------------

[Match]

Name=eth0

[Network]

DHCP=ipv4

--------------------------------------

在不需要记录系统日志的应用中,将日志存储功能禁用后,也可以在一定程度上缩减启动时间。

--------------------------------------

/etc/systemd/journald.conf

[Journal]

Storage=none

#Compress=yes

--------------------------------------

内核参数中的 quiet,同样也适用于 Systemd。这个有助于 Systemd 的启动时间。

按照上述的优化,User space 启动到Login 的时间为 ~3.8 秒。

--------------------------------------

[1.748022 0.493043] [    0.783547] Freeing unused kernel memory: 280K (805f6000 - 8063c000)

[0.609885 0.609885] [    1.390393] systemd-fsck[117]: rootfs: clean, 21509/876392 files, 446716/3543040 blocks

……

[3.799609 0.016926] colibri-imx6 login:

--------------------------------------

System V init

在很长一段时间内,Linux 也使用 SysV 作为标准的 init 系统。由于基于脚本的系统,这是模块化的,并且可以相对容易地精简系统。特别是对相对静态的系统,并不需要 Systemd 的设备激活和 socket 激活。此时,SysV可以是很好的选择。

Toradex 的 Linux BSP 基于 OpenEmbedded 框架发布,用户可以很方便的配置  SysV。所需的patch文件请从这里下载。

重新编译使用 SysV 的镜像

--------------------------------------

[email protected]:~/Toradex/oe-core/v2.5/oe-core/build$ bitbake  console-trdx-image

--------------------------------------

在使用上面的镜像更新目标板后,在 Linux 执行

--------------------------------------

[email protected]:~# /usr/sbin/resize.sh

--------------------------------------

如果使用的是 Colibri I.MX6D 512MB , 在 U-boot 中执行

--------------------------------------

Colibri iMX6 #  patch_ddr_size

--------------------------------------

在使用  SysV 的系统上,同样也可以将一些不需要的启动项删除。

--------------------------------------

[email protected]:~#update-rc.d -f bootlogd remove

[email protected]:~#update-rc.d -f mountall.sh remove

[email protected]:~#update-rc.d -f networking remove

--------------------------------------

按照上述的优化,User space 启动到Login 的时间为 ~1.1 秒。

--------------------------------------

[1.594064 0.371247] [    0.661430] Freeing unused kernel memory: 280K (805f6000 - 8063c000)

[0.057864 0.057864] INIT: version 2.88 booting

……

[1.098062 0.000060] colibri-imx6 login:

--------------------------------------

在 /etc/init.d 建立启动脚本加载应用

--------------------------------------

[email protected]:vi /etc/init.d/drawing.sh

### BEGIN INIT INFO

# Provides:       toradex

# Default-Start:  3 5

# Default-Stop:   0 1 2 6

# Description:    draw rectangles on fb0

### END INIT INFO

/home/root/rectangles &

[email protected]:chmod a+x drawing.sh

[email protected]:update-rc.d drawing.sh defaults

--------------------------------------

到此,使用上述方法,应用程序能在 2.55 秒时间内启动。

--------------------------------------

[email protected]:~/software/grabserial-1.8.1$ sudo ./grabserial -d /dev/ttyUSB0 -t

[0.000002 0.000002]

[0.162820 0.162818]

[0.162917 0.000097] U-Boot 2015.04 (Apr 14 2016 - 10:09:25)

……

[0.690563 0.001000] Starting kernel ...

……

[2.633291 0.000076] colibri-imx6 login:

--------------------------------------

参考:

Flashing Embedded Linux to iMX6 Modules

http://developer.toradex.com/knowledge-base/flashing-linux-on-imx6-modules

Build U-Boot and Linux Kernel from Source Code

http://developer.toradex.com/knowledge-base/build-u-boot-and-linux-kernel-from-source-code

OpenEmbedded (core)

http://developer.toradex.com/knowledge-base/board-support-package/openembedded-%28core%29

时间: 2024-10-05 18:30:46

基于ARM嵌入式 Linux 快速启动的相关文章

外网访问ARM嵌入式Linux系统

外网访问ARM嵌入式Linux系统 实验室里的ARM嵌入式Linux系统,只能在局域网内访问,怎样从外网也能访问ARM嵌入式Linux系统? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动ARM嵌入式Linux系统 ARM嵌入式Linux系统默认的sshd端口是22. 2. 实现步骤 2.1 下载并解压holer软件包 Holer软件包:holer-linux-arm.tar.gz Holer支持多种ARM版本,请选择跟自己ARM版本匹配的holer程序上传至系统上. 2.2 获

为基于busybox根文件系统的ARM嵌入式Linux交叉编译dropbear使能SSH

最近使用busybox为基于ARM的板卡定制了一个极简单的根文件系统,由于busybox仅支持telnet而不支持ssh,本文将详细描述如何交叉编译dropbear并将其移植到目标板卡上使能dropbear. 目标环境: CPU:ARM 内核版本:4.X 工具下载: zlib-1.2.8 dropbear-2016.74.tar.bz2 第一步:编译zlib-1.2.8 下载zlib-1.2.8.tar.gz,笔者将其放在/home/liangwode/test目录下. mkdir -pv bu

基于ARM、linux的MF RC522射频读卡器

摘要:本设计将ARM.linux的嵌入式技术与RFID技术相结合,对于实现移动支付终端的低功耗.便携式和网络化具有特别的意义.首先是采用MF RC522芯片设计与制作读写器,实现对Mifare卡的读写操作:其次是使用S3C2440A芯片和linux搭建嵌入式系统,作为各模块沟通和处理的枢纽:最后是运用开发软件编写服务端和MFC.Qt界面客户端的程序,使得各模块通过SPI和wifi通信的方式协同工作.结合实物和软件的设计.制作与调试,实现了一个性能稳定和使用灵活的可移动终端系统. 论文: 基于RF

嵌入式Linux 修改启动LOGO

1.嵌入式 Linux LOGO显示原理 嵌入式Linux是直接在FrameBuffer的基础上.直接显示一个ppm格式的图象. 它 kernel/drivers/video/fbcon.c中的fbcon_show_logo()完成,最大颜色支持224色而不常见的255色. 默认的logo文件是 drivers/video/logo/logo_linux_clut224.ppm. 2.制作流程 因为LINUX LOGO格式需要ppm格式来显示.这种格式是一种用ASCII来描述图像数据一种格式.一

基于ARM和Linux的嵌入式Web Server设计与实现_爱学术——免费下载

[摘要]介绍了嵌入式ARM处理器的特点和硬件平台的设计.嵌入式操作系统的设计,构建了基于嵌入式Web Server的远程监测系统结构,重点分析了嵌入式TCP/IP协议栈的体系结构.嵌入式Web服务器BOA中一些重要文件的配置.实现动态Web服务的CGI技术及动态WEB服务器的移植,实现了基于B/S结构的嵌入式动态Web远程监测系统. [作者] 彭道刚  张浩  江剑宁  许龙虎 转载至爱学术:https://www.ixueshu.com/document/f838a30d77441936318

交叉编译VIM并移植到ARM嵌入式Linux系统

原创作品,允许转载,转载时请务必以超链接形式标明文章.作者信息和本声明,否则将追究法律责任.   众所周知,vim是vi的增强版本,实际体验要比vi好用很多,由于笔者为ARM系统制作的基于busybox的文件系统中只带了vi工具,本文主要介绍怎么移植vim工具到基于busybox的制作的rootfs中. 一.编译环境: Host机:ubuntu-16.10(64bit) Target: arm 交叉工具链:arm-linux-gnueabi-gcc 工具包: ncurses-5.9.7: htt

ARM嵌入式Linux系统开发

1. 2.

基于ARM处理器的反汇编器软件简单设计及实现

写在前面 2012年写的,仅供参考 反汇编的目的 缺乏某些必要的说明资料的情况下, 想获得某些软件系统的源代码.设计思想及理念, 以便复制, 改造.移植和发展: 从源码上对软件的可靠性和安全性进行验证,对那些直接与CPU 相关的目标代码进行安全性分析: 涉及的主要内容 分析ARM处理器指令的特点,以及编译以后可执行的二进制文件代码的特征: 将二进制机器代码经过指令和数据分开模块的加工处理: 分解标识出指令代码和数据代码: 然后将指令代码反汇编并加工成易于阅读的汇编指令形式的文件: 下面给出个示例

嵌入式linux面试题解析(一)——ARM部分二

嵌入式linux面试题解析(一)--ARM部分二 1.描述一下嵌入式基于ROM的运行方式基于RAM的运行方式有什么区别. 基于RAM的运行方式:需要把硬盘和其他介质的代码先加载到ram中,加载过程中一般有重定位的操作: 基于ROM:没有上面的操作. 基于ROM:速度较基于RAM的慢,因为会有一个把变量,部分代码等从存储器(硬盘,flash)搬移到RAM的过程:可用RAM资源比基于RAM的多: 基于RAM:速度较基于ROM的快,可用RAM比基于ROM的少,因为所有的代码,数据都必须存放在RAM中.