LINUX.ORG.RU
ФорумTalks

Эту историю мы назовем «почему я разлюбил QEMU...»

 , ,


2

5

… или «как перестать бояться и разбить палаточный городок аккурат на минном поле».

В общем, ни для кого не секрет, что у нас тут семимильными шагами идет возврат от богомерзкого x86 к классической паре UNIX+RISC в ее современной реинкарнации: Linux+ARM. И так уже получилось, что на этот раз заход был не со стороны рабочих станций, а со всяких маломощных мобильников и малинок. Сбоку подают голос пользователи Apple Silicon, но их пока сравнительно мало.

Одновременно с этим, армов стало достаточно много, чтобы задуматься о том, как собирать под них софт. И если раньше для этого использовали кросс-компиляцию и всякие хаки, то сейчас у нас появилась прекрасная эмуляция вида qemu-arm-static с binfmt, исполняемые на x86. То есть, достаточно сделать чрут с армовой осью, а затем сказать ядру, чтобы для запуска бинарников с сигнатурой ARM он использовал qemu-arm-static - и вуаля, ваши армовые бинарники волшебным образом начинают работать на x86 машине, как родные.

Круто? Круто. Можно зачрутиться в систему, позапускать в ней всякие pacman и dpkg, чтобы получить корень, в котором затем выполнять сборку пакетов с помощью обычных makepkg, debuild и других дистротулов. Здорово? Здорово. И наплевать, что это медленнее кросс-сборок, потому что в разы проще и не требует от всего софта поддерживать префиксы для фейкового корня. А еще можно нативно повыполнять какие-то команды внутри чрута, чтобы получить корень, который затем можно скопировать на карту памяти вашего армового эмбеда.

Сейчас у нас есть как минимум два широко рапространенных инструмента для сборки чего-либо в эмуляции:

  • docker-buildx. Это такой docker-build на стероидах, который в том числе поддерживает сборку докер-образов для ARM (и других архитектур) на x86. Как? С помощью QEMU и binfmt, разумеется.
  • pi-gen с опцией USE_QEMU=1. Это официальная тулза для сборки образов Raspberry Pi. Суть почти та же: команды выполняются в чруте, чтобы получить образ для карты памяти.

Эти вещи удобны тем, что вы выполняете команды внутри вашей армовой ОС, как если бы работали непосредственно на ней. Особенно удобен докер, потому что докерфайл - это готовая документация по воспроизведению образа системы (при соблюдении определенных условий, но не суть).

В обоих случаях QEMU играет ключевую роль. И всё бы было хорошо, но он иногда падает. Либо сегфолтится, либо вылетает в какой-нибудь ассерт - разницы нет, суть в том, что эмуляция прерывается. Увы, но мой опыт активного использования qemu-arm-static показывает, что баги в нем хотя и фиксятся, но имеют свойство либо возвращаться с очередным мажорным релизом, либо что-то в ранее работающих воркфлоу может сломаться в новом неожиданном месте. Я не большой спец по QEMU, но таки делал репорты. Что-то исправлено, а что-то открыто до сих пор. Что хуже - поведение QEMU часто зависит от настроек хостовой ОС, и то, что вылетает на арче, не будет вылетать на дебиане.

«Не обновляй QEMU» - скажете вы. Да, но при очередном обновлении glibc в чруте, QEMU тоже может перестать работать из-за какого-нибудь нереализованного системного вызова, или редкого сочетания багов в glibc/QEMU. Поэтому можно угодить в ситуацию, когда старый QEMU уже не работает, а новый - еще не работает в вашем окружении.

Теперь, когда мы выяснили, что QEMU сам по себе не очень надежен, можно подумать - дескать, хрен бы с ним, ну падает и падает. В конце концов, безглючного софта не существует, и если QEMU упадет, то процесс просто прекратится, не так ли?

А вот нет.

