修复 Ubuntu 24.04 WSL 启动时 mv: cannot stat '/var/run/motd.new': No such file or directory
0x00 缘起
在 Windows 11 的 WSL2 环境中启动 Ubuntu 24.04 时,本应正常显示的 MOTD(Message of the Day)出现异常:
PS C:\Users\Megatron> wsl -d Ubuntu24.04
mv: cannot stat '/var/run/motd.new': No such file or directory
ERROR: could not install new MOTD
This message is shown once a day. To disable it please create the
/root/.hushlogin file.
其中,真正需要修复的错误是:
mv: cannot stat '/var/run/motd.new': No such file or directory
ERROR: could not install new MOTD
经过实际排查和复现,最终通过为 /usr/sbin/update-motd 增加排他锁解决该问题。
本文首先给出完整修复方法。只想解决问题的读者,按照“对策”部分操作即可;希望了解故障原理的读者,可以继续阅读后面的“缘由”部分。
0x01 环境
操作系统:
Ubuntu 24.04.4 LTS
运行环境:
Windows 11 WSL2
Windows 中的 WSL 发行版名称:
Ubuntu24.04
WSL 内核版本:
6.18.33.2-microsoft-standard-WSL2
当前用户:
root
涉及的主要文件:
/etc/profile.d/update-motd.sh
/usr/sbin/update-motd
/etc/update-motd.d/
/run/motd
运行时目录关系:
/var/run -> /run
本文中的 Ubuntu 命令需要使用 root 用户执行。如果当前不是 root,可以先运行:
sudo -i
0x02 对策
本节只提供完整解决方案,不展开说明底层原理。
第一步:确认 flock 命令存在
进入 Ubuntu WSL,执行:
command -v flock
正常情况下应输出:
/usr/bin/flock
如果没有任何输出,安装 util-linux:
apt update
apt install -y util-linux
安装完成后再次确认:
command -v flock
第二步:备份原始 update-motd
执行:
backup="/usr/sbin/update-motd.bak.$(date +%Y%m%d-%H%M%S)"
cp -a /usr/sbin/update-motd "$backup"
echo "Backup: $backup"
正常情况下会显示实际备份路径,例如:
Backup: /usr/sbin/update-motd.bak.20260731-103500
请保留该备份,以便需要时恢复。
第三步:为 update-motd 增加排他锁
执行以下命令:
python3 - <<'PY'
from pathlib import Path
path = Path("/usr/sbin/update-motd")
text = path.read_text()
marker = "UPDATE_MOTD_LOCKED"
if marker in text:
print("The update-motd lock already exists; no changes were made.")
raise SystemExit(0)
old = """set -e
"""
new = """set -e
# Serialize concurrent invocations. All instances otherwise share
# /var/run/motd.new and can race while installing the generated MOTD.
if [ "${UPDATE_MOTD_LOCKED:-}" != 1 ]; then
export UPDATE_MOTD_LOCKED=1
exec /usr/bin/flock -x /run/update-motd.lock "$0" "$@"
fi
"""
if text.count(old) != 1:
raise SystemExit(
'ERROR: expected exactly one "set -e" line; no changes were made.'
)
path.write_text(text.replace(old, new, 1))
print("The update-motd lock was added successfully.")
PY
正常情况下应输出:
The update-motd lock was added successfully.
如果之前已经添加过锁,则会输出:
The update-motd lock already exists; no changes were made.
第四步:检查脚本语法
执行:
sh -n /usr/sbin/update-motd && echo "Syntax OK"
预期结果:
Syntax OK
如果没有出现其他错误,说明脚本语法正常。
第五步:检查锁代码
执行:
grep -n -A6 -B3 'UPDATE_MOTD_LOCKED' /usr/sbin/update-motd
应当能看到类似内容:
set -e
# Serialize concurrent invocations. All instances otherwise share
# /var/run/motd.new and can race while installing the generated MOTD.
if [ "${UPDATE_MOTD_LOCKED:-}" != 1 ]; then
export UPDATE_MOTD_LOCKED=1
exec /usr/bin/flock -x /run/update-motd.lock "$0" "$@"
fi
也可以查看脚本对应行:
nl -ba /usr/sbin/update-motd | sed -n '18,42p'
第六步:按照原始场景验证
在 Ubuntu 中删除 MOTD 的每日显示标记:
rm -f /root/.motd_shown
exit
返回 Windows PowerShell 后,彻底关闭 WSL:
wsl --shutdown
等待 3 秒:
Start-Sleep -Seconds 3
重新启动 Ubuntu 24.04:
wsl -d Ubuntu24.04
也可以将三个命令依次执行:
wsl --shutdown
Start-Sleep -Seconds 3
wsl -d Ubuntu24.04
修复后,Ubuntu 应当正常显示 MOTD:
Welcome to Ubuntu 24.04.4 LTS (GNU/Linux 6.18.33.2-microsoft-standard-WSL2 x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Fri Jul 31 10:37:28 CST 2026
System load: 0.0
Usage of /: 9.7% of 1006.85GB
Memory usage: 5%
Swap usage: 0%
Processes: 65
Users logged in: 0
IPv4 address for eth2: 192.168.6.186
This message is shown once a day. To disable it please create the
/root/.hushlogin file.
验收标准如下:
- Ubuntu 欢迎信息正常显示;
- 系统信息正常显示;
- 不再出现以下错误:
mv: cannot stat '/var/run/motd.new': No such file or directory
- 不再出现以下错误:
ERROR: could not install new MOTD
其中:
This message is shown once a day. To disable it please create the
/root/.hushlogin file.
是 Ubuntu 的正常提示,不是故障。
至此,修复完成。
如何回滚
如果修改后需要恢复原始脚本,先查找备份:
ls -lt /usr/sbin/update-motd.bak.*
找到对应备份文件后执行:
cp -a /usr/sbin/update-motd.bak.YYYYMMDD-HHMMSS /usr/sbin/update-motd
将文件名替换成实际备份文件,例如:
cp -a /usr/sbin/update-motd.bak.20260731-103500 /usr/sbin/update-motd
恢复后检查语法:
sh -n /usr/sbin/update-motd && echo "Syntax OK"
然后重新执行验证流程:
rm -f /root/.motd_shown
exit
在 PowerShell 中执行:
wsl --shutdown
Start-Sleep -Seconds 3
wsl -d Ubuntu24.04
软件包升级后的检查
/usr/sbin/update-motd 是由 Ubuntu 软件包管理的文件。未来升级相关软件包时,自定义修改可能被覆盖。
升级系统后,可以执行以下命令检查锁代码是否仍然存在:
grep -n 'UPDATE_MOTD_LOCKED\|update-motd.lock' /usr/sbin/update-motd
如果没有输出,并且问题再次出现,则重新按照本节步骤添加排他锁。
0x03 缘由
MOTD 是如何被触发的
Ubuntu WSL 启动交互式 Shell 时,会加载:
/etc/profile.d/update-motd.sh
该脚本会判断当前 Shell 是否为交互式 Shell,同时检查:
/root/.motd_shown
如果当天尚未显示过 MOTD,就会调用:
update-motd
对于 root 用户,脚本中的相关逻辑为:
[ $(id -u) -eq 0 ] || SHOW="--show-only"
update-motd $SHOW
由于 root 的 UID 为 0,所以不会设置 --show-only,而是直接执行完整的 MOTD 生成流程。
执行完成后,脚本会创建:
/root/.motd_shown
因此,同一天内再次打开 Shell 时,MOTD 通常不会重复显示。
这也是复现问题时必须先执行以下命令的原因:
rm -f /root/.motd_shown
随后还需要执行真实的:
wsl --shutdown
再重新启动 WSL,才能按照原始启动场景触发问题。
update-motd 如何生成 MOTD
Ubuntu 24.04 中的 /usr/sbin/update-motd 会依次执行:
/etc/update-motd.d/
目录下的组件,例如:
00-header
10-help-text
50-landscape-sysinfo
50-motd-news
91-contract-ua-esm-status
91-release-upgrade
92-unattended-upgrades
99-wsl
核心逻辑可以概括为:
touch /var/run/motd.new
run-parts --lsbsysinit /etc/update-motd.d \
> /var/run/motd.new
mv -f /var/run/motd.new /var/run/motd
也就是:
- 创建临时文件
/var/run/motd.new; - 执行
/etc/update-motd.d/中的脚本; - 将所有输出写入
/var/run/motd.new; - 生成完成后,将其移动为
/var/run/motd; - 显示最终 MOTD。
/var/run 并没有损坏
排查时检查了:
ls -ld /run /var/run
readlink -f /var/run
结果表明:
/var/run -> /run
因此:
/var/run/motd.new
实际上就是:
/run/motd.new
/var/run 到 /run 的符号链接关系正常,所以本次问题并不是:
/var/run目录不存在;/var/run链接损坏;- WSL 无法访问
/run; - MOTD 使用了错误路径。
MOTD 组件没有删除临时文件
进一步检查了临时文件的引用:
grep -RnsE 'motd\.new|could not install new MOTD' \
/etc/profile.d \
/etc/update-motd.d \
/usr/bin \
/usr/sbin \
/usr/lib \
2>/dev/null
结果显示,对 /var/run/motd.new 进行创建、写入和移动的主要程序是:
/usr/sbin/update-motd
同时,没有在 /etc/update-motd.d/ 的组件中发现主动删除或移动 /var/run/motd.new 的逻辑。
因此,可以排除某个普通 MOTD 组件主动删除临时文件的可能。
真正原因:并发实例争用同一个文件
/usr/sbin/update-motd 使用固定文件:
/var/run/motd.new
作为临时输出文件。
这个文件名并不包含:
- 进程 PID;
- 随机字符串;
- 用户标识;
- 会话标识。
这意味着,无论同时运行多少个 update-motd 实例,它们都会操作同一个文件。
当两个实例同时运行时,可能出现以下执行顺序。
实例 A 开始生成 MOTD:
A:创建 /var/run/motd.new
A:向 /var/run/motd.new 写入内容
实例 B 也开始生成 MOTD:
B:创建或截断 /var/run/motd.new
B:向同一个 /var/run/motd.new 写入内容
实例 A 先完成,并执行:
A:mv /var/run/motd.new /var/run/motd
此时,原来的:
/var/run/motd.new
已经被移动成:
/var/run/motd
实例 B 随后执行相同命令:
B:mv /var/run/motd.new /var/run/motd
但此时源文件已经被实例 A 移走,因此实例 B 得到:
mv: cannot stat '/var/run/motd.new': No such file or directory
随后 /usr/sbin/update-motd 输出:
ERROR: could not install new MOTD
这是一种典型的并发竞态,也就是多个进程在没有同步机制的情况下争用同一个共享资源。
为什么报错后 /run/motd 仍然存在
故障发生后,系统中仍然可以看到:
/run/motd
这并不矛盾。
它恰好说明至少有一个 update-motd 实例已经成功执行:
mv -f /var/run/motd.new /var/run/motd
另一个实例稍后再次执行 mv 时,因为源文件已经消失才发生报错。
因此,现场同时存在以下现象:
/run/motd 已经生成
以及:
mv: cannot stat '/var/run/motd.new'
这正是并发争用的典型特征。
/run/motd.dynamic.new 不是直接原因
排查时还发现:
/run/motd.dynamic.new
该文件属于另一套动态 MOTD 生成流程。
它与本次报错中的:
/var/run/motd.new
不是同一个文件,因此不能因为二者名称相似,就认定 /run/motd.dynamic.new 导致了当前故障。
本次错误的直接触发点仍然是:
mv -f /var/run/motd.new /var/run/motd
执行时,/var/run/motd.new 已经被另一个实例抢先移动。
flock 为什么能够解决问题
修复中加入的代码为:
if [ "${UPDATE_MOTD_LOCKED:-}" != 1 ]; then
export UPDATE_MOTD_LOCKED=1
exec /usr/bin/flock -x /run/update-motd.lock "$0" "$@"
fi
第一次进入 /usr/sbin/update-motd 时:
UPDATE_MOTD_LOCKED
尚未设置,因此脚本会:
- 设置
UPDATE_MOTD_LOCKED=1; - 请求
/run/update-motd.lock的排他锁; - 获得锁后重新执行
/usr/sbin/update-motd; - 保留原始命令行参数。
重新进入脚本后,因为已经存在:
UPDATE_MOTD_LOCKED=1
所以不会再次调用 flock,从而避免无限递归。
使用的参数:
-x
表示排他锁。任意时刻只允许一个实例持有该锁。
最终执行流程由原来的并发模式:
实例 A ──┐
├── 同时操作 /var/run/motd.new
实例 B ──┘
变为串行模式:
实例 A 获取锁
实例 A 生成并安装 MOTD
实例 A 退出并释放锁
实例 B 获取锁
实例 B 生成并安装 MOTD
实例 B 退出并释放锁
这样,每个实例都可以完整执行:
创建临时文件
生成 MOTD
移动临时文件
显示 MOTD
不会再发生一个实例提前移动另一个实例正在使用的临时文件。
为什么锁文件放在 /run
锁文件使用:
/run/update-motd.lock
/run 是 Linux 的运行时目录,适合保存:
- PID 文件;
- Socket 文件;
- 服务运行状态;
- 临时锁文件。
WSL 完全关闭后,/run 中的运行时数据会被重新创建,不会作为长期配置残留。
即使锁文件本身存在,也不代表锁一直被占用。真正的文件锁由内核维护;持锁进程退出后,锁会自动释放。
因此,将锁文件放在:
/run/update-motd.lock
符合运行时文件的使用方式。
0x04 结论
Ubuntu 24.04 WSL 启动时出现:
mv: cannot stat '/var/run/motd.new': No such file or directory
ERROR: could not install new MOTD
并不是 /var/run 路径损坏,也不是某个 MOTD 组件主动删除了临时文件。
真正原因是多个 update-motd 实例共用:
/var/run/motd.new
当它们并发执行时,其中一个实例先将临时文件移动为 /var/run/motd,另一个实例再次执行 mv 时便找不到源文件。
在 /usr/sbin/update-motd 入口处加入 flock 排他锁后,可以将所有调用串行化,从而避免多个实例同时操作同一个临时文件。
修复后,按照原始场景执行:
rm -f /root/.motd_shown
exit
再在 PowerShell 中执行:
wsl --shutdown
Start-Sleep -Seconds 3
wsl -d Ubuntu24.04
MOTD 可以正常显示,原有错误不再出现,说明修复生效。
对于只想解决问题的读者,按照“0x02 对策”操作即可;对于希望理解底层机制的读者,“0x03 缘由”则完整解释了故障发生和文件锁生效的过程。
0x05 参考
Ubuntu 官方文档:
https://help.ubuntu.com
Ubuntu Pro:
https://ubuntu.com/pro
Microsoft WSL 官方文档:
https://learn.microsoft.com/windows/wsl/
Ubuntu MOTD 入口脚本:
/etc/profile.d/update-motd.sh
Ubuntu MOTD 更新程序:
/usr/sbin/update-motd
Ubuntu MOTD 组件目录:
/etc/update-motd.d/
查看 flock 使用手册:
man 1 flock
查看 update-motd 使用手册:
man 8 update-motd
查看 run-parts 使用手册:
man 8 run-parts
0x06 后记
“知之者不如好之者,好之者不如乐之者。”
——《论语·雍也》
* 本文所记录的问题、排查过程及修复结果均来自真实环境,文字内容由 AI 协助组织编写,请读者结合自身环境验证并注意甄别。

654

被折叠的 条评论
为什么被折叠?



