探究IMX6ULL启动原理并学会优雅关闭GUI和启动动画(适用韦东山、正点原子、野火)

前言

前段时间在学习韦东山的Linux课程过程中,特地去补了一下开发板的开机原理相关知识,之后才解决关闭GUI的问题,目前网上关闭GUI的方案都是删掉或移除某个文件,但这种方式太简单粗暴,而且如果想找回来,会比较麻烦。我使用的是IMX6ULL开发板,这篇文章会从以IMX6ULL的角度来讲解IMX6ULL的启动过程原理,以及如何优雅的进行关闭GUI和启动动画,下面进入正文。

当给开发板通电到Linux系统启动,这个过程发生了什么?

1. 上电复位(Power-on Reset)

当i.MX6ULL处理器上电时,处理器的复位引脚被拉高,触发上电复位。此时,处理器开始执行内部ROM代码。

2. ROM代码执行(ROM Code Execution)

i.MX6ULL内部的ROM代码负责初始化基本硬件并确定启动设备。ROM代码会按照预设的启动设备顺序(如eMMC、NAND Flash、SD卡等)尝试加载启动加载器。

3. 加载并执行SPL(Secondary Program Loader)

如果启动设备中包含有效的SPL镜像,ROM代码将其加载到片上RAM(如OCRAM)中,并跳转执行。SPL的主要任务包括:

  • 初始化时钟和电源管理。
  • 初始化DDR内存。
  • 配置必要的外设。

4. 加载并执行U-Boot(主要引导程序)

SPL完成基本硬件初始化后,会加载U-Boot(通常存储在同一存储设备中)的镜像到DDR内存中,并跳转到U-Boot的入口点执行。U-Boot的主要功能包括:

  • 初始化更多的硬件外设。
  • 提供一个命令行接口,用于调试和配置。
  • 加载操作系统内核和设备树文件(Device Tree Blob, DTB)。

5. 加载操作系统内核和设备树

在U-Boot的环境中,通常会有一系列的自动化脚本(如bootcmd)配置加载Linux内核和设备树文件。具体步骤如下:

  1. 加载内核镜像:U-Boot从存储设备中加载Linux内核镜像(如zImage或uImage)到DDR内存中。
  2. 加载设备树文件:U-Boot加载与硬件配置匹配的设备树文件到内存中。
  3. 启动内核:U-Boot将控制权交给内核,内核开始执行。

6. 内核启动(Kernel Boot)

Linux内核接管系统控制后,开始进行一系列初始化,包括:

  • 设置中断向量表。
  • 初始化内存管理。
  • 初始化设备驱动。
  • 挂载根文件系统。
    内核初始化完成后,会启动用户空间的第一个进程,即init进程。

7. 启动init进程

内核启动后,会查找并执行init进程。通常情况下,init进程是用户空间的第一个进程,其路径通常为/sbin/init/bin/initinit进程负责进一步的系统初始化,包括:

  • 读取初始化脚本(如/etc/inittab或系统的init配置)。
  • 启动系统服务和守护进程。
  • 设置用户登录环境。

image.png

分析inittab文件和init.d目录

inittab 文件

了解了开发板的启动过程之后,我们来看看inittab脚本里面含有什么(inittab文件是用于定义系统启动和关闭过程中需要执行的任务。它主要用于BusyBox init系统,当代更流行是systemd系统),IMX6ULL开发板上的inittab文件内容如下:

# /etc/inittab
#
# Copyright (C) 2001 Erik Andersen <andersen@codepoet.org>
#
# Note: BusyBox init doesn't support runlevels.  The runlevels field is
# completely ignored by BusyBox init. If you want runlevels, use
# sysvinit.
#
# Format for each entry: <id>:<runlevels>:<action>:<process>
#
# id        == tty to run on, or empty for /dev/console
# runlevels == ignored
# action    == one of sysinit, respawn, askfirst, wait, and once
# process   == program to run

# Startup the system
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -o remount,rw /
::sysinit:/bin/mkdir -p /dev/pts /dev/shm
::sysinit:/bin/mount -a
::sysinit:/sbin/swapon -a
null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd
null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin
null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout
null::sysinit:/bin/ln -sf /proc/self/fd/2 /dev/stderr
::sysinit:/bin/hostname -F /etc/hostname
# now run any rc scripts
::sysinit:/etc/init.d/rcS