Берем типичный сценарий современного использования QEMU - docker-buildx. В докерфайле у нас есть несколько команд RUN, которые выполняют всякие установки пакетов и прочие операции. А что происходит при установке пакетов, помимо распаковки и размещения файлов? Правильно, запускаются всякие хуки на баше. Спаунится пакетный менеджер, из него спаунится баш для скрипта, а из скрипта поочередно спаунятся команды. Коды возврата которых, разумеется, никто не проверяет. Ведь вряд ли у вас из под руда упадет какой-нибудь ls /, верно? Или, скажем, греп.

Но с QEMU они падают.

Вот что вы можете увидеть в эмуляции, пытаясь поставить пакет:

:: Proceed with installation? [Y/n]
:: Retrieving packages...
 openssl-3.1.4-1-aarch64 downloading...
 openssl-1.1-1.1.1.w-1-aarch64 downloading...
checking keyring...
checking package integrity...
loading package files...
checking for file conflicts...
:: Processing package changes...
upgrading openssl...
installing openssl-1.1...
error: command terminated by signal 11: Segmentation fault
:: Running post-transaction hooks...
(1/1) Arming ConditionNeedsUpdate...

Я мог бы привести еще пяток примеров, но я не сохранил их, потому что пишу этот пост ради срача, а не как научную статью.

Внутри пост-инсталл хука происходит сегфолт, но поскольку почти никто не ставит в скриптах set -e и не проверяет код возврата тривиальных (ха-ха) операций, установленный пакет будет находиться в неопределенном состоянии. И сборка будет продолжаться, потому что код возврата всего скрипта был нулевой! Пакет вроде поставлен, но корректно ли?

Эта проблема касается не только pacman и dpkg, но и вообще любых шелл-скриптов. Кто вообще может гарантировать, что во всех этих килопарсеках портянок, запускающихся в эмуляции, не будет вот таких непредсказуемых сбоев, на которые никто и никогда не делал тесткейс, потому что они, по мнению авторов скриптов, не могут сбойнуть?

Самое страшное тут то, что docker-buildx и сборки образов через QEMU сейчас получили повальное распространение из-за своей простоты. Вокруг нас может быть уже полно устройств с ОС, поломанными еще на этапе сборки. К чему это может привести - время покажет. Как минимум - к сложно детектируемым ошибкам у конечного пользователя.

Ну и самый интересный вопрос: хто виноват?

  • Ну, в меньшей степени виноват QEMU. Это отличная утилита, которая просто делает свою работу. Она не виновата в том, что ее начали использовать в настолько комплексных окружениях. В некоторых ситуациях она сегфолтится, в других - сознательно падает с какой-то диагностикой. Это лучшее поведение, какое только можно придумать в этом случае.
  • docker-buildx? На своем уровне он тоже делает, что может.
  • Баш-портянки? Пожалуй, они. Точнее, общая низкая культура их кода. Коды возврата не проверяются, фейлы пайпов не проверяются. Кушаем, как есть.
  • А может, виноваты диды? В своей бесконечной мудрости отцы UNIX сделали шелл, который ведет себя как сишечка: когда ты хочешь выстрелить себе в ногу, тебе услужливо подают патрон и усаживают в кресло. Ничто не мешало сделать неявную проверку кода возврата и выход из скрипта с ошибкой, и требовать явно эту ошибку игнорить при необходимости. Они предполагали, что этим будут пользоваться люди, которые знают, что делают, но теперь, спустя сорок лет, мы имеем в скриптах повальный чад кутежа.

А шо делать-то?

А ничего. Исправить все скрипты в мире не представляется возможным. Слепая установка set -e или ее аналоги на более низком уровне может привести к другим проблемам. Остаются два пути:

  • Использовать чистые кросс-сборки без эмуляции, как делали другие мудрые диды (мое почтение NetBSD c их тулчейном).
  • Использовать сорта чрутов (включая докер), но на реальном железе, а при нехватке производительности - подключать distcc.

В обоих случаях теряется удобство связки docker+QEMU, но за это удобство мы платим прямо сейчас щелчком взрывателя под жопой.

