Closed13

GitHubのWSL/issuesを追う #8204 #10006

mars2nicomars2nico

#8204

スリープから復帰したとき時刻がずれるという事象について書かれたissue

対処法は

  1. wslを一度シャットダウンして、もう一度起動する
  2. 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

mars2nicomars2nico

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
mars2nicomars2nico

いまのところいい感じ
ただネットワークが切れていると更新されないのが落とし穴
そこだけ注意

mars2nicomars2nico

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でクラス分けを確認

mars2nicomars2nico

いろいろと調べた結果以下のことが分かった:

  • systemd-timesyncdはNTPクライアント実装である
  • systemd-timesyncdはRTCを書き換えないかもしれない

WSLを立ち上げるたびにエコじゃないトラフィックが発生してしまうのが気がかり
WSLの標準のkernelではMicrosoft Hyper-V guest supportが有効のようなので
そこ経由でうまくいく方法がないか模索してみたりしましたが技術不足で一旦保留

なので、systemd-timesyncdのConditionVirtualizationを空にする方法は私の環境ではそのままにして
なにか発生したときは報告しようと思う

mars2nicomars2nico

プレリリースだけど修正されたそう

https://github.com/microsoft/WSL/issues/10006#issuecomment-1904756227

ICTIMESYNCFLAG_SYNC についての調べ方(メモ)

ここでHyper-Vのゲストについての時刻同期の基本的な考え方を理解して
ここadj_guesttime関数のコメントを読むとよさそう
そして次どうしたらいいのかわからない

リリースの内容をみるにどうも暗黙的に(レストア時に)時刻同期するものが追加された模様
一応 hv_utils.c を修正していそうなメーリングリストのアーカイブにはたどり着いた

想像するに暗黙的にするかどうかをカネールコンフィグで切り替えられる感じなのかな
そこまで調べられなかった

mars2nicomars2nico

気付いたらプレリリースも終わってリリースされていたので
最新バージョンにてテスト

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

治った めでたい🎉

テスト用のコマンドはこちらの記事に書かれていたものを使用しました
https://qiita.com/moriai/items/f633e01728d8d062adb1#どんな感じか

確認したバージョン

WSL バージョン: 2.1.5.0
カーネル バージョン: 5.15.146.1-2
Windows バージョン: 10.0.22631.3296

mars2nicomars2nico

ソースコードの修正の仕方が分からない部分がほとんどだが、
ホストに対してゲストの遅れが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
mars2nicomars2nico

WSL向けLinuxカーネルのソースコード中のこれってどこで設定するのか調べました。

drivers/hv/hv_util.c
module_param(timesync_implicit, bool, 0644);

modprobeを使うとどうやら設定できるみたいです。

ただ、WSLのデフォルトのカーネルではカーネルモジュールが組み込み済みなので
動的にパラメータを変更できない(lsmodしても空が返ってくる)記載がありました[1]

デフォルトのカーネルコンフィグはたぶんこの部分です。

WSL2-Linux-Kernel/arch/x86/configs/config-wsl
CONFIG_HYPERV_UTILS=y

自前でカーネルをビルドするときにhv_utilをモジュールとしてビルドしてやればパラメータを設定できるはず。

脚注
  1. lubuntu - lsmod command not working in windows subsystem for linux - Ask Ubuntu ↩︎

mars2nicomars2nico

修正内容にしきい値が設定されていることについて気になったので確かめた

ちゃんと検証したわけでないが、ホストの時刻設定(手動)とWSLの振る舞いから察すると
WSL上のhv_utilsの時刻調整は数秒に1回くらいの周期で動いていて
ホストの時刻を常にウォッチしている
これは

  • ホストの時刻を手動で未来時刻に設定するとWSLの時刻も追従する
  • WSLの時刻を手動で過去時刻に設定すると、即時または数秒後、ホストの時刻に上書きされる

ことから確認できる

WSLを5秒のずれも許さないようなようなシチュエーションで使う際には
systemdを有効化して時刻を設定してあげたほうがいいかもしれない

このスクラップは2024/03/31にクローズされました