
Комментарии 8
Google отключил io_uring не из-за производительности – из-за attack surface. В 2021–2023 годах серия CVE показала: shared memory ring между user-space и ядром – это плоскость атаки, которой у epoll просто нет. Когда untrusted код в sandbox делает io_uring-операцию, он получает прямой контакт с кольцом ядра, а не проходит через strace-видимые syscall-границы. Именно поэтому io_uring отключён в Android, ChromeOS и в дефолтных политиках многих SELinux-профилей – не потому что медленный.
В своё время Windows пыталась заигрывать с передачей данных прямо в user-mod (работа со звуком: пользователь выделяет память от винды (не куча) с правильными флагами и далее винда позволяет драйверам работать с этой памятью напрямую). Всё это вполне работало, редко-редко синий экран мог вылететь :)
НО: Как только начали минимально думать про безопасность - всё это быстренько свернули.
Складывается впечатление, что "скорость работы" и "удобство работы" (usermod и т.д.) - они в противоположных сторонах. Возможно, там, где нужна скорость - всё уйдёт в kernelmod? Какие-нибудь аналоги eBPF или полноценные модули ядра?
Раньше уходило в kernel mode (или вообще exoOS, где приложение было основной частью ядра). Сейчас наверное больше в сторону вообще отказа от kernel - типа-userspace, но максимально обособленно от ядра (DPDK, SPDK, залоченная память, прибитые к kernel-изолированным CPU ядрам потоки и вот это все)
Мне казалось, ASIO так и работает, нет? И является стандартом де факто для музыкальной индустрии.
Granulex, всё верно, спасибо за уточнение. В статье я сжал этот пункт, а зря - тема заслуживает развёрнуто.
Соглашусь по всем тезисам. Главная боль действительно не в перформансе, а в архитектуре доверия: shared ring между userspace и ядром стирает классическую syscall-границу, на которой раньше держалась львиная доля seccomp/SELinux-политик. Любая seccomp-фильтрация по номеру сискола после io_uring_enter теряет смысл - реальную операцию ядро уже взяло из SQE и проводит сама, минуя обычный путь.
Из конкретики, которая подкрепляет тезис: в Android начиная с 14 io_uring запрещён через seccomp по умолчанию для непривилегированных приложений; в ChromeOS он отключён в sandboxed renderer-процессах с 2023, после серии CVE; Project Zero в 2023 разбирали, как из найденных kernel exploit chains большинство пошло именно через io_uring - просто потому, что attack surface для непривилегированного процесса разом вырос на сотни новых kernel paths. В RHEL 9 он тоже отключён под дефолтной SELinux-политикой для сервисов, которым явно не разрешён.
То есть формулировка ровно та, к которой пришла индустрия: io_uring - инструмент привилегированных процессов в trusted-окружении. На мультитенантных хостах с непроверенным кодом его надо явно вырезать.
Если интересно, могу разобрать в отдельной заметке, как именно ломается seccomp-модель на io_uring - там есть несколько неочевидных моментов с регистрацией fd через IORING_OP_OPENAT2 в обход политик файловой системы.
тобы понимать, что io_uring - не новое явление, а догоняющий ход в долгой эволюции, полезно посмотреть на соседей. Идея completion-based async I/O старше readiness-based лет на двадцать.
Не на двадцать, а очень-очень-очень намного больше. В частности, асинхронный ввод-вывод с уведомлением приложения о завершении операции -- основа ввода-вывода в OS/360, а её разработка началась ещё в первой половине 1960-х одновременно с разработкой самих машин -- первых мэйнфреймов Системы 360; первая, жутко обрезанная версия стала доступна пользователям в 1966-м. Правда, в прикладных программах обычно пользовались синхронным вводом-выводом, поскольку это проще для программиста, но асинхронщина была всегда доступной и использовалась, когда это было нужно.
Асинхронный ввод-вывод -- основа и "матери" Винды (VAX/VMS), и её "бабки" (RSX-11M), а это -- 1970-е годы.
На typical сетевой нагрузке epoll часто остаётся правильным выбором.
может уже сразу надо всю статью write на English?
Отличная статья! Читается на одном дыхании.
Хочется уточнить
1)В контексте registered buffers вы упоминаете IORING_REGISTER_BUFFERS как важную оптимизацию, но при O_DIRECT нужны буферы выровненные по 4096 байт. Как вы организуете pool таких буферов: держите pre-allocated slab из выровненных регионов и раздаёте slice'ы, или каждый раз выделяете отдельно?
И второй момент — io_uring_register_buffers_update (partial update без полного unregister) — вы его щупали, или в вашей практике проще пересоздать весь pool целиком?
2)ScyllaDB/Seastar — один из главных референсов по thread-per-core + io_uring. Они утверждают drop p99 latency на NVMe до 80 мкс при этой модели. Вы сравнивали свои цифры с их публичными бенчмарками? И вот что интересно: у Seastar своя реализация io_uring поверх голого syscall без liburing — как думаете, это реально даёт прирост по сравнению с liburing/monoio, или это больше историческое наследие (Seastar старше liburing)?
3)По грабли №2 (cancel race condition): "Вы используете IOSQE_IO_LINK для cancel — как обрабатываете случай, когда link chain ломается по ошибке на первой операции? ECANCELED на второй SQE в этом случае приходит или нет?
io_uring без розовых очков: 5 граблей, которые сожгли мне неделю, и где он реально быстрее epoll