# Put a getty on the serial port
ttymxc0::respawn:/sbin/getty -L  ttymxc0 0 vt100 # GENERIC_SERIAL

# Stuff to do for the 3-finger salute
#::ctrlaltdel:/sbin/reboot

# Stuff to do before rebooting
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

此文件中条目的通用格式是:

id:运行级别:动作:进程
  • id: 终端设备ID(如tty1、tty2等),如果为空,则表示默认控制台(/dev/console)。
  • runlevels: 运行级别。在BusyBox init中,这个字段被忽略。
  • action: 定义如何处理这个条目。可以是sysinit、respawn、askfirst、wait和once。
  • process: 要运行的程序或命令。

从文件中我们可以得到如下信息:
在系统初始化阶段(sysinit):

# 挂载proc文件系统到/proc。
::sysinit:/bin/mount -t proc proc /proc

# 将根文件系统重新挂载为读写模式。
::sysinit:/bin/mount -o remount,rw /

# 创建必要的目录,如/dev/pts和/dev/shm。
::sysinit:/bin/mkdir -p /dev/pts /dev/shm

# 根据/etc/fstab文件中的配置,挂载所有文件系统。
::sysinit:/bin/mount -a

# 启用所有的交换分区。
::sysinit:/sbin/swapon -a

# 创建标准输入、输出和错误的符号链接。
null::sysinit:/bin/ln -sf /proc/self/fd /dev/fd
null::sysinit:/bin/ln -sf /proc/self/fd/0 /dev/stdin
null::sysinit:/bin/ln -sf /proc/self/fd/1 /dev/stdout
null::sysinit:/bin/ln -sf /proc/self/fd/2 /dev/stderr

# 设置系统主机名,主机名来自/etc/hostname文件。
::sysinit:/bin/hostname -F /etc/hostname

# 执行初始化脚本/etc/init.d/rcS
::sysinit:/etc/init.d/rcS

运行级别(respawn):

# 以下条目定义了一个getty进程,以便提供一个登录提示符:
ttymxc0::respawn:/sbin/getty -L  ttymxc0 0 vt100 # GENERIC_SERIAL

在串口ttymxc0上运行getty进程,以便通过串口终端登录。respawn表示进程如果退出会被重新启动。

系统关闭(shutdown):

# 执行关闭脚本/etc/init.d/rcK,通常用于停止各种系统服务。
::shutdown:/etc/init.d/rcK
# 禁用所有的交换分区。
::shutdown:/sbin/swapoff -a
# 卸载所有文件系统。
::shutdown:/bin/umount -a -r

init.d 目录

在系统初始化阶段,init去执行了/etc/init.d/rcS脚本,先来看下init.d目录下有什么:

[root@100ask:/etc/init.d]# ls -l /etc/init.d
total 84
-rwxr-xr-x 1 root root 1012 Jul 23  2021 S01syslogd
-rwxr-xr-x 1 root root 1004 Jul 23  2021 S02klogd
-rwxr-xr-x 1 root root 1876 Jul 23  2021 S02sysctl
-rwxr-xr-x 1 root root  620 Jul 23  2021 S05lvgl
-rwxr-xr-x 1 root root  559 Jul 23  2021 S09modload
-rwxr-xr-x 1 root root 1634 Jan  1  1970 S10udev
-rwxr-xr-x 1 root root 1684 Jul 23  2021 S20urandom
-rwxr-xr-x 1 root root 1635 Jul 23  2021 S30dbus
-rwxr-xr-x 1 root root  438 Jul 23  2021 S40network
-rwxr-xr-x 1 root root  707 Jul 23  2021 S44modem-manager
-rwxr-xr-x 1 root root  751 Jul 23  2021 S45network-manager
-rwxr-xr-x 1 root root  581 Jul 23  2021 S49ntp
-rwxr-xr-x 1 root root  531 Jul 23  2021 S50mosquitto
-rwxr-xr-x 1 root root  557 Jul 23  2021 S50pulseaudio
-rwxr-xr-x 1 root root  591 Jul 23  2021 S50sshd
-rwxr-xr-x 1 root root  614 Jul 23  2021 S50telnet
-rwxr-xr-x 1 root root  427 Jul 23  2021 S80dnsmasq
-rwxr-xr-x 1 root root 1343 Jul 23  2021 S98swupdate
-rwxr-xr-x 1 root root 1403 Jul 23  2021 bluetooth
-rwxr-xr-x 1 root root  423 Jul 23  2021 rcK
-rwxr-xr-x 1 root root  455 Jan  1 00:06 rcS

