修复 Ubuntu 24.04 WSL 启动时 `mv: cannot stat ‘/var/run/motd.new’: No such file or directory

修复 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

也就是:

  1. 创建临时文件 /var/run/motd.new
  2. 执行 /etc/update-motd.d/ 中的脚本;
  3. 将所有输出写入 /var/run/motd.new
  4. 生成完成后,将其移动为 /var/run/motd
  5. 显示最终 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

尚未设置,因此脚本会:

  1. 设置 UPDATE_MOTD_LOCKED=1
  2. 请求 /run/update-motd.lock 的排他锁;
  3. 获得锁后重新执行 /usr/sbin/update-motd
  4. 保留原始命令行参数。

重新进入脚本后,因为已经存在:

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 协助组织编写,请读者结合自身环境验证并注意甄别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值