简介:psplash源码是一套面向ARM嵌入式Linux平台的启动进度界面程序,用于系统启动或升级过程中显示进度条与动画。这份源码包适合嵌入式开发者、Linux系统定制人员学习参考,能够帮助理解用户态程序如何与内核启动流程配合,并自行改造出符合产品需求的启动画面。资源共79个文件,压缩包大小仅2.67MB,主要包含C源码、头文件、configure/Makefile构建脚本、图片资源及少量示例文件。其中splash.c负责图形绘制,config.h可调整默认样式,backend目录则提供不同显示后端实现,整体结构紧凑,便于逐模块阅读。目前已有667人浏览学习,对于希望掌握psplash原理并进行二次开发的读者来说,是一份可直接编译使用的完整代码集。通过分析init、绘制、后端适配等模块,还能获得嵌入式启动界面定制、交叉编译及调试优化方面的具体经验。
1. psplash 这块开机 LOGO 背后是一套 frame buffer 裸图技术
做嵌入式设备的人多少都会碰到这么个需求:设备上电,屏幕先显示个 LOGO,过两秒再进系统。很多人第一反应是加个 Qt/GTK 程序在启动早期跑,试完就会发现根本跑不起来,因为图形栈还没初始化。psplash 就是为这个场景设计的,它是纯 frame buffer 程序,不依赖 X11、Wayland 或任何图形库,直接往 /dev/fb0 里写像素,项目体积小到可以塞进 initramfs。我最早是在某公司的车载中控项目里拆的它,那时才意识到它不只是个开机 LOGO 工具,更是一整套轻量级帧缓冲绘图方案。这篇文章适合做嵌入式 Linux、需要自定义开机画面的开发者,读完你就能用它生成自己设备的启动画面,并搞清进度条的联动机制。
2. 源码结构与编译配置:从 configure 到交叉编译四个必须说清的事
psplash 源码包不大,但结构很紧凑。拆开之后你会发现它其实由三块组成:核心绘制逻辑、一个命令行工具 psplash-write、一个以 FIFO 方式通信的 psplash-client。这三块各自承担什么角色,以及交叉编译时要动哪些参数,是能否顺利跑起来的分水岭。
2.1 源码目录里到底有哪些文件
正常下载解压后,你会看到这几个关键文件:
- psplash.c / psplash.h:主程序,负责打开 frame buffer 设备、绘制 LOGO、进度条、旋转动画
- psplash-fb.c / psplash-fb.h:对 frame buffer 的封装,处理 mmap、绘制像素点、图像拷贝
- psplash-write.c:一个小工具,往 FIFO 里写命令字符串,比如进度值、文本提示
- psplash-client.c:读取 FIFO 内容,通过共享内存或直接调起绘制函数更新画面
- psplash-poky-img.c:一张默认的 LOGO 图像,被转换成了 C 数组
- make-image.sh:把 PNG 图像转换成 C 头文件的脚本
- psplash-init / psplash-systemd:服务启动脚本模板
拆这份源码时最容易忽略的是 psplash-write 和主程序的关系。主程序 psplash 负责画图,而 psplash-write 是外部往它嘴里喂数据的通道。调试的时候你会发现,几乎所有进度条不动的问题,都出在 FIFO 路径或命名管道没建好。
2.2 编译前按需裁剪配置项
psplash 本身是 autotools 工程,进入源码根目录依次执行这几步就行:
./autogen.sh ./configure --host=arm-linux-gnueabihf \ --prefix=/usr \ --enable-static make逻辑说明:第一行生成 configure 脚本,第二行指定交叉编译工具链前缀和安装路径,第三行生成 Makefile 并编译。--enable-static表示静态链接,这在这种早期启动程序中几乎是必须的,因为那时候动态库很可能还没挂载。
参数说明:--host必须设成你目标板的工具链前缀,比如 aarch64 的板子就写aarch64-linux-gnu。--prefix一般设成/usr或你根文件系统的挂载点,这个直接影响你后续布署时文件被放置的位置。如果你的板子内存极小,还可以加--disable-rotation关闭旋转动画,能省下不少 CPU 时间。
编译产物里有两个主要二进制:一个是psplash,另一个是psplash-write。前者是要开机启动的主程序,后者是你系统里脚本和服务用来更新进度条的。
2.3 交叉编译与安装到根文件系统
交叉编译本身不难,难在最后往根文件系统里放。我通常会先把它临时安装到一个干净的目录里,再手动拷贝到目标根文件系统:
make install DESTDIR=$HOME/psplash-staging这条命令把全部安装文件放到$HOME/psplash-staging下,不会污染你的交叉编译环境。之后你把这个 staging 目录里的 usr/bin/psplash、usr/bin/psplash-write、usr/lib 下的库文件分别拷到你要打包的根文件系统对应位置即可。
常见的做法是把 psplash 放在 initramfs 里,通过 init 脚本或 systemd 的一个早期服务去启动它。以 init 脚本为例,你需要在挂载 proc、dev 之后,启动 udev 之前执行 psplash,否则 frame buffer 设备可能还没创建出来。psplash-write 则通常被你的启动脚本调用,比如系统服务每启动完一个模块就往 FIFO 里写入 25、50、75 这类数值,进度条就会跟着动。
3. 把 LOGO 换成自己的图:make-image 脚本与两个像素级小问题
源码里默认的 LOGO 是某公司风格的开源 LOGO,你肯定想换成自己产品的图。这里牵涉到图像转换、C 数组生成、以及一个很容易踩的字节对齐问题。
3.1 png 源图的尺寸与色彩边界
make-image.sh 脚本读入一个 PNG 文件,输出一个 C 头文件。你可以在源码根目录执行:
./make-image.sh logo.png psplash-logo-img.h逻辑说明:这个脚本内部调用了 ImageMagick 或类似工具把 PNG 转成原始 RGB/RGBA 像素数据,然后格式化输出为一个 C 语言的静态数组定义,编译时它会被直接编进二进制。
参数说明:PNG 的建议尺寸要根据你的屏幕来。常见做法是先查屏幕分辨率:
fbset -i拿到屏幕的宽和高后,把 LOGO 图缩放到宽度不超过屏幕的 60%、高度不超过 40%,居中显示视觉上会比较协调。颜色深度上要特别注意,psplash 目前主要支持 16bpp 和 32bpp 两种 frame buffer 格式。如果你设备是 16bpp(RGB565),Logo 图案里出现 16 位色表示不出来的颜色时,边缘会出现明显色斑。
3.2 make-image 转出来的 c 文件到底怎么工作
生成的 C 头文件核心是一段类似这样的代码:
static const uint8_t psplash_logo[] = { 0x89, 0x50, 0x4e, 0x47, ... }; static const int psplash_logo_width = 320; static const int psplash_logo_height = 180; static const int psplash_logo_bpp = 32;逻辑说明:这是把图片的原始像素字节按行顺序铺开,psplash_logo_width和psplash_logo_height决定了绘制时读取的行列范围。主程序在初始化 frame buffer 后,直接把这个数组内容拷贝到 buffer 的指定区域。这种方式的另一个好处是图片数据被编进了二进制,省去从文件系统读文件时可能出现的等待和失败。
参数说明:psplash_logo_bpp是 bits per pixel,它必须与你屏幕的 roto zoom 一致。一个血泪经验是,很多交叉编译工具链按char处理数组元素,但某些编译器选项会把char默认成有符号类型,导致逐字节绘制时出现奇怪的纵向条纹。所以如果你看到 LOGO 显示成一排排断裂的横线,检查一下这个数组元素类型,必要时显式改成unsigned char。
3.3 进度条颜色与位置都是可调参数
psplash 的头文件里定义了进度条的各种颜色值和位置参数,你可以在 psplash.h 里找到类似下面这样的配置项:
#define PSPLASH_BAR_WIDTH 300 #define PSPLASH_BAR_HEIGHT 30 #define PSPLASH_BAR_COLOR 0xff6600 #define PSPLASH_BAR_BG_COLOR 0xcccccc #define PSPLASH_BAR_START_X 10 #define PSPLASH_BAR_START_Y 200逻辑说明:进度条的绘制逻辑很简单:先画背景色矩形,再按当前百分比填充前景色矩形。PSPLASH_BAR_START_X和PSPLASH_BAR_START_Y决定进度条原点坐标,PSPLASH_BAR_WIDTH与PSPLASH_BAR_HEIGHT决定矩形宽高。
参数说明:颜色值用的是 RGB 24 位十六进制,比如0xff6600是橙色。调整时注意不要改完颜色后忘了改背景色,否则进度条到 40% 的时候,上半条会重叠一段难看的颜色。这里我一般会把背景色调成深灰,前景色调成纯白,对比度高,视觉上更干净。
4. 避坑指南:启动阶段最容易翻车的 5 个地方
psplash 本身代码简单,真正的坑几乎都出在它与启动流程的衔接处。以下按现象、原因、解决路径来写,都是实测过的场景。
现象一:开机后屏幕是亮的,但 LOGO 是花屏,出现斜向错位条纹。
原因是 frame buffer 的 stride(行字节数)和屏幕宽度不成简单倍数关系。很多屏幕一行实际占用的字节数会比宽乘 bpp 略大,psplash 部分版本在计算绘制偏移时用一个固定公式,导致每行像素错位。解决方法是查看/sys/class/graphics/fb0/virtual_size和stride的值,如果发现 stride 比宽乘 bpp 多出的字节数不为零,就需要改 psplash-fb.c 里的行步长计算逻辑。
现象二:psplash-write 执行后提示无法打开 FIFO,但 FIFO 文件明明存在。
原因多半是 psplash 主进程还没完成 FIFO 的创建和监听,write 端的脚本就跑起来了,于是 open 时文件还不存在或权限不够。解决方法是明确指定 FIFO 路径,并保证两端路径一致。常见做法是在启动脚本里先创建/run/psplash_fifo,再启动 psplash:
mkfifo /run/psplash_fifo chmod 666 /run/psplash_fifo /usr/bin/psplash --fifo /run/psplash_fifo &现象三:进度条卡在某个值不动,比如启动到一半音量服务启动时卡在 50%。
原因是启动脚本里的每步进度值单位不一致,有的是写绝对百分比 0-100,有的是写增量。psplash 的 FIFO 命令语法是PROGRESS 60这类绝对命令,写成PROGRESS +25它不认。解决方法是全局统一用绝对进度值,用脚本里的一个动态变量来维护当前进度。
现象四:psplash 启动就报 mmap 失败,屏幕黑屏闪退。
这通常发生在调用了fbset修改分辨率后。原因是你手动fbset 800x480-32改过 frame buffer 格式,但之后没有刷新FBIOGET_VSCREENINFO的缓存信息。解决方法是确认psplash启动前屏幕上已经能正常显示 framebuffer 内容,如果当前 console 还在 VGA 文本模式,先执行con2fb或把 console 切换到 fbcon,确保psplash能拿到正确的 fb_info。
现象五:开机时 LOGO 显示不到一秒就消失,屏幕直接黑掉。
原因是 init 脚本里投递进度值的节奏太快,psplash-write 在 FIFO 里写入了 100,主程序收到后认为启动完成,主动退出了。解决方法是把最终 100% 值的写入时机推迟到 main 服务真正初始化完成后。如果只是为了画面停留更久,可以在 psplash 主程序的--sleep参数传入一个毫秒值,让它即使收到 100 也再多停留一段时间再退出。
5. 验证与进阶:用一张测试图把 psplash 从黑匣子里拉出来
编译通过了不代表画面就对。我做嵌入式屏调试时最常用的一招,是先在 PC 上用虚拟 frame buffer 接 psplash,把屏幕内容转成 PNG,这样不是黑匣子也能看到每一步改动的实际效果。
首先要确认系统里有Xvfb或linux fb模拟工具。如果你手头只有普通 Linux 的 X 环境,可以走这个流程:
Xvfb :1 -screen 0 800x480x24 & export DISPLAY=:1 modprobe vfb fbset -fb /dev/fb1 800x480 24逻辑说明:Xvfb 跑起一个虚拟 X 服务,然后 vfb 驱动在内存里创建一块虚拟 frame buffer。这样 psplash 可以直接打开/dev/fb1而不依赖真实硬件。之后你就能像平常一样在这个虚拟屏幕上验证 LOGO 和进度条。
参数说明:-screen 0 800x480x24里的 800x480 要和目标屏的分辨率一致,x24表示 24 位色深。如果你是 16 位屏就写 16。这个虚拟 frame buffer 能模拟绝大多数绘制逻辑,但没法模拟真实的显示时序和行场同步问题,真机上还是要做过一次完整验证。
验证时我最喜欢用这个命令实时看 fifo 里的值:
cat /run/psplash_fifo & echo "PROGRESS 30" > /run/psplash_fifo echo "PROGRESS 75" > /run/psplash_fifo echo "PROGRESS 100" > /run/psplash_fifo逻辑说明:第一行后台实时盯 FIFO 内容,后面三行模拟系统服务依次上报进度。如果 LOGO 位置偏了或进度条颜色不对,改完立即重新编译能看到效果。
进阶玩法上,你还可以把 psplash 的绘制结果通过 img2txt 之类工具转成终端文本图,快速确认出坐标是否正确,省去一次次烧卡。具体做法是先把 frame buffer 内容导出:
cat /dev/fb1 > /tmp/fb_dump.raw python3 - <<'EOF' import struct data = open('/tmp/fb_dump.raw','rb').read() # 解析 raw 里的像素,转成 png EOF逻辑说明:这个 Python 片段只是示意你需要把 raw 数据按 4 字节一个像素解析成 RGB 值,再用 Pillow 保存成 png。这样不用真接屏幕也能预览到当前 LOGO 处在哪个位置、进度条有没有盖住主图。参数说明:raw 的数据宽度等于屏宽乘 bpp 再除以 8,注意从/dev/fb1读取时按块读,别一次性读空了。
拆完这个源码后,我自己的习惯是每拿到一块新屏,第一件事就是把 psplash 的初始分辨率参数和 bpp 单独拉出来做一次变量配置,坚决不塞在绘制函数里。从那以后,我每次搭新设备的开机画面都会强制走一遍虚拟 fb 验证流程,把坐标、颜色和进度条全调完再上真机。希望帮到你。
本文还有配套的精品资源,点击获取