说实话,大多数人第一次意识到GRUB界面还能换皮肤,都是在别人的截图里。同样是开机菜单,别人那里是一张高清背景加半透明高亮条,你这里却是黑底紫框白字,连个圆角都没有。这不是硬件差异,而是你还没碰过自定义GRUB主题这件事。
这篇就从底层机制开始讲,带你把分辨率、字体、背景图、theme.txt菜单定义、进度条这些全部串起来,再给出可复现的安装与回退方案。中间会重点聊一个高频事故——主题装完,GRUB界面键盘失灵,以及如何安全地把主题部署到主力机上而不把自己锁在引导菜单外面。不管你是Ubuntu、Debian还是Arch系用户,只要想让开机界面像样一点,这篇文章都适用。
1. 先搞明白GRUB凭什么叫"能吃主题":显示栈与模块化逻辑
很多人以为GRUB就是一段写死在固件里的小程序,其实完全不是。现代Linux用的GRUB 2是一个相当完整的引导框架,它之所以能画背景图、渲染字体、摆菜单,靠的是一整套可插拔模块。理解这一点,后面遇到"主题没生效""界面花屏"才能快速定位。
1.1 从GRUB legacy到GRUB 2:图形能力从哪来
GRUB legacy时代,开机菜单基本就是纯文本,靠BIOS中断在屏幕上输出字符,想换背景想都别想。GRUB 2重新设计了架构,引入了帧缓冲(framebuffer)概念。简单说,GRUB加载自己的显卡驱动(比如vbe或efifb)后,会拿到一块可写入像素的显存区域,然后用gfxterm这个图形终端组件把文字渲染成位图,而不是直接吐ASCII字符。
主板上电后,固件会先初始化显示输出,GRUB再在固件提供的显示模式基础上切换分辨率。整个环节里,all_video模块负责探测并启动显卡输出,gfxterm负责图形终端,png/jpeg负责解码图片,font负责字形渲染,themes模块则专门解析主题描述文件。这一串模块只要有一个没加载,主题显示就会出问题。
我在排查主题失效时,第一步永远是看grub.cfg开头那几行insmod有没有把all_video和gfxterm带进来。很多一键主题安装脚本为了精简体积,会把模块列表整个重写,漏掉某个关键模块,界面就直接退化回文本模式。
1.2 一套主题文件到底在控制什么
GRUB主题不是一个可执行程序,而是一堆资源的集合。核心是一个theme.txt描述文件,里面用类似键值对的语法声明界面上每个元素的位置、大小、颜色和字体。再加上若干张PNG/JPG图片(背景、高亮框、图标等)和PF2字体文件,组合起来就是一套完整主题。
由于是纯文本描述,主题文件可以手工编辑,也能用脚本批量生成。你完全可以把一份theme.txt放进Git里做版本管理,改了之后直接重跑update-grub看效果,改坏了也不影响GRUB核心逻辑——最坏情况是主题解析失败,GRUB退回默认菜单,不会让系统启动不了。
我之前帮朋友排查过一次"开机界面变成纯黑"的问题,最后发现就是theme.txt里少写了一个闭合大括号,解析器直接放弃了整个主题。所以玩GRUB主题,心态上可以放松,它没有改内核那么危险。
1.3 分辨率匹配:主题翻车的第一大原因
GRUB主题里的坐标、宽高都是按像素写的。如果你的背景图是1920x1080,但实际开机时GRUB工作在1024x768,界面尺寸比例就会失衡,菜单甚至可能溢出屏幕边缘。
解决办法是在/etc/default/grub里设置GRUB_GFXMODE:
code复制GRUB_GFXMODE=1920x1080,1024x768,auto
这个参数的意思是,GRUB先尝试请求1920x1080的分辨率,如果固件或显卡驱动不支持,就依次尝试后面的选项,最后实在不行才用auto让GRUB自己选一个可用模式。
注意GRUB_GFXMODE只影响GRUB界面本身的分辨率,与内核启动后的分辨率(由GRUB_CMDLINE_LINUX_DEFAULT里的video=参数控制)是两回事。想要开机画面和GRUB菜单分辨率统一,得在两边同时设置。很多教程只提GFXMODE不提内核参数,结果就是GRUB菜单很清晰,一进系统splash画面却变糊了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 做主题之前,先把分辨率、字体和背景图这三样备齐
磨刀不误砍柴工。直接打开theme.txt开始人肉写坐标当然也能出东西,但很容易陷入"改一个数字、重启一次、看一遍效果"的低效循环。我通常先把素材和运行环境全部准备好,最后才碰主题文件。
2.1 确认你的机器环境
动手前先确认三件事:当前GRUB版本、固件模式、主题目录是否存在。
bash复制grub-install --version
[ -d /sys/firmware/efi ] && echo "UEFI mode" || echo "Legacy BIOS mode"
ls -la /boot/grub/themes/
UEFI和BIOS模式对GRUB的模块加载路径有差异,但不是主题文件格式上的差异,同一份theme.txt两种模式都能用。区别在于BIOS模式依赖vbe模块,UEFI模式依赖efifb/all_video,排查问题时需要留意。
如果/boot/grub/themes/目录不存在,手动创建就行,权限建议root所有、目录755、文件644。
2.2 用grub-mkfont生成PF2字体
GRUB不直接使用系统的TTF或OTF字体,它有自己的字体格式PF2,需要用grub-mkfont转换。这一步很重要,因为GRUB里的字体是像素字体,生成时指定多大字号,它在屏幕上就渲染多大,不会像桌面环境那样做矢量缩放。
bash复制sudo apt install grub2-common fonts-dejavu-core # Debian/Ubuntu
sudo mkdir -p /boot/grub/fonts
sudo grub-mkfont -s 20 -o /boot/grub/fonts/dejavu_sans_20.pf2 /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
-s参数控制字号,推荐16到24之间。字号太小,在高分屏上看不清;字号太大,PF2文件体积暴涨,加载也会变慢。实测下来20号是相对均衡的选项。
如果需要显示中文,建议直接找社区做好的CJK PF2字体,或者用grub-mkfont转换Noto Sans CJK,但要注意中文字形多,生成的文件可能好几MB,GRUB加载字数多的时候菜单响应会变慢。我的方案是英文界面,只在自定义label里放少量中文,单独生成一个小字号中文字体。
生成完字体后,在/etc/default/grub里设置:
code复制GRUB_FONT="/boot/grub/fonts/dejavu_sans_20.pf2"
这样update-grub会自动在grub.cfg里加入loadfont语句。很多主题装上后字体四四方方、没有圆润感,就是因为漏了这一步,GRUB只能用默认的unicode.pf2硬撑。
2.3 背景图的正确规格与遮罩制作
背景图建议直接用PNG,分辨率与你的目标显示分辨率一致。我习惯先定一个基准分辨率(比如1920x1080),主题里所有坐标都按这个分辨率布局,然后用百分比定位来抵消实机分辨率差异。
制作背景图前,先想清楚菜单区域会占据画面哪一块。GRUB菜单默认从左上角开始渲染,长度由菜单项数量决定,所以左下角和右下角可以放心放装饰性元素,右上至中央区域尽量保持简洁。
如果要给背景加暗色遮罩让菜单文字更清晰,我的建议是用ImageMagick直接把遮罩合进背景图里,而不是在GRUB层面叠加多个图层:
bash复制convert -size 1920x1080 xc:"rgba(0,0,0,0.55)" overlay.png
composite -gravity center -compose over overlay.png background.png final.png
GRUB的图层合成能力比较基础,图层一多容易在特定显卡驱动下出现渲染异常,把遮罩做进背景图里反而最稳。
2.4 素材目录的组织方式
code复制/boot/grub/themes/mytheme/
├── theme.txt
├── background.png
├── menu_highlight.png
└── fonts/
└── dejavu_sans_20.pf2
theme.txt里的图片路径默认是相对于theme.txt所在目录的,所以desktop-image: "background.png"就能直接引用同目录的图片。字体我习惯用绝对路径,因为PF2文件统一放在/boot/grub/fonts/下,方便多个主题共用。
3. 手写一份theme.txt:从空菜单到能看的界面
theme.txt的语法不复杂,但没有一份可复现的参考模板,第一次摸的时候很容易卡在坐标属性上。下面这份是我实测可用的极简主题,先整体过一遍,再逐段拆。
code复制# GRUB Theme - minimal
# 全局配置
desktop-image: "background.png"
title-text: ""
# 全局字体
terminal-font: "DejaVu Sans 20"
# 菜单区域
boot_menu {
left = 20%
top = 30%
width = 60%
height = 40%
item_font = "DejaVu Sans 20"
item_color = "#cccccc"
selected_item_color = "#ffffff"
item_height = 40
item_padding = 8
item_spacing = 6
selected_item_pixmap_style = "menu_highlight.png"
highlight_overlay = true
}
# 左下角时钟
label {
id = "__clock__"
text = "2025-01-01 00:00:00"
left = 5%
top = 90%
width = 30%
height = 24
align = "left"
color = "#aaaaaa"
font = "DejaVu Sans 20"
}
# 底部倒计时进度条
progress_bar {
id = "__timeout__"
left = 20%
top = 85%
width = 60%
height = 12
fg_color = "#66ccff"
bg_color = "#222222"
border_color = "#555555"
highlight_overlay = true
}
3.1 全局配置:桌面背景与标题
desktop-image声明背景图,路径相对于theme.txt所在目录。title-text: ""把GRUB默认显示在左上角的标题文字清空,很多美化主题都会这么做,避免自带文字和背景冲突。
terminal-font指定终端字体。注意这个字体名要跟GRUB加载的PF2字体对应,设置GRUB_FONT后,update-grub会加载它,theme.txt里填的字名才能被识别。
3.2 boot_menu段:菜单项的排列与高亮
boot_menu是主题里最重要的组件。它的left/top/width/height控制整个菜单区域的容器位置,这里用了百分比,比写死像素更抗分辨率变化。
item_font:菜单项文字字体item_color:普通项文字颜色selected_item_color:选中项文字颜色item_height:单个菜单项高度(像素)item_padding:文字与菜单项边框内的内边距item_spacing:相邻菜单项的间距selected_item_pixmap_style:选中项背景图highlight_overlay:true时,选中项背景图会覆盖在原生高亮之上,不会被菜单项自身颜色混合掉
这里有一个容易被忽视的点:item_height不是最终行高。实际行高等于item_height + item_padding * 2 + item_spacing。如果你做了一张固定尺寸的高亮框图片,调整间距后可能会发现选中背景和文字对不齐,所以要反复测。
3.3 label段:时钟与静态文字
label段可以放静态文字,也可以挂内置动态组件。id = "__clock__"表示让GRUB在渲染时把这段文字替换为当前系统时间,格式由text字段提供参考,GRUB会按实际时间刷新。
align属性控制文字在label区域内的对齐方式,有left、center、right三种。想让时钟出现在右下角,就把left调到70%以上,align改right。
3.4 progress_bar段:倒计时进度条
progress_bar表示倒计时进度条,GRUB发现id = "__timeout__"时,会把一段横条绑定为菜单超时倒计时。属性里fg_color是进度前景色,bg_color是背景色,border_color是边框色。
注意,进度条的width和height是内容区尺寸,边框会在内容区基础上向四周延展。如果你需要进度条水平居中,用百分比left配合width = 60%即可,GRUB会按比例换算。
4. 动态元素不神秘:倒计时、时钟与定位规则
上一节把静态骨架搭起来了,这一节专门说动态元素和坐标规则。GRUB主题没有真正的动画,所谓动态元素其实就两个:实时时钟和倒计时进度条。它们都是通过预留的id让GRUB识别,再由渲染引擎在每次屏幕刷新时替换内容。
4.1 倒计时进度条的工作方式
__timeout__这个id非常特别。它在GRUB渲染主题时,会自动读取当前菜单剩余超时时间,然后把进度条的长度按比例映射过去。也就是说,你不需要写任何逻辑代码,只要在theme.txt里声明了这个id,它就自动和GRUB_TIMEOUT变量联动。
如果你把GRUB_TIMEOUT=5设置成5秒,但主题里progress_bar的宽度是600像素,GRUB就会把600像素分成5份,每秒减少120像素。这种设计的好处是主题作者不用关心GRUB内部怎么计时,只要摆放好组件的位置就行。
进度条还有一个highlight_overlay属性,设成true会让进度条用前景色覆盖渲染,设成false则可能只显示边框变化,视觉上更朴素。
4.2 时钟label的刷新机制
时钟组件是label段里比较特殊的存在。GRUB每秒钟刷新一次这段文字的显示,所以你可以在text字段里写一个类似时间格式的占位符,实际渲染时会被替换。这个特性很实用,我见过有的主题直接在GRUB界面里显示"按任意键进入系统,5秒后自动启动",用的是label+progress_bar组合,视觉效果相当好。
但时钟也不是万能的,它依赖固件提供的实时时钟信息。有些服务器主板在冷启动时不初始化RTC,GRUB阶段读到的可能还是1970年,这时候时钟组件就会显示一个诡异的时间。这不是主题文件的问题,是硬件或固件设置问题。
4.3 坐标与堆叠顺序:组件不是乱摆的
GRUB主题的组件渲染有严格的z序:theme.txt里越靠后的组件,绘制时越靠上。也就是说,后面定义的元素会盖住前面定义的元素。
定位时,相对坐标+和-也很重要。比如你要在菜单右侧放一张图标,可以这样:
code复制image {
left = "+10"
top = "-5"
width = 32
height = 32
file = "icon.png"
}
+10表示在父容器左边距基础上向右偏移10像素,-5表示在父容器顶边基础上向上偏移5像素。理解了这个偏移规则,你就能做相对布局,不用每次算全局坐标。
5. 部署不是复制粘贴:改grub配置的次序和"键盘失灵"排查
主题文件写好了,接下来进入最容易出事的环节:改启动配置。很多人贪方便直接用主题安装脚本一键覆盖,结果重启后键盘没反应或者菜单消失,才开始后悔没备份。这一节我把标准流程和应急排障写清楚。
5.1 修改/etc/default/grub的正确次序
第一步永远是备份。我建议备份两份,一份留在系统里,一份放到U盘或另一个分区:
bash复制sudo cp /boot/grub/grub.cfg /boot/grub/grub.cfg.bak
sudo cp /etc/default/grub /etc/default/grub.grub.bak
接着编辑/etc/default/grub,把主题相关配置加进去:
code复制GRUB_TIMEOUT=5
GRUB_GFXMODE=1920x1080,1024x768,auto
GRUB_THEME="/boot/grub/themes/mytheme/theme.txt"
GRUB_FONT="/boot/grub/fonts/dejavu_sans_20.pf2"
然后重新生成grub.cfg:
bash复制sudo update-grub
注意,不同发行版命令不一样。Ubuntu/Debian系用update-grub,Arch系用grub-mkconfig -o /boot/grub/grub.cfg。很多教程只写Ubuntu的命令,Arch用户照抄就会踩坑。
重启前先别急,再检查一下grub.cfg里是否包含了主题声明和字体加载:
bash复制grep -E "theme|loadfont|gfxterm" /boot/grub/grub.cfg | head -20
如果看到loadfont和set theme=的行,就可以重启验证了。
5.2 主题装完,GRUB界面键盘失灵:完整排查链路
这是社区里出现频率极高的问题。常见现象是:主题正常显示,菜单也出来了,但按方向键、回车都没反应,系统最终按超时自动启动默认项。
遇到这种状况,先别急着怪主题。通常不是主题文件本身导致键盘失灵,而是主题依赖的图形终端模块加载后,键盘模块没有被正确初始化。以下是完整的排查链路,按顺序走一遍基本能定位。
第一步,在GRUB界面按c键,尝试进入GRUB命令行。如果能进,说明键盘工作正常,问题可能出在菜单渲染逻辑上,可以试试按Esc退回菜单再操作,或者在命令行执行normal返回菜单。
第二步,如果按c完全没反应,找一个PS/2接口的老键盘插上测试。很多台式机主板还保留PS/2口,如果PS/2键盘能用,问题基本锁定在USB键盘驱动上。
第三步,确认grub.cfg里的模块加载。在可用的键盘上,进入GRUB命令行执行:
code复制insmod usb_keyboard
insmod at_keyboard
configfile /boot/grub/grub.cfg
如果执行后键盘恢复,说明对应的键盘模块确实缺失。重启后问题复现的话,需要检查是不是某些主题安装脚本为了精简体积,把你原本的GRUB模块列表覆盖了。
第四步,如果PS/2和USB键盘在GRUB界面都失灵,检查主板固件设置。很多主板默认开启"USB Legacy Support"选项,这个选项负责在操作系统加载前让USB键鼠工作。如果被关闭,GRUB阶段USB键鼠自然没反应。进BIOS把它打开即可。
第五步,如果以上都无效,只能用U盘启动一个Live Linux系统,chroot进原系统后恢复grub.cfg备份,再重新update-grub。这个操作需要root权限,先挂载根分区和boot分区,再chroot执行命令。
我在实践中发现,键盘失灵很多情况下不是单点故障,而是多个因素叠加。比如固件关掉了USB Legacy,同时grub.cfg又被主题脚本精简掉了usb_keyboard模块,双重打击下问题才爆发。所以排查时不要只盯一个方向。
5.3 临时预览主题而不影响当前引导
想不重启就预览主题效果?这在GRUB里其实可以做到。重启进入GRUB界面后,先别让系统自动启动,按c进入命令行,手动按顺序执行:
code复制insmod all_video
insmod gfxterm
terminal_output gfxterm
loadfont /boot/grub/fonts/dejavu_sans_20.pf2
set theme=/boot/grub/themes/mytheme/theme.txt
configfile /boot/grub/grub.cfg
执行完,GRUB会用新主题重新绘制一次菜单。如果界面正常,说明主题文件没有语法错误;如果黑屏或花屏,至少你还没动过任何配置文件,重启后GRUB会自动回到之前的默认菜单。
不过这个命令序列需要键盘在GRUB命令行下能正常工作,如果键盘本身就是坏的,还是去虚拟机里测试吧。虚拟机里按Shift(BIOS模式)或Esc(UEFI模式)可以强制显示GRUB菜单,快照功能让你随便折腾,不需要担心搞坏主力机。
6. 多系统场景下的主题行为与常见坑
不少人的机器是Windows和Linux双系统,或者一台机器装了多个Linux发行版。主题在这些场景下表现如何,以及那些"装了主题Windows就启动不了"的传言是不是真的,这一节说清楚。
6.1 主题不会影响Windows引导,但会暴露配置问题
先说结论:GRUB主题只负责渲染菜单外观,不会改变菜单项对应的启动命令。主题文件损坏的最坏结果是不显示图形界面,退回默认文本菜单,而不是把Windows入口抹掉。
但为什么有人会在装完主题后遇到"GRUB无法引导Windows"?多半是他动系统配置时,不小心改坏了grub.cfg里Windows的menuentry,或者os-prober没有被正确执行。比如有些发行版出于安全考虑默认禁用os-prober,需要在/etc/default/grub里设置:
code复制GRUB_DISABLE_OS_PROBER=false
然后重新运行update-grub,Windows入口才会重新被探测出来。
另一种情况是EFI系统分区里的/EFI/Microsoft/Boot/bootmgfw.efi路径变了,GRUB的search --fs-uuid找不到对应的UUID,菜单项就会失效。这跟主题本身没关系,纯属引导配置问题。排查的时候,可以直接在grub.cfg里看Windows菜单项,再对照EFI分区实际文件位置。
6.2 BIOS与UEFI固件下的主题差异
BIOS+MBR和UEFI+GPT两种模式下,GRUB加载主题的路径不太一样。BIOS模式下,GRUB使用vbeinfo来查询可用显示模式,对旧显卡兼容性普遍较好;UEFI模式下,GRUB使用efifb或显卡的GOP协议,不同主板固件实现有差异,偶发"界面卡在文本模式"的情况。
如果你的机器是UEFI模式,且GRUB菜单始终无法进入图形模式,可以先在/etc/default/grub里确认GRUB_TERMINAL_OUTPUT没有被设置成console:
code复制GRUB_TERMINAL_OUTPUT="gfxterm"
console意味着用纯文本输出,主题当然不会生效。这个变量在部分发行版的默认配置里是注释状态,update-grub时可能衍生出terminal_output console,需要手动改成gfxterm。
另外,某些服务器主板或专业工作站自带显卡不带标准VGA BIOS,GrUB在启动初期只能显示最基础的文本界面,等到操作系统加载显卡驱动后才有画面。这不是主题问题,是固件层面就不支持GRUB阶段的图形输出,只能接受文本菜单。
6.3 常见问题速查表
下面这个表格是这几年碰到的GRUB主题问题的浓缩,遇到对应现象可以直接查:
| 现象 | 常见误判 | 实际情况 | 处理方式 |
|---|---|---|---|
| 主题没生效,还是默认界面 | theme.txt写错 | /etc/default/grub没有GRUB_THEME,或者update-grub未执行 | 补上GRUB_THEME并重新生成grub.cfg |
| 界面纯文本,无背景图 | 背景图损坏 | 缺少gfxterm/all_video,或GRUB_TERMINAL_OUTPUT被设为console | 加载gfxterm模块,确认terminal_output设置为gfxterm |
| 菜单溢出屏幕 | 屏幕太小 | 主题按固定分辨率布局 | 改用百分比定位,调低基准分辨率 |
| 中文显示成方块 | 字体没装 | PF2字体缺少中文字形 | 转换或下载支持中文的PF2,设置GRUB_FONT |
| 进度条/时钟不出现 | 组件id写错 | 未使用__timeout__/__clock__保留id | 检查组件的id字段 |
| 键盘失灵 | 主板坏了 | 键盘模块未加载或USB Legacy被关闭 | 先insmod键盘模块,再查BIOS设置 |
备份grub.cfg之后,绝大多数"主题搞坏系统"的恐惧都可以放下。
最后分享一个我自己坚持到现在的小习惯:每次改GRUB相关配置前,除了备份grub.cfg,还会把/etc/default/grub和整个/boot/grub/themes/目录打包放到另一个磁盘或U盘里。主题这东西改起来快,但想凭记忆还原上一次能用的版本就很难。有一次我调高亮框调了一下午,最后发现备份里有更好用的旧版本,直接解压覆盖,省了一晚上。GRUB主题说到底是个配置活加美术活,多做备份,多测虚拟机,你也一样能做出开机就让人多看一眼的引导界面。
