Мы активно пользуемся WSL2 для того, чтобы открывать линуксовые коры в Visual Studio. Для обеспечения консистентности символов и коры мы монтируем squashfs образ, созданный в целевой системе. После очередного апдейта целевого дистрибутива, у нас всё сломалось - squash монтировался, но мог посередине файла выдать ошибку чтения, записав в dmesg что-то типа

[ 17.157892] SQUASHFS error: xz decompression failed, data probably corrupt
[ 17.158705] SQUASHFS error: Failed to read block 0x5c3cb5e: -5

Естественно, точно такой же сквош на целевой системе работал как часы и никаких ошибок не выдавал.

По мере исследования выяснилось, что ядро WSL не включает опции ядра, связанные с BCJ фильтрацией в XZ.

BCJ фильтр

Алгоритм XZ это на самом деле LZMA плюс интервальное кодирование как разновидность арифметического. В свою очередь, LZMA можно очень упрощенно описать как замену одинаковых участков сжимаемого текста на ссылки 'назад' - сжатие по словарю, а интервальное кодирование - как выбор самого короткого числа, описывающего сжимаемый текст с точки зрения вероятностей отдельных символов в месте их появления.

Поскольку используется словарное сжатие LZMA, важно иметь много одинакового текста. А в исполняемых файлах много вызовов библиотечных функций. Эти вызовы выглядят примерно так:

   0x000000000040112a <+4>:	    bf 10 20 40 00	mov    $0x402010,%edi
   0x000000000040112f <+9>:	    e8 fc fe ff ff	call   0x401030 <puts@plt>
   0x0000000000401134 <+14>:	bf 1c 20 40 00	mov    $0x40201c,%edi
   0x0000000000401139 <+19>:	e8 f2 fe ff ff	call   0x401030 <puts@plt>

Два вызова puts() в листинге выше используют инструкцию call relative to next instruction но на самом деле указывают на одно и то же место. Если бы мы нашли все такие вызовы puts() и поменяли их с относительного адреса на абсолютный, то у нас было бы очень много одинаковых пятибайтных кусочков. То же самое справедливо для инструкций ветвления (Branch) и безусловных переходов (Jump) - отсюда и название BCJ фильтра - Branch/Call/Jump.

Естественно, такой способ подходит только для исполняемого кода и замедляет обработку - например, при тестировании сборки Fedora Linux за 30 мб cжатия от 1.7 Гб образа заплатили 18 минутами распаковки (примерно в два раза медленнее). Но кому-то и такое может зайти. Дефолт такой дефолт.

Впрочем, раз squashfs по дефолту собирается с BCJ фильтром, надо соответствующие опции включить в ядре. А для этого ядро надо пересобрать.

Сборка ядра

В качестве подопытного кролика будет выступать AlmaLinux 9 - но не так сложно найти гайды по пересборке ядра в базовой для WSL Ubuntu. В общем и целом процесс отличается от "обычной" сборки ядра для линукса только файлом настроек.

Внимание!
В отличие от линукса, NTFS регистронезависим, и поэтому могут быть проблемы с доступом к исходникам во время компиляции! Все действия этого раздела проводятся внутри WSL2 на собственной файловой системе!

Поставим всё, что для сборки понадобится:

sudo dnf install --enablerepo=crb kernel-devel bc \
   git dwarves ncurses-devel binutils{,-gold} rsync kmod

kernel-devel это немножко перебор - но зато ставит большую часть инструментария для сборки ядра.
ncurses-devel это для menuconfig - текстового интерфейса конфигурации ядра.

Скачаем исходники ядра с настройками "от Микрософт"

git clone https://github.com/microsoft/WSL2-Linux-Kernel.git --depth=1 -b linux-msft-wsl-5.15.y
cd WSL2-Linux-Kernel

На момент публикации основная ветка - linux-msft-wsl-5.15.y, следующий кандидат - linux-msft-wsl-6.1.y.
Если нужно конкретное ядро - есть таги вида linux-msft-wsl-<maj>.<min>.<x>.<y> - например, linux-msft-wsl-6.1.21.2

Файл с настройками ядра можно найти в Microsoft/config-wsl.
Теперь нужно изменить эти настройки - руками в файле или через menuconfig

make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl

Конкретно для работы BCJ было достаточно добавить

CONFIG_XZ_DEC_X86=y

но в ядре есть и опции для BCJ фильтрации кода других архитектур.

Для точного определения какое ядро используется можно поменять суффикс локальной версии (General setup -> Local Version) и/или добавить git sha к суффиксу (General setup -> Automatically append version information).

Дальше - сборка ядра и установка модулей.
Базовая настройка от Микрософта не имеет настроек компиляции в модули, но вдруг именно вам надо?
Заголовки ядра тоже вещь очень на любителя.

make -j $(nproc) KCONFIG_CONFIG=Microsoft/config-wsl
make modules_install headers_install

Предупреждение - мне ни модули ни хедеры не пригодились, поэтому привожу команду только для полноты картины. Возможно, потребуется что-то ещё чтобы модули загружались автоматически.

Установка и проверка нового ядра

Если make закончился успешно, то новое ядро можно найти в arch/x86/boot/bzImage. Для использования в WSL его надо вытащить наружу

mkdir -p /mnt/c/Users/<USER>/.wsl-kernels/
cp arch/x86/boot/bzImage /mnt/c/Users/<USER>/.wsl-kernels/

WSL2 использует конфигурационный файл .wslconfig для юзероспецифичных настроек WSL.
Для использования своего ядра надо туда записать

[wsl2] 
kernel=C:\\Users\\<USER>\\.wsl-kernels\\bzImage

потом перезапустить подсистему WSL

wsl --shutdown
wsl

и потом изнутри WSL проверить какое ядро запущено

uname -r

Дополнительные ссылки

Ядерная реализация XZ в линуксе взята из https://github.com/tukaani-project/xz-embedded

LZMA - https://neerc.ifmo.ru/wiki/index.php?title=Алгоритм_LZMA, https://www.7-zip.org/sdk.html

интервальное кодирование - https://ru.wikipedia.org/wiki/Интервальное_кодирование

Другие маны по сборке ядра для WSL2:
https://github.com/Zhoneym/WSL-Docs/blob/main/WSL/compile-wsl2-kernel.md
https://alexkaouris.medium.com/run-your-own-kernel-with-wsl2-21e3143e014e

Комментарии (2)


  1. fshp
    06.06.2024 03:22
    +7

    В NTFS можно включить case sensitive для отдельных директорий


    1. win32asm Автор
      06.06.2024 03:22

      Спасибо, не знал, попробую.