2、环境变量管理(上):printenv、setenv、saveenv 详解
环境变量,说白了就是 U-Boot 的“记忆”。板子一上电,U-Boot 怎么知道从哪里启动?IP 地址配多少?内核用什么参数?全靠这些变量撑着。
我刚开始接触 U-Boot 时,总觉得环境变量就是个配置文件。后来调一块新板子,发现启动参数写错了,板子死活起不来。从那以后,我对这三个命令就特别上心。
核心认知:环境变量是 U-Boot 运行时的键值对数据库。printenv 负责查看,setenv 负责修改,saveenv 负责持久化。缺一个,你的配置就可能在下次重启后丢失。
2.1 printenv —— 把家底翻出来看看
printenv 是最常用的命令之一。不加参数,它会打印所有环境变量。我调试时第一件事就是敲它,看看当前板子的“状态快照”。
U-Boot> printenv
baudrate=115200
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw
bootcmd=mmc dev 0; fatload mmc 0:1 0x82000000 uImage; bootm 0x82000000
bootdelay=2
ethaddr=00:11:22:33:44:55
ipaddr=192.168.1.100
serverip=192.168.1.10
stderr=serial
stdin=serial
stdout=serial
Environment size: 412/16380 bytes
最后一行很关键——它告诉你当前环境变量占了多少空间,总容量多大。如果接近上限,你就要小心了。
我的习惯:每次拿到一块新板子,第一件事就是 printenv 并把输出保存到文本文件。这样万一改乱了,还能对照着恢复。我曾经因为手滑删了 bootcmd,板子变砖,幸亏有备份才救回来。
printenv 也支持查看单个变量:
U-Boot> printenv bootargs
bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw
如果变量不存在,U-Boot 会告诉你:
U-Boot> printenv nonexist
## Error: "nonexist" not defined
2.2 setenv —— 改变量,就这么简单
setenv 的语法很直白:
setenv 变量名 变量值
举个例子:
U-Boot> setenv ipaddr 192.168.1.200
U-Boot> printenv ipaddr
ipaddr=192.168.1.200
注意,这时候修改只存在于内存中。你重启板子,ipaddr 又会变回原来的值。想永久保存?往下看 saveenv。
setenv 还有一个重要用法——删除变量:
U-Boot> setenv myvar
U-Boot> printenv myvar
## Error: "myvar" not defined
不给值,就是删除。这个操作很危险,尤其是误删了 bootcmd 或 bootargs。我见过有人调试时想清一个临时变量,结果手快把 bootcmd 删了,板子再也起不来。
警告:删除变量前,先用 printenv 确认变量名。特别是 bootcmd、bootargs、ethaddr 这些关键变量。删错了,板子可能无法启动。
setenv 也支持带空格的变量值,用引号括起来就行:
U-Boot> setenv myvar "hello world"
U-Boot> printenv myvar
myvar=hello world
你想想看,如果不用引号,U-Boot 会把 world 当成下一个参数,结果就变成了两个变量。
2.3 saveenv —— 把修改写进“永久记忆”
saveenv 没有参数,直接敲就行:
U-Boot> saveenv
Saving Environment to MMC...
Writing to MMC(0)... done
它会将当前内存中的所有环境变量,写入持久存储设备。这个设备可能是 SPI Flash、NAND、MMC 或 EEPROM,取决于你的板子配置。
| 存储介质 | 特点 | 常见场景 |
|---|---|---|
| SPI Flash | 速度慢,容量小,可靠 | 小容量嵌入式板 |
| NAND Flash | 容量大,有坏块问题 | 高密度存储设备 |
| MMC/SD | 速度快,分区管理 | 树莓派、开发板 |
| EEPROM | 字节级读写,寿命长 | 网络设备、服务器 |
saveenv 执行后,下次上电或重启,U-Boot 会从存储介质中重新加载环境变量。你之前用 setenv 做的修改,才会真正生效。
我的建议:养成“改完就存”的习惯。我见过太多人改了变量,然后直接断电或重启,结果发现修改没生效,又回来查半天。setenv 之后立刻 saveenv,形成肌肉记忆。
2.4 三个命令的协作关系
这三个命令,说白了就是一套“读-改-写”流程:
- printenv:读取当前环境变量(从内存中)
- setenv:修改内存中的变量值
- saveenv:将内存中的变量写入持久存储
重启后,U-Boot 从持久存储加载环境变量到内存。所以如果你只 setenv 不 saveenv,重启后一切归零。
记住这个口诀:printenv 看现状,setenv 改内存,saveenv 写持久。三步缺一不可。
2.5 避坑指南
我这些年踩过的坑,总结成几条:
- 变量名大小写敏感:bootargs 和 Bootargs 是两个不同的变量。U-Boot 习惯用小写,别搞混。
- 值里不要有多余空格:
setenv ipaddr 192.168.1.100(末尾多了一个空格),printenv 看不出来,但实际值多了个空格,可能导致网络配置异常。 - 环境变量空间有限:printenv 最后一行会显示已用/总大小。如果满了,saveenv 会失败。我曾经在调试时塞了一大堆临时变量,结果 saveenv 报错,排查了半天。
- ethaddr 不要乱改:MAC 地址通常是出厂烧录的,改错了可能导致网络不通。如果你非要改,先记下原始值。
我曾经犯过的错:有一次调试网络,我改了 ethaddr,然后 saveenv。结果板子连不上交换机,查了半天才发现 MAC 地址冲突。最后只能重新烧录环境变量分区才恢复。所以,改关键变量前,一定先 printenv 备份。