★★★★

Последнее исправление: liksys (всего исправлений: 2)

Насколько вообще есть смысл прям канпелять внутри эмулятора? Этож тормоза дикие.

алсо

семимильными шагами очередной убийца х86 стремится на кладбище убийц х86

token_polyak ★★★★
()
Последнее исправление: token_polyak (всего исправлений: 1)

Использовать чистые кросс-сборки без эмуляции, как делали другие мудрые диды (мое почтение NetBSD c их тулчейном).

А там тоже блуд. Мы с чуваками полгода ловили баг, который проявлялся как гонка под точно, мамой клянусь, берущимся локом. В итоге баг оказался в некогерентности процессоров в определенных условиях. Как только вылезает малопопулярное железо, начинаются приключения.

cumvillain
()
Последнее исправление: cumvillain (всего исправлений: 3)
Ответ на: комментарий от token_polyak

Ну расчёт на то, что «x86 большой, x86 всё сожрёт». К тому же довольно часто 16 ядер x86 уже есть, а 16 ядер ARM ещё найти и купить надо.

intelfx ★★★★★
()
Ответ на: комментарий от token_polyak

Насколько вообще есть смысл прям канпелять внутри эмулятора? Этож тормоза дикие.

Да, но если у тебя ядреный райзен, то уже наплевать. В основном речь даже не про компиляцию, а именно про бутстрап оси внутри чрута, который супер-удобен тем, что весь бутстрапящий код не должен явным образом поддерживать префикс для корня.

liksys ★★★★
() автор топика
Ответ на: комментарий от vbcnthfkmnth123

А нельзя ли взять QEMU, собрать его статически с musl и просто не обновлять эту версию?

Ты начнешь наступать на баги в старой версии QEMU :))))

cumvillain
()
Последнее исправление: cumvillain (всего исправлений: 1)
Ответ на: комментарий от vbcnthfkmnth123

Проблема не в нем, а в том, что внутри чрута libc мог измениться и начать использовать какой-то новый сисколл, о котором QEMU не знает. В лучшем случае придется городить костылис LD_PRELOAD и подсовывать свою реализацию сисклоа, в худшем - хер тебе, а не работающий QEMU.

liksys ★★★★
() автор топика

А почему бы вместо qemu-user использовать полную виртуализацию, то есть qemu-system? Да, тормоза дикие, но для бутстрапа сойдёт.

Werenter ★★★
()
Ответ на: комментарий от Werenter

Система должна быть в состоянии загрузиться, а минимальные образы не всегда это могут. Можно взять полную виртуализацию и чрутится внутри нее в минималку, но это просто ахтунг какой-то по монстроузности.

liksys ★★★★
() автор топика

возврат от богомерзкого x86 к классической паре UNIX+RISC

Нет в этом ничего классического. UNIX был изначально сделан под CISC процессоры: PDP-11 и чуть позже Vax. RISC чипы получили относительное распространение только к концу 80х.

в ее современной реинкарнации: Linux+ARM.

Современный Linux к классическому Unix относится примерно никак. Ты б ещё венду юнихом назвал.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 2)
Ответ на: комментарий от hateyoufeel

Нет в этом ничего классического. UNIX был изначально сделан под CISC процессоры, т.е. PDP-11 и чуть позже Vax.

Мало ли что там изначально было. После VAX пошли сплошные RISC, на них UNIX в основном и стал жить.

Современный Linux к классическому Unix относится примерно никак.

Ох уж эти мантры.

liksys ★★★★
() автор топика
Ответ на: комментарий от liksys

После VAX пошли сплошные RISC, на них UNIX в основном и стал жить.

После Vax разные версии евнухса стали жить вообще на чём угодно. Лулз в том, что в середине 80х самым популярным евнухсом по количеству установок был Xenix от Microsoft, и это в основном был x86.

Ох уж эти мантры.

Да нет никаких мантр. Просто от оригинального евнухса там пара сисколлов осталось да баш, а в остальном всё давно выкинули в современном софте и используют совершенно другой API и совершенно другие подходы к разработке.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от cumvillain

