GitHubのWSL/issuesを追う #8204 #10006
#8204
スリープから復帰したとき時刻がずれるという事象について書かれたissue
対処法は
- wslを一度シャットダウンして、もう一度起動する
- wslでsystemdを有効にして、timedatectlのConditionVirtualizationの設定を空に書き換える
の2つがありそう
現時点でのおすすめはsystemdを使う方法
wslのsystemd対応は2022年9月にアナウンスされたばかりでホットな話題
手元端末でも設定が完了したのでこれから動作を確認するつもり
systemdの設定方法一般については次の記事が詳しい
第598回 systemdユニットの設定を変える | gihyo.jp
原因について余裕があれば追っていきたいところだが
これまでissueを読んできた感じ、これに限らず、バグと判定されたissueとfixされたコミットが関連付けられていることがなく追うのが難しいと感じている
たまたま気になったissueがそうなだけなのかどこか別に管理されているのか確認したい
実はこのバグ自体は既に修正されていたバグである [1] [2]
私の手元端末でも修正を確認していたが、今は時刻がずれるようになってしまって 1. の対処法を何度も実施していた
systemdを有効することに少しためらいはあったがめんどくささが上回った
[1] https://github.com/microsoft/WSL/issues/5324#issuecomment-828418759
[2] https://learn.microsoft.com/ja-jp/windows/wsl/kernel-release-notes#510163
systemdを有効にしたことでデフォルトで追加されるタイマー(cronのsystemd版みたいな機能)の調べ方と無効にする方法を調べた
今までsystemdは無効だったのだからタイマーを無効にしても問題ないだろうという理屈
早く無効化したい人向け
すべてのタイマーを再起動後も無効にするコマンドを作りました
使用は、クリーンインストール直後推奨 自己責任でお願い致します
systemctl show '*' -ttimer -PId | sed '/^$/d' | sudo xargs -r systemctl mask
-ttimer、-PId の意味
まず、systemctlを用いたタイマーの調べ方
systemctl show '*' -ttimer -PId
または
systemctl list-timers
-ttimerと-PIdはそれぞれ以下のショートハンドです
--type timer
--value --property Id
また、ワイルドカード*
が勝手に展開されないようにシングルクォート'
で囲います
xargs -r の意味
xargs
の-r
オプションはパイプされてきた配列の長さが0件だったとき何もしないようにするためのオプション--no-run-if-empty
の省略形です
ストア版WSL(Ubuntu)をクリーンインストールした環境では
結果として以下の9つのタイマーを取得できました
- man-db.timer
- systemd-tmpfiles-clean.timer
- apt-daily.timer
- logrotate.timer
- ua-timer.timer
- motd-news.timer
- dpkg-db-backup.timer
- apt-daily-upgrade.timer
- e2scrub_all.timer
これらのタイマーの依存先サービスの状態はすべてstatic
だったのでsystemctl mask
で無効化します
systemctl mask
の意味するところや関連する話題についてはCentOS 7 で tmp fileを消してくれるserviceを停止する - さかもとのブログが具体例を交えての説明になっていてとても参考になりました
なお、マスク済の一覧が欲しいときはオプション--state masked
を使用するようです
$ wsl.exe --version
WSL バージョン: 1.0.3.0
カーネル バージョン: 5.15.79.1
WSLg バージョン: 1.0.47
MSRDC バージョン: 1.2.3575
Direct3D バージョン: 1.606.4
DXCore バージョン: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windowsバージョン: 10.0.22621.819
いまのところいい感じ
ただネットワークが切れていると更新されないのが落とし穴
そこだけ注意
1か月前に書き込んだ内容が誤っていたので訂正
誤:timedatectlのConditionVirtualization
正:systemd-timesyncdのConditionVirtualization
ところで、このConditionVirtualizationについて調べていくうちに
時刻同期はRTCかNTPどちらかが働いていればうまくいく程度の認識だったが
仮想化技術に応じてそれぞれ事情が異なるようだと認識を改めた
まとめると、確信がないので強くは言えないが
基本的にWSLがsystemdにおいてcontainer
にクラス分け(※)されている限りは
WSLは以下のような使い方しかできない
- 頻繁にWSL distroを上げ下げする環境
- 常時NTPサーバーへの接続が期待できて、WSL distro上で時刻同期デーモンを稼働させた環境
前者はコンテナのような使い方、後者は仮想マシンサーバーのような使い方という理解でいいかもしれない
なので、systemd-timesyncdのConditionVirtualizationを空にする方法は表だっておすすめしにくくなった
※man systemd.unit
のConditionVirtualizationの節を読んで man systemd-detect-virt
でクラス分けを確認
いろいろと調べた結果以下のことが分かった:
- systemd-timesyncdはNTPクライアント実装である
- systemd-timesyncdはRTCを書き換えないかもしれない
WSLを立ち上げるたびにエコじゃないトラフィックが発生してしまうのが気がかり
WSLの標準のkernelではMicrosoft Hyper-V guest supportが有効のようなので
そこ経由でうまくいく方法がないか模索してみたりしましたが技術不足で一旦保留
なので、systemd-timesyncdのConditionVirtualizationを空にする方法は私の環境ではそのままにして
なにか発生したときは報告しようと思う
クロック関連の問題が1つにまとめられた模様
気付いたらプレリリースも終わってリリースされていたので
最新バージョンにてテスト
PS> wsl cat /etc/wsl.conf
[boot]
systemd=false
PS> Get-Date -Format $rfc3339; wsl date --rfc-3339=ns; Get-Date -Format $rfc3339
2024-03-31 01:36:37.372073400+09:00
2024-03-31 01:36:38.363301685+09:00
2024-03-31 01:36:38.422570800+09:00
スリープからの復帰後:
PS> Get-Date -Format $rfc3339; wsl date --rfc-3339=ns; Get-Date -Format $rfc3339
2024-03-31 01:37:04.064881900+09:00
2024-03-31 01:37:04.629151311+09:00
2024-03-31 01:37:04.687666800+09:00
治った めでたい🎉
テスト用のコマンドはこちらの記事に書かれていたものを使用しました
確認したバージョン
WSL バージョン: 2.1.5.0
カーネル バージョン: 5.15.146.1-2
Windows バージョン: 10.0.22631.3296
気になっていた修正箇所ですが
このコミットで取り入れられていました
ソースコードの修正の仕方が分からない部分がほとんどだが、
ホストに対してゲストの遅れが5秒未満の場合は時刻補正されないらしいところは分かった。
数回スリープ復帰した後に同じコマンドテストしてみた結果、実機でも確認できました。
PS> Get-Date -Format $rfc3339; wsl date --rfc-3339=ns; Get-Date -Format $rfc3339
2024-03-31 02:36:16.176697000+09:00
2024-03-31 02:36:13.228902834+09:00
2024-03-31 02:36:16.361985400+09:00
WSL向けLinuxカーネルのソースコード中のこれってどこで設定するのか調べました。
module_param(timesync_implicit, bool, 0644);
modprobeを使うとどうやら設定できるみたいです。
ただ、WSLのデフォルトのカーネルではカーネルモジュールが組み込み済みなので
動的にパラメータを変更できない(lsmod
しても空が返ってくる)記載がありました[1]。
デフォルトのカーネルコンフィグはたぶんこの部分です。
CONFIG_HYPERV_UTILS=y
自前でカーネルをビルドするときにhv_utilをモジュールとしてビルドしてやればパラメータを設定できるはず。
修正内容にしきい値が設定されていることについて気になったので確かめた
ちゃんと検証したわけでないが、ホストの時刻設定(手動)とWSLの振る舞いから察すると
WSL上のhv_utilsの時刻調整は数秒に1回くらいの周期で動いていて
ホストの時刻を常にウォッチしている
これは
- ホストの時刻を手動で未来時刻に設定するとWSLの時刻も追従する
- WSLの時刻を手動で過去時刻に設定すると、即時または数秒後、ホストの時刻に上書きされる
ことから確認できる
WSLを5秒のずれも許さないようなようなシチュエーションで使う際には
systemd
を有効化して時刻を設定してあげたほうがいいかもしれない