如下是rcS脚本的内容:

#!/bin/sh

# Start all init scripts in /etc/init.d
# executing them in numerical order.
#
psplash -n &
for i in /etc/init.d/S??* ;do

     # Ignore dangling symlinks (if any).
     [ ! -f "$i" ] && continue

     case "$i" in
        *.sh)
            # Source shell script for speed.
            (
                trap - INT QUIT TSTP
                set start
                . $i
            )
            ;;
        *)
            # No sh extension, so fork subprocess.
            $i start
            ;;
    esac
done

/bin/hostname -F /etc/hostname

rcS脚本很简单,它使用了psplash程序开启一个启动动画,这里介绍下什么是psplash

psplash(Plymouth Splash)是一个轻量级的、适用于嵌入式Linux系统的启动画面程序。它在系统启动时显示一个简单的图形界面,通常是一个logo或进度条,以掩盖启动过程中出现的详细文本输出,从而为用户提供更友好的视觉体验。

接着,rcS脚本遍历/etc/init.d/目录下的所有以 S 开头的脚本文件,并且忽略任何悬挂的符号链接(指向不存在文件的链接)。如果当前文件$i不存在(不是一个普通文件),则跳过执行,继续下一轮循环。
接着使用case语句处理文件,根据文件扩展名不同采取不同的执行方式:

  • 对于以.sh结尾的脚本:
    • 使用子shell执行,以加快执行速度。
    • trap - INT QUIT TSTP:忽略中断、退出和挂起信号。
    • set start:将start作为参数传递给脚本。
    • . $i:源执行脚本(即在当前shell环境中执行)。
  • 对于其他文件:
    • 直接以子进程的方式执行,并传递start参数。

可以看出,每个S开头的文件都会被执行,我们打开其中一个文件,以S05lvgl文件为例,从它的命名可以看出它是一个启动LVGL GUI的脚本文件,它的内容如下:

#!/bin/sh
#
# Start lvgl....
#

start() {
        printf "Starting 100ask lvgl: "
        echo -n -e '\e[?17;14;224c'
        echo 0 > /sys/class/graphics/fbcon/cursor_blink
        start-stop-daemon -S -q -m -b -p /var/run/lvgl.pid \
                        -x /usr/share/lvgl/lvgl_100ask_demo
      [ $? = 0 ] && echo "OK" || echo "FAIL"
}

stop() {
        printf "Stopping lvgl: "
        start-stop-daemon -K -q -p /var/run/lvgl.pid \
                          -x /usr/share/lvgl/lvgl_100ask_demo
        [ $? = 0 ] && echo "OK" || echo "FAIL"
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart|reload)
        stop
        start
        ;;
  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit $?

这是一个典型的init脚本,包含startstoprestart函数,这个脚本做了如下事情:
start 函数

start() {
        printf "Starting 100ask lvgl: "
        echo -n -e '\e[?17;14;224c'
        echo 0 > /sys/class/graphics/fbcon/cursor_blink
        start-stop-daemon -S -q -m -b -p /var/run/lvgl.pid \
                        -x /usr/share/lvgl/lvgl_100ask_demo
      [ $? = 0 ] && echo "OK" || echo "FAIL"
}
  • printf "Starting 100ask lvgl: ":打印启动消息。
  • echo -n -e '\e[?17;14;224c':设置控制台属性。这个转义序列用于配置终端(可能是设置光标形状或其他属性)。
  • echo 0 > /sys/class/graphics/fbcon/cursor_blink:禁用帧缓冲控制台的光标闪烁。
  • start-stop-daemon -S -q -m -b -p /var/run/lvgl.pid -x /usr/share/lvgl/lvgl_100ask_demo
    • -S:启动一个新进程。
    • -q:安静模式,不输出信息。
    • -m:创建一个pid文件。
    • -b:在后台运行。
    • -p /var/run/lvgl.pid:指定pid文件路径。
    • -x /usr/share/lvgl/lvgl_100ask_demo:要执行的程序路径。
  • [ $? = 0 ] && echo "OK" || echo "FAIL":根据上一个命令的返回值($?)判断启动是否成功,成功则打印"OK",失败则打印"FAIL"。
    stop 函数
stop() {
        printf "Stopping lvgl: "
        start-stop-daemon -K -q -p /var/run/lvgl.pid \
                          -x /usr/share/lvgl/lvgl_100ask_demo
        [ $? = 0 ] && echo "OK" || echo "FAIL"
}
  • printf "Stopping lvgl: ":打印停止消息。
  • start-stop-daemon -K -q -p /var/run/lvgl.pid -x /usr/share/lvgl/lvgl_100ask_demo
    • -K:停止一个进程。
    • -q:安静模式,不输出信息。
    • -p /var/run/lvgl.pid:指定pid文件路径。
    • -x /usr/share/lvgl/lvgl_100ask_demo:要停止的程序路径。
  • [ $? = 0 ] && echo "OK" || echo "FAIL":根据上一个命令的返回值($?)判断停止是否成功,成功则打印"OK",失败则打印"FAIL"。
    case 语句
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart|reload)
        stop
        start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit $?
  • case "$1" in ... esac:根据传入的参数决定执行哪个函数。
    • start:调用start函数启动服务。
    • stop:调用stop函数停止服务。
    • restart|reload:先调用stop函数停止服务,再调用start函数启动服务。
    • *:对于任何其他参数,打印使用说明并退出(exit 1表示错误退出)。
  • exit $?:以最后一个命令的退出状态结束脚本。

优雅关闭GUI和启动动画

至此,我们已经知道了IMX6ULL的整个启动原理,并了解到GUI和启动动画是如何启动的,那么我们就没必要粗暴的对这些文件进行删除或移动了。
我们可以使用/etc/init.d/S05lvgl stop命令,当然这种方式的缺点是每次开机都要运行一遍,如果你希望每次开机启动的时候就关闭,那么可以注释掉S05lvgl文件里面的运行LVGL脚本命令即可,或者使用service命令,service命令用于管理init启动的服务。
关闭启动动画也很简单,直接将/etc/init.d/rcS脚本的psplash -n &这一行注释掉即可,这样,每当我们启动开发板,就不会有启动动画和GUI出现了。

结语

这篇文章介绍了IMX6ULL的启动原理,但其实它也适用于其他类型开发板,原理都差不多。我觉得只有了解了原理,再来进行我们想要的操作,就会感觉尽在掌控之中,和不了解原理带来茫然的感觉相比,效果还是不一样的。如果你觉得这篇文章不错,请帮忙点赞、转发、收藏,谢谢支持~

  • 12
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
启动正点原子imx6ull设备,你需要按照以下步骤进行操作: 1. 将正点原子的镜像解压到Ubuntu虚拟机中。 2. 进入设备树文件所在目录。使用命令`cd arch/arm/boot/dts`进入该目录。 3. 修改设备树文件(imx6ull-alientek-emmc.dts),可以使用`code imx6ull-alientek-emmc.dts`命令打开该文件进行编辑。 4. 在设备树文件中进行必要的修改,以防止LED灯占用。具体的修改内容需要根据你的需求和设备的具体情况来确定。 5. 编译内核和设备树。根据你提供的信息,你可能需要使用NXP提供的U-boot和Linux版本进行编译。确保你已经安装了相应的编译工具链和依赖库。 6. 根据你的开发板型号和版本,选择正确的启动方式。具体的启动步骤可能因为不同的开发板而有所不同,你可以参考正点原子提供的文档或者官方指南来进行操作。 7. 遵循正确的启动顺序,按照正点原子提供的说明进行启动。 需要注意的是,以上步骤仅供参考,具体操作可能因为你的环境和需求而有所不同。请仔细阅读相关文档和官方指南,并根据实际情况进行操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [正点原子 imx6ull linux 更新内核与设备树 通过nfs挂载](https://blog.csdn.net/hk2121/article/details/129665151)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [正点原子IMX6ULL开发板-liunx内核移植例程-uboot卡在Starting kernel...问题](https://blog.csdn.net/Qiuhongim/article/details/129287967)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值