27 октября 2026 года сопровождающий Dolphin Méven Car рассказал в блоге KDE, как закрыл один из самых старых и неприятных багов KIO: копирование файлов на флешку заканчивалось раньше, чем данные реально попадали на носитель. Исправление войдёт в KDE Frameworks 6.32, выход которой запланирован на 13 ноября.
KIO это подсистема ввода-вывода KDE: через неё копирует файлы Dolphin и другие приложения KDE (на ней же построен, например, KIO Snapshot для снимков Btrfs). Так что изменение касается всех, кто пишет файлы на флешки, карты памяти и сетевые папки из KDE.
Почему полоска копирования обманывала
Ядро Linux не пишет данные на носитель сразу. Вызов write() возвращает управление, как только данные легли в страничный кэш в оперативной памяти, а на устройство они уходят позже, когда ядро сочтёт нужным. На локальном SSD это «позже» наступает почти мгновенно, и разницы никто не замечает. На медленной флешке разница огромная.
Поэтому полоска в уведомлении о копировании показывала, как быстро файл заполняет память, а не как он пишется на флешку. В примере автора файл на 128 МиБ на флешку USB 2.0 со скоростью записи 2,3 МБ/с «копировался» за доли секунды (от 0,06 до 0,13 с), а реально оказывался на носителе только спустя полторы минуты (от 68 до 91 с). Если выдернуть флешку в эту минуту, данные пропадут.
С перемещением было ещё хуже: KIO удалял исходный файл сразу после копирования, когда копия могла лежать только в памяти. Именно из-за этого в 2021 году Nate Graham поднял багу высший приоритет.
Баг 281270: пятнадцать лет в одном отчёте
Жалобу на это открыли 3 сентября 2011 года, ещё на KDE 4.7: баг 281270 «скорость и прогресс копирования на съёмный диск отражают запись в локальный кэш, а не на сам диск». Автор отчёта заметил, что уведомление показывало около 50 МБ/с при копировании на устройство USB 1.1, которое физически не умеет быстрее 12 Мбит/с. За пятнадцать лет в отчёте набралось 77 комментариев и 17 дубликатов.
Нужный инструмент назвали почти сразу. Через две недели автор отчёта передал совет разработчика ядра Alan Stern использовать sync_file_range(), и Christoph Feck ответил, что это «ровно то, что нужно». Довести идею до рабочего кода удалось только сейчас. Сам Méven Car дважды пробовал в 2024 году, но ждал записи не того куска файла, и ожидание фактически не срабатывало. Предложение просто отключить кэш при копировании (MR 2162) закрыли.
Почему нельзя было просто вызвать fsync()
Сделать копирование честным легко: вызывать fsync() после каждого файла или открывать файл с флагом O_DIRECT в обход кэша. Но оба способа замедляют копирование в разы, причём и на быстрых локальных дисках, где проблемы не было. По замерам автора, fsync() на каждый файл растянул копирование 3000 файлов по 16 КиБ на локальный диск с 0,04 секунды до 10 секунд. Вся сложность была в том, чтобы сделать копирование честным и не медленным.
Что меняется в KIO из Frameworks 6.32
Исправление состоит из четырёх merge request’ов (MR) в KIO:
- MR 2506 определяет, какие устройства съёмные;
- MR 2517 и MR 2518 сбрасывают данные на устройство в конце копирования и перемещения;
- MR 2283 добавляет окно обратной записи (writeback window).
Окно работает так. Данные пишутся порциями. Когда порция заполнена, отдельный поток запускает её запись на устройство через sync_file_range() и ждёт окончания, а копирование тем временем заполняет следующую. В памяти одновременно лежит не больше двух порций, поэтому полоска опережает реальную запись максимум на одну. Размер порции подстраивается под скорость носителя так, чтобы её запись занимала около 200 мс: он начинается с 1 МиБ и растёт, но не выше 16 МиБ (на CIFS до 128 МиБ).
В конце копирования на съёмный носитель KIO вызывает один syncfs() для всей файловой системы флешки и, пока он идёт, показывает уведомление «Запись скопированных данных на устройство…». Файлы крупнее 16 МиБ дополнительно сбрасываются через fsync() вместе с папкой. Если сброс не удался, копирование считается неудачным: данных на носителе может не быть. При перемещении исходные файлы теперь удаляются только после успешного сброса, а если флешку выдернули на этом шаге, все исходники остаются на месте.
Скорость самой записи на флешку не изменилась, полоска просто стала честнее. Время на часах выросло только потому, что в него теперь входит запись на носитель, а момент, когда данные реально на флешке, не сдвинулся. Обычные несъёмные диски работают как раньше: даже с принудительно включённым новым режимом 3000 мелких файлов копировались 1,55 секунды против 1,54.
Понять, что носитель съёмный, оказалось непросто. По файловой системе этого не видно: exFAT бывает на карте памяти, а FAT стоит и на EFI-разделе ноутбука. Большинство USB-дисков вообще не сообщают, что они съёмные. Поэтому KIO смотрит в sysfs: флаг самого диска, USB-порт или слот PCIe, через который он подключён, SD-карты. Для зашифрованных томов и LVM он спускается до физического диска.
Сетевые папки: NFS, SMB и другие
У сетевых файловых систем причина та же, но симптом другой. При копировании большого файла на NFS или SMB в памяти оставались сотни мегабайт, которые ещё предстояло отправить на сервер, и прогресс сильно убегал вперёд. На SMB с настройкой по умолчанию cache=strict ещё и тормозил просмотр папки: обращение к файлу ждёт, пока его данные уйдут на сервер. На канале 5 МБ/с почти каждый листинг папки во время копирования 1 ГиБ ждал от 15 до 20 секунд.
Теперь окно обратной записи получают все монтирования, которые KIO и так считает медленными: NFS, CIFS, Ceph, GlusterFS, 9p, sshfs и FUSE-системы вроде rclone. В замерах автора на SMB-ресурсе с ограничением 5 МБ/с листинг папки во время копирования 256 МиБ занимал 1,7 секунды вместо 11,3 и 5,5 секунды в двух прогонах, а прогресс опережал сервер не больше чем на 1 МиБ вместо 256 МиБ. Само копирование стало медленнее примерно на 3%. На NFS копирование в одном прогоне было на 11% медленнее, в двух других чуть быстрее.
Это отчасти помогает и с зависаниями Dolphin на медленных сетевых папках, которые собраны в баге 423187: запросы листинга теперь ждут не больше двух порций записи. Остальные причины зависаний разбирают в отдельных MR.
Бонус: копирование между разными ФС
Ещё одно изменение в 6.32 (MR 2520) просто ускоряет копирование между разными файловыми системами. Начиная с Linux 5.19 copy_file_range() между ФС снова возвращает ошибку EXDEV, и KIO в таком случае копировал через read() и write() по кусочку. Теперь он переходит на sendfile(), который копирует данные внутри ядра. На флешку это не ускорит: там упор в скорость записи самого носителя.
Как быть до выхода Frameworks 6.32
По расписанию KDE версию 6.32 пометят тегом 6 ноября, а опубликуют 13 ноября. В дистрибутивы она приедет в разное время вместе с обычными обновлениями пакетов, отдельно ставить Frameworks не нужно. Что вошло в прошлый выпуск, я разбирал в заметке про KDE Frameworks 6.31. Узнать свою версию можно в «Информации о системе» (KInfoCenter) или в терминале:
kinfo | grep "KDE Frameworks"
Пока стоит версия старше 6.32, полоске копирования на флешку верить нельзя. Перед тем как вынуть флешку, используйте «Безопасное извлечение» в Dolphin или апплете «Диски и устройства»: носитель отмонтируется только после того, как кэш будет записан. В терминале дождаться окончания записи можно командой sync, а сколько данных ещё ждёт записи, видно в /proc/meminfo:
sync grep -E "^(Dirty|Writeback):" /proc/meminfo
Пятнадцатилетний баг закрыт: с KDE Frameworks 6.32 копирование на флешку в Dolphin заканчивается тогда, когда данные действительно записаны, а на сетевых папках прогресс идёт вровень с сервером и папка не замирает во время копирования. Как пишет автор, sync_file_range() обычно используют базы данных и движки хранения, а других файловых менеджеров с этим вызовом он не знает.
