资讯动态

psplash源码解析:嵌入式Linux启动画面实现与定制

发布时间:2026/9/9 22:15:23 来源:尧图企业网站定制
简介面向ARM架构嵌入式Linux开发的psplash源码包适用于需要在系统启动或升级阶段展示进度条与动画界面的开发者。资源包含完整工程文件共79个文件压缩后仅2.67MB涵盖C语言核心源码如splash.c、psplash-fb.c、头文件、配置脚本configure、Makefile.in、图像资源png、img.h以及编译生成的中间产物.o、.a等便于直接阅读和二次构建。通过分析init、splash、backend等模块可深入理解psplash与内核交互的加载机制并针对具体硬件调整进度条样式、背板图片和动画逻辑。同时包内附带了autogen.sh、make-image-header.sh等辅助脚本帮助开发者快速完成交叉编译和环境集成。目前已有667人学习下载对于正在研究嵌入式Linux启动优化或需要定制开机界面的工程师而言是一份结构清晰、可直接上手的参考材料。 我之前调试一块 ARM 板卡时内核起来之后到应用启动之前屏幕要黑三四秒客户觉得开机体验不行。后来我把目光落到 psplash 源码上。这个由 Yocto 项目维护的启动画面程序代码量不大但把嵌入式 Linux 启动早期的 framebuffer 渲染、进度条通知、图片静态化处理这些事安排得明明白白。这篇内容就围绕 psplash 源码的核心设计、编译集成和实际排错展开适合做嵌入式产品、需要自定义开机 logo、又不想引入完整图形栈的团队参考。1. 一个朴素的启动 logo为什么值得抠 psplash 源码psplash 是嵌在启动早期环境里的小程序它解决的核心问题只有一个在内核初始化完成、根文件系统可用、但用户图形界面还没起来之前往屏幕上画一张图和一个进度条让用户知道设备还活着、正在启动。这个阶段的环境其实相当恶劣。文件系统可能还是只读的进程管理器还没有拉起来也没有 X11、Wayland 这些图形栈。psplash 的聪明之处在于把依赖压到最低只需要一个能用的/dev/fb0framebuffer 节点和 libc剩下的事情全部自己干。图形栈在这时候反而是负担因为哪怕一个最简单的 X server 也要处理输入设备、窗口管理、合成器等一大堆逻辑而 psplash 只需要纯粹的往显存里写像素。很多人第一次看到 psplash 源码会觉得不过瘾因为它没有炫技的东西。但正是这种朴素让它可靠。它不做图片解码因为解码 PNG 需要引入 libpng还要在启动早期处理内存分配干脆把图片在编译期就转成 C 头文件运行时就变成一段静态数组。它不给进度条做复杂动画进度推进靠一个命名管道外部进程写一个数字进去它就画一段圆弧。拿它和 plymouth 比会更直观。plymouth 是桌面发行版常用的启动画面方案支持 DRM/KMS、多个插件、脚本语言功能确实强大但对一个只有 800x480 屏幕的工控板来说重量和复杂度都过了。psplash 的设计目标就不是桌面而是小系统、老内核、简单 framebuffer这套约束反而让它的源码非常适合拿来研究嵌入式启动阶段应该怎么写代码。2. psplash 源码骨架核心文件各管哪段启动流程psplash 源码很紧凑核心文件拆开看其实就几块主流程、framebuffer 抽象、FIFO 通信、控制台处理、配置和构建辅助脚本。2.1 从 main 到 framebuffer 的设备初始化psplash-main.c是入口负责初始化 framebuffer、加载图片数据、进入主循环。psplash-fb.c/h则把对 framebuffer 的操作封装成一组绘制原语。psplash 打开 framebuffer 的方式非常直接就是 open ioctl mmap 三板斧int fd open(/dev/fb0, O_RDWR); if (fd 0) return -1; struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; ioctl(fd, FBIOGET_VSCREENINFO, vinfo); ioctl(fd, FBIOGET_FSCREENINFO, finfo); fb-width vinfo.xres; fb-height vinfo.yres; fb-stride finfo.line_length; fb-bpp vinfo.bits_per_pixel; fb-data mmap(NULL, fb-stride * fb-height, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);注意一点finfo.line_length和vinfo.xres不一定相等很多 LCD 控制器会按 16 字节或者更高对齐每行所以刷屏的时候一定要用line_length作为步进不能直接用xres * bpp / 8。这个细节在实际硬件上踩过坑的人应该都有印象。2.2 消息显示与 FIFO 写端拆分psplash.c是整个程序的核心里面主要是进度条和提示文字的绘制逻辑。psplash-write.c是一个独立的命令行小工具作用是向 FIFO 写入进度值或者一条文本消息。psplash-console.c则负责临时接管内核 console 的输出避免启动日志和 framebuffer 上的画面互相干扰。这里的设计有个值得学习的地方psplash 把显示进程和控制进程彻底拆开了。psplash 本身只是一个被动显示端它不知道系统启动到哪一步也不需要知道其他启动脚本通过psplash-write向 FIFO 写入数据它才更新画面。这种解耦让 psplash 对启动流程完全无侵入任何脚本只要能写文件就能控制进度条不需要链接任何库不需要 IPC 框架。配置集中在psplash-config.h常见的可配置项包括 framebuffer 设备路径、FIFO 路径、默认显示消息、背景颜色、进度条颜色等。如果要在不同板卡之间切换改这个头文件再重新编译就行比做一套配置系统轻得多。3. 渲染链路PNG 变成 C 头文件再变成屏幕像素psplash 对图片素材的处理方式非常值得嵌入式开发者借鉴它不在运行时解码图片而是把图片在编译期转成静态数组运行时就变成一段静态数组。3.1 make-image-header.sh 与静态图片数组源码树里的make-image-header.sh脚本负责这件事。它调用 ImageMagick 的convert工具把 PNG 转成裸像素数据再生成对应的 C 头文件。生成的数组会按像素格式排列比如 RGB565 或者 RGBA8888取决于脚本参数。这样做的好处很明显。启动早期 CPU 资源有限如果每个 boot logo 都要跑到运行时去解压 PNG一来要引入 libpng 依赖二来解码本身有内存碎片、异常处理等不确定因素。放在编译期处理之后运行时的逻辑就是单纯把一段已知格式的数组拷贝到显存速度极快出错概率也极低。生成的头文件通常长这样static const uint8_t psplash_image_data[] { 0x00, 0x01, 0x02, 0x03, ... };源码库里自带的psplash-poky-img.h就是这类脚本的产物日常开发中基本不会手工编辑它都是替换图片后重新生成。3.2 屏幕像素是怎么填上去的psplash 的绘制 API 核心是psplash_fb_draw_image这类函数。逻辑说起来很简单拿到图片数组按行拷贝到 framebuffer 的 mmap 内存区域里。但实际要考虑几个细节图片尺寸大于屏幕时需要居中显示或者按屏幕尺寸裁剪framebuffer 的 bpp 和生成数组时的色深必须一致否则颜色完全不对小端平台和大端平台处理像素数据的方式不同psplash-config.h里的PSPLASH_FB_ENDIAN就是干这个用的。当图片尺寸比屏幕小psplash 会把图片居中放置。做法是先算出(screen_width - image_width) / 2作为起点 x 坐标再把图片的每一行数据拷贝到对应显存行。这个简单的居中算法在很多嵌入式 UI 里都是通用思路。4. 进度条不是画出来的是读管道读出来的这是 psplash 最容易被忽略、但最巧妙的部分进度条本身是一个圆环psplash 内部用角度来表示当前进度而推进角度的指令不是程序内部自己生成的而是通过 FIFO 管道从外部读进来的。4.1 FIFO 协议与 psplash-write 的职责默认情况下psplash 启动后会创建一个命名管道路径一般是/tmp/psplash_fifo。外部程序想更新进度时只需要往这个管道里写一个数字。psplash 主循环通过 select 或者 poll 监听这个管道读到一个数值后把它换算成圆环需要扫过的角度再调用绘制函数更新画面。psplash-write这个工具就是对写 FIFO 过程的一层薄封装。它的核心逻辑压缩一下就是这样int fd open(PSPLASH_FIFO, O_WRONLY); if (fd 0) return -1; write(fd, argv[1], strlen(argv[1]) 1); close(fd);也就是说启动脚本里执行psplash-write 25psplash 就把进度条推到 25% 的位置执行psplash-write Loading kernel module...底部提示文字就变了。这个协议简单到没法出错。4.2 启动脚本和 udev 怎么利用这个通道在 Yocto 集成的系统里进度条推进通常由 init 脚本或者 systemd 单元触发。比如启动过程中加载完某个关键服务后脚本就往 FIFO 里写一个新数值。因为 FIFO 是普通文件语义任何语言都能写shell 里echo 50 /tmp/psplash_fifo也是一样的效果。如果启动到某个阶段需要提示用户等待也可以用文本消息。源码里还带了一个 psplash-udev 相关的示例脚本借助 udev 规则在设备节点出现时向 FIFO 写入提示文字。这个用法很适合演示 USB 设备热插拔时的状态反馈。这里有一个实操细节FIFO 的权限要确认。早期 udev 或者 tmpfs 初始化时如果改变了/tmp下的文件权限外部脚本写 FIFO 会失败psplash 主进程读不到数据进度条就会一直停在原地没有任何报错。排查这类问题先看 FIFO 的文件权限再手动echo一下基本就能定位。5. 两种集成姿势实测Yocto 配方与手动交叉编译psplash 最常见的集成方式有两个一是走 Yocto 的 recipe 体系二是手动交叉编译后塞进根文件系统。两种方式我都实测过各有适用场景。5.1 Yocto 环境下最快接入如果产品基于 Yocto接入 psplash 几乎是一行配置的事情。在 image 的 bbappend 或者 local.conf 里追加 splash 特性IMAGE_FEATURES:append splash这个特性会隐式把 psplash 装进镜像并在启动阶段自动拉起。想自定义 logo通常是在自己的 meta 层里给 psplash recipe 写一个 bbappend把默认的 logo 图片替换成自己的素材。具体变量名在不同 Yocto 版本里略有差异以你使用的版本的psplash.bb为准但思路一致覆盖 SRC_URI 里的图片资源重新生成图片头文件后打包。5.2 手动交叉编译的几个注意点不依赖 Yocto 的时候手动编译 psplash 也不难。源码树里提供了 Makefile交叉编译时把工具链前缀传进去就行make CCarm-linux-gnueabihf-gcc CFLAGS-I${SYSROOT}/usr/include这里最容易忽略的是头文件依赖。psplash 的 framebuffer 操作依赖内核 uapi 里的linux/fb.h如果工具链的 sysroot 里没有这个头文件编译报错会非常隐晦。解决方法是把内核源码里的include/uapi目录加进 CFLAGS 的搜索路径或者直接使用完整的工具链 sysroot。编译产出的psplash和psplash-write两个二进制放到根文件系统里启动脚本负责创建 FIFO 并拉起 psplash 进程。两种方式的取舍我整理成一张表对比项Yocto 集成手动交叉编译接入速度快特性开关即用需要自己写 init 脚本自定义 logorecipe 覆盖图片即可手动跑脚本生成头文件排错难度依赖 Yocto 构建环境产物透明问题好定位适用场景整套镜像走 Yocto 构建已有手工构建的根文件系统6. 黑屏、花屏、进度条卡死启动画面的排错经验最后分享一些实际排查经验。psplash 本身逻辑简单出问题基本集中在几个外部因素上。6.1 黑屏先分清是没跑还是画了但看不见黑屏是最常见的现象但黑屏背后可能是两种完全不同的原因。第一种是 psplash 进程压根没启动或者启动后立刻退出了第二种是进程在跑但内容没显示出来。排查顺序建议是先确认进程启动后执行ps | grep psplash如果进程不存在大概率是 init 脚本没拉起或者缺少 FIFO 文件导致启动失败。如果进程存在但屏幕还是黑的就要看 framebuffer 节点了。很多板卡的/dev/fb0在启动早期权限不对psplash 打开节点失败后往往不会打印明显错误直接用strace跟踪一下open(/dev/fb0)的返回结果是最快的判断方式。6.2 花屏和颜色异常十有八九是像素格式不匹配颜色完全不对、偏色或者花屏几乎都是生成头文件时用的色深和 framebuffer 实际 bpp 不一致。屏幕是 RGB565图片却按 RGBA8888 生成的数组画出来的必然是乱的。修改方式很简单重新用正确的色深跑一遍make-image-header.sh重新编译 psplash。大端平台需要把PSPLASH_FB_ENDIAN配置项打开这个最容易遗漏。6.3 进度条不动FIFO 路径和写端权限psplash 正常显示 logo但进度条一直不动说明显示进程没问题是外部写端没打通。先检查psplash-write和 psplash 主进程用的 FIFO 路径是否一致。再检查写端是否有权限打开 FIFO有时 tmpfs 挂载选项或者 udev 规则会改变/tmp下的目录权限。手动执行一条echo 50 /tmp/psplash_fifo如果进度条动了问题就在写端脚本上如果没动问题大概率在主进程的 FIFO 监听逻辑没跑起来。我个人的习惯是先把 psplash 默认的 logo 和进度条跑通再换自己的定制图片。因为默认配置经过充分验证链路是通的后面只要改素材就行排查范围能缩小很多。如果你正在调一款老内核板卡的开机画面按这个顺序来省下的时间可不是一点半点。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价