Современный Linux к классическому Unix относится примерно никак. Ты б ещё венду юнихом назвал.

define unix

Кстати да, было бы неплохо чтобы ТС это сделал. А то под этим словом имеют ввиду всё подряд, включая загрузку в консоль по дефолту и наличие порта баша.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

После Vax разные версии евнухса стали жить вообще на чём угодно.

И в том же серверном сегменте в основном это были сорта RISC.

Да нет никаких мантр.

Linux - это идейный, а главное живой наследний UNIX. Не играет роли, была ли там когда-то общая кодовая база со старыми UNIX, главное - подход.

Это - факт.

Кстати да, было бы неплохо чтобы ТС это сделал.

Успокойся, фанбой. Статья вообще не об этом. Можешь считать, что речь идет о UNIX-подобных ОС, если это не будет оскорблять твоих религиозных чувств.

liksys ★★★★
() автор топика
Последнее исправление: liksys (всего исправлений: 2)
Ответ на: комментарий от liksys

И в том же серверном сегменте в основном это были сорта RISC.

Ну да. В конце 80х, как я и написал.

Linux - это идейный, а главное живой наследний UNIX. Не играет роли, была ли там когда-то общая кодовая база со старыми UNIX, главное - подход.

Подход в люниксе к евнухсу отношения не имеет вообще никакого. Люникс сейчас гораздо ближе к венде в этом плане (нет, я не говорю, что это плохо). Или что именно ты имеешь ввиду под «подходом»? Расскажи нам.

Можешь считать, что речь идет о UNIX-подобных ОС

Что такое «UNIX-подобная ОС»?

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Мне не интересно с тобой сраться о твоих верованиях.

По теме есть что сказать? Или история юниксов - это предел твоих компетенций?

liksys ★★★★
() автор топика
Ответ на: комментарий от liksys

Мне не интересно с тобой сраться о твоих верованиях.

Причём тут верования? Мне реально интересно, что люди имеют ввиду под этим термином.

По теме есть что сказать? Или история юниксов - это предел твоих компетенций?

Ага. Ты кросскомпиляцию и кросс-установку пакетов не осилил. Тот же apt в Debian позволяет ставить пакеты для другой архитектуры. Иди маны почитай. Если тебе для сборки контейнеров ничего кроме этого не нужно, то qemu из этой связки можно без проблем выкинуть.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Ага. Ты кросскомпиляцию и кросс-установку пакетов не осилил. Тот же apt в Debian позволяет ставить пакеты для другой архитектуры. Иди маны почитай. Если тебе для сборки контейнеров ничего кроме этого не нужно, то qemu из этой связки можно без проблем выкинуть.

Если надо собрать проект в духе

GOOS=linux GOARCH=arm64 go build ...

то да. Если там дерево из десятков RPM пакетов разной степени кривости, то нет.

cumvillain
()
Последнее исправление: cumvillain (всего исправлений: 1)
Ответ на: комментарий от cumvillain

Если там дерево из десятков RPM пакетов разной степени кривости, то нет.

Я не знаю про RPM, но с apt несколько лет назад проблем это сделать вообще не было.

Хотя RPM то ещё говно, поэтому я не удивлюсь, если там и это сломано. Red Hat вообще ничего нормально сделать не могут.

hateyoufeel ★★★★★
()

Ага. У нас тоже была такая херня, что на билдсервере под arm64 падала установка пакетов. Дешевле всего оказалось воткнуть хостом мейнстримовый дебиан и не выпендриваться.

HE_KOT
()
Последнее исправление: HE_KOT (всего исправлений: 1)

Ну ты как военкоры какие-то. Это в скриптлетах плохо, то в скриптлетах плохо, и вот это в скриптлетах плохо, а потом в конце виноват почему-то кто угодно, кроме идеи скриптлетов.

t184256 ★★★★★
()
Последнее исправление: t184256 (всего исправлений: 1)
Ответ на: комментарий от t184256

Чойта. Скрипты и виноваты. А еще виноваты создатели скриптозапускалок, у которых дефолтное поведение - это игнорирование ошибок.

liksys ★★★★
() автор топика
Ответ на: комментарий от liksys

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

t184256 ★★★★★
()
Ответ на: комментарий от t184256

Да. Но я еще к тому виду, что «дизайн» скриптов сам по себе сломан. Всегда нужно падать при ошибках, а они их замалчивают.

liksys ★★★★
() автор топика
Последнее исправление: liksys (всего исправлений: 1)
Ответ на: комментарий от t184256

Я именно о них и писал. Это при том, что у нас есть нормальные скриптовые языки, типа перла и питона, в которых есть все средства для вызова внешних команд и проверки их значений.

Все сколько-нибудь сложные скрипты следует писать на нормальных языках, а не на шелле. Шелл defective by design. Буквально мина замедленного действия, которая постоянно срабатывала во всех проектах, и в очередной раз сработала уже с QEMU.

liksys ★★★★
() автор топика
Последнее исправление: liksys (всего исправлений: 1)
Ответ на: комментарий от liksys

Да при чем тут QEMU? У меня сегодня batch-job боком пошёл, потому что кое-кто в баш-портянке не обработал сценарий аппаратного сбоя при вызове df. Без всякого там QEMU.

t184256 ★★★★★
()
Последнее исправление: t184256 (всего исправлений: 1)
Ответ на: комментарий от t184256

Мы чет на разных языках говорим) Портянки сами по себе генератор проблем, но из-за QEMU они начинают сбоить в еще более непредсказуемых местах. Это просто фактор, увеличивающий вероятность отказа.

liksys ★★★★
() автор топика

Ну, наверно, не нужно glibc обновлять? Это ж libc, база.

Shadow ★★★★★
()
Ответ на: комментарий от liksys

внутри чрута libc мог измениться и начать использовать какой-то новый сисколл

STABLE APİ İS NONSENSE
thunar ★★★★★
()
Ответ на: комментарий от thunar

Наброс тупой. В линуксе старые сисколлы не убирают. Могут добавляться новые, но при этом они не ломают обратную совместимость, а прямой совместимости вообще практически нигде нет.

Werenter ★★★
()
Ответ на: комментарий от liksys

в которых есть все средства для вызова внешних команд и проверки их значений.

А в баше уже выпилили ?

anc ★★★★★
()
Ответ на: комментарий от t184256

в баш-портянке не обработал сценарий аппаратного сбоя при вызове df.
аппаратного сбоя
аппаратного

Какие же «нехорошие люди»… Полностью поддерживаю, баш это зло! Даже не умеет с умершим железом работать. :(

anc ★★★★★
()
Ответ на: комментарий от hateyoufeel

PDP-11 и чуть позже Vax

Там между ними был ещё период «классичности» M68k.

GAMer ★★★★★
()
Ответ на: комментарий от token_polyak

Насколько вообще есть смысл прям канпелять внутри эмулятора? Этож тормоза дикие.

Если нет кросскомпилятора.

Meyer ★★★★★
()
21 марта 2024 г.

Просто надо было ставить Gentoo.

keeper_b ★★★★
()

а чем OBS не устраивает?

kott ★★★★★
()

Странно, что только ты обратил внимание на эту проблему. Ты комп не пробовал менять?

Puzan ★★★★★
()
Ответ на: комментарий от Puzan

Пробовал, конечно.

Странного ничего нет. Условия воспроизведения специфические, не со всеми хостами может работать, не на всех glibc случается. Плюс, многие ли будут читать логи «успешной» сборки софта в своих CI, если на первый взгляд все работает? Думаю, нет.

Проблема-то фундаментальная, на самом деле.

liksys ★★★★
() автор топика
Последнее исправление: liksys (всего исправлений: 1)
Закрыто добавление комментариев для недавно зарегистрированных пользователей (со score < 50)