Глубины ввода-вывода Windows (часть 5) — внутреннее устройство NTFS: файловая система через MFT
· Го Комура · Windows, NTFS, I/O, Файловая система, MFT, Ядро, .NET, Расследование ошибок
За четыре предыдущие части мы проследили, как течёт запрос I/O (части 1–3) и как его принимает диспетчер кэша (часть 4). В конце запрос приходит в файловую систему. На этот раз очередь её главного представителя — NTFS.
Точка зрения меняется. До сих пор речь была динамическая — поток запроса. Теперь статическая история о том, как данные лежат на диске. Суть невидимого «Zone.Identifier» у скачанного файла. Почему копирование десяти тысяч мелких файлов гораздо медленнее одного файла того же суммарного размера. Насколько правда «NTFS журналируемая — значит спокойно» — всё объясняется из этой структуры.
Это часть 5 серии «Глубины ввода-вывода Windows».
Предпосылка этой части: базовые IRP и стек устройств части 1 делают чтение легче. Но чтобы читать и отдельно, термины предыдущих частей сначала определим.
| Термин | Одной строкой | Подробнее |
|---|---|---|
| IRP (I/O Request Packet) | «Накладная запроса I/O», в которую внутри ядра превращается вызов API вроде ReadFile. Драйвер принимает эту накладную и обрабатывает |
Часть 1 |
| Диспетчер I/O и стек устройств | Часть ядра, которая делает IRP и по очереди передаёт его драйверам, сложенным до целевого устройства, и сама эта стопка | Часть 1 |
| Диспетчер кэша | Часть, которая держит содержимое файла в памяти и позже пакетом пишет на диск то, что пришло в WriteFile. Источник «сразу после записи на диск ещё не обязательно попало» |
Часть 4 |
Таблица 1: термины предыдущих частей, которые эта часть предполагает
Ещё одно: два этапа из части 1 — cleanup (когда закрылся последний дескриптор) и close (когда исчезли все ссылки внутри ядра) — понадобятся в объяснении удаления файла в главе 4.
1. Сначала вывод
- Центр NTFS — MFT (главная файловая таблица). Все файлы ведутся как записи в MFT; вся информация о файле — «внутри записи MFT» или «в области вне MFT, на которую запись указывает» (глава 2).1
- Сущность файла — «набор атрибутов». Маленький файл целиком, вместе с данными, помещается в запись MFT (резидентный), большой держит только ссылку на прогон кластеров (нерезидентный). Медленность массовой обработки мелких файлов объясняется отсюда (глава 2).1
- Данных может быть несколько (несколько потоков данных). Обычные данные — «безымянный поток»; дополнительный поток задают как
file.txt:имя. Суть Zone.Identifier (Mark of the Web) (глава 3).2 - Имя тоже атрибут. Несколько имён у одной записи — жёсткая ссылка. Сокращённое имя 8.3 тоже живёт рядом как «ещё одно имя» (глава 4).34
- Точка повторного разбора — официальный механизм «открыл — и в другом месте». Символические ссылки, соединения и Files On-Demand OneDrive — все приложения этих данных с тегом (глава 5).56
- Журналов два.
$LogFile— для восстановления согласованности метаданных (опережающий журнал, чтобы не сломаться), журнал USN — для записи истории изменений (реестр «что изменилось»). Роли совершенно разные (глава 6).78 - «Размер» и «размер на диске» — разные вещи. Разрыв дают разреженные файлы и сжатие. Фон «сжатый файл не становится асинхронным» из части 2 тоже здесь (глава 7).910
Карта знаний этой статьи
Центр NTFS — MFT (главная файловая таблица): все файлы ведутся как записи в MFT. Маленькие данные резидентны внутри записи, большие — нерезидентны и держат только ссылку на прогон кластеров; в этом суть медленности массовой обработки мелких файлов и фрагментации. Несколько потоков данных, жёсткие ссылки и сокращённые имена 8.3 — приложения того же механизма «набор атрибутов»; точка повторного разбора — официальный крючок на «открыть», который держит от символической ссылки до Files On-Demand OneDrive. $LogFile защищает согласованность структуры; долговечность содержимого данных нужно отдельно строить инструментами управления кэшем части 4.
flowchart LR
accTitle: Карта знаний внутреннего устройства NTFS и MFT
accDescr: Рисунок, показывающий связи NTFS, MFT, записи файла, резидентных и нерезидентных атрибутов, прогонов данных, фрагментации, нескольких потоков данных, Zone.Identifier, жёстких ссылок, сокращённых имён 8.3, точек повторного разбора (символическая ссылка, соединение, Files On-Demand OneDrive), $LogFile и журнала USN, разреженных файлов и сжатия NTFS
ntfs["NTFS"]
mft["MFT (главная файловая таблица)"]
mft_record["запись файла MFT"]
mft_zone["зона MFT"]
fragmentation["фрагментация (NTFS)"]
resident_attribute["резидентный атрибут (resident)"]
non_resident_attribute["нерезидентный атрибут (non-resident)"]
data_run["прогон данных"]
fsutil["fsutil"]
alternate_data_stream["альтернативный поток данных (ADS)"]
zone_identifier["Zone.Identifier (Mark of the Web)"]
streams_tool["streams (Sysinternals)"]
size_disk_usage_mismatch["расхождение логического размера и использования диска"]
hard_link["жёсткая ссылка"]
eight_dot_three_name["сокращённое имя 8.3"]
reparse_point["точка повторного разбора"]
symbolic_link["символическая ссылка"]
junction["соединение (точка монтирования)"]
onedrive_files_on_demand["Files On-Demand OneDrive"]
ntfs_logfile["$LogFile (журнал транзакций NTFS)"]
volume_corruption["повреждение тома"]
cache_manager["диспетчер кэша"]
usn_journal["журнал USN (журнал изменений)"]
sparse_file["разреженный файл"]
ntfs_compression["сжатие NTFS"]
asynchronous_io["асинхронный ввод-вывод"]
procmon["Process Monitor (procmon.exe)"]
ntfs -->|"использует"| mft
mft -->|"использует"| mft_record
mft -->|"требует"| mft_zone
mft_zone -.->|"снижает"| fragmentation
mft_record -->|"использует"| resident_attribute
mft_record -->|"использует"| non_resident_attribute
resident_attribute -->|"несовместимо с"| non_resident_attribute
non_resident_attribute -->|"использует"| data_run
data_run -.->|"может вызвать"| fragmentation
fragmentation -->|"проверяется"| fsutil
ntfs -->|"использует"| alternate_data_stream
zone_identifier -->|"хранится в"| alternate_data_stream
zone_identifier -->|"проверяется"| streams_tool
alternate_data_stream -->|"проверяется"| streams_tool
alternate_data_stream -.->|"может вызвать"| size_disk_usage_mismatch
ntfs -->|"использует"| hard_link
hard_link -->|"требует"| mft_record
ntfs -.->|"использует"| eight_dot_three_name
eight_dot_three_name -->|"настраивается"| fsutil
ntfs -->|"использует"| reparse_point
reparse_point -->|"проверяется"| fsutil
symbolic_link -->|"реализует"| reparse_point
junction -->|"реализует"| reparse_point
onedrive_files_on_demand -->|"реализует"| reparse_point
ntfs -->|"использует"| ntfs_logfile
ntfs_logfile -->|"снижает"| volume_corruption
ntfs -->|"использует"| cache_manager
ntfs -->|"использует"| usn_journal
usn_journal -->|"проверяется"| fsutil
ntfs -->|"использует"| sparse_file
sparse_file -->|"может вызвать"| size_disk_usage_mismatch
ntfs -->|"использует"| ntfs_compression
ntfs_compression -->|"может вызвать"| size_disk_usage_mismatch
ntfs_compression -->|"несовместимо с"| asynchronous_io
ntfs -->|"проверяется"| procmon
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 35, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
2. Всё — записи MFT
2.1. Реестр тома
При форматировании тома NTFS создаются MFT (master file table) и ряд файлов метаданных, имена которых начинаются с $. В MFT есть хотя бы одна запись на каждый файл тома, и запись самой MFT тоже входит.1
flowchart TB
subgraph VOL["Том NTFS"]
MFT["$MFT ── главная файловая таблица<br/>реестр записей всех файлов (сама тоже в нём)"]
LOG["$LogFile ── журнал транзакций<br/>операций над метаданными (глава 6)"]
BITMAP["$Bitmap ── использование кластеров"]
OTH["$Boot / $Secure / $UpCase и др.<br/>прочие файлы метаданных"]
DATA["Область данных пользователя<br/>(место нерезидентных данных)"]
end
MFT -->|"запись указывает на положение"| DATA
Рис. 1: Структура тома NTFS. «Управленческая информация самой файловой системы тоже живёт как файлы» — устройство NTFS.
Сведения о файле — размер, метки времени, права доступа и вплоть до содержимого данных — хранятся внутри записи MFT или в области вне MFT, положение которой описывает запись.1 При удалении файла запись помечается «свободной» и идёт в повторное использование, но сама MFT не сжимается. Чтобы держать MFT непрерывной, резервируется область зоны MFT; когда том заполняется, начинается фрагментация MFT — даже этот срок жизни описан в официальной документации.1
2.2. Файл = набор атрибутов, резидентный и нерезидентный
Содержимое записи файла — список атрибутов. Стандартные сведения (метки времени и т. п.), имя файла, безопасность и данные. Здесь важная развилка.
flowchart TB
subgraph REC["Запись файла MFT (реестр на один файл)"]
STD["Атрибут стандартных сведений<br/>метки времени, флаги атрибутов"]
FN["Атрибут имени файла<br/>(может быть несколько ── глава 4)"]
DATA["Атрибут данных"]
end
Q{"Данные маленькие?"}
RES["Резидентный (resident)<br/>само содержимое помещается в запись<br/>чтение заканчивается одним доступом к MFT"]
NONRES["Нерезидентный (non-resident)<br/>в записи только «ссылка на прогон кластеров»<br/>сами данные — в области пользователя"]
DATA --> Q
Q -->|"примерно до сотен байт"| RES
Q -->|"больше"| NONRES
Рис. 2: Запись файла — набор атрибутов. Если данные малы, они «резидентны» внутри записи.
Как одно и то же содержимое записи выглядит у резидентного и нерезидентного — рядом понятнее.
flowchart LR
subgraph RES2["Резидентный (resident) ── маленький файл"]
RA["Запись файла MFT (фиксированная длина)<br/>стандартные сведения / имя / безопасность<br/>─────────────<br/>атрибут данных = само содержимое<br/>«значение настройки = 1» лежит здесь напрямую"]
RB["Отдельного места на диске нет<br/>чтение заканчивается доступом к MFT"]
RA --> RB
end
subgraph NON2["Нерезидентный (non-resident) ── большой файл"]
NA["Запись файла MFT (фиксированная длина)<br/>стандартные сведения / имя / безопасность<br/>─────────────<br/>атрибут данных = таблица прогонов данных<br/>ряд «откуда сколько кластеров»"]
NB["Область данных пользователя<br/>прогон 1: непрерывные кластеры"]
NC["Область данных пользователя<br/>прогон 2: непрерывные кластеры в другом месте"]
NA -->|"указывает на положение"| NB
NA -->|"указывает на положение"| NC
end
Рис. 3: Резидентный против нерезидентного. У нерезидентного в записи только таблица «где сколько настоящих данных» (прогоны данных).
Чем больше прогонов, тем больше при чтении одного файла приходится ходить по разбросанным областям. Это и есть суть фрагментации ниже.
Из этой структуры объясняется несколько явлений с площадки.
- Почему копирование десяти тысяч мелких файлов медленное. На каждый файл — создание записи MFT, регистрация имени, настройка безопасности: операции над метаданными. Не сам перенос данных доминирует, а работа реестра (и каждая из них ещё станет целью проверки фильтров части 6).
- Суть фрагментации. Нерезидентные данные записываются как «ряд непрерывных участков кластеров (прогонов)». Если непрерывную область не взять, число прогонов растёт, растёт число нужных позиционирований — это фрагментация. Реальный ряд прогонов можно подсмотреть
fsutil file layout. - «Папка» ничем не особенная. Каталог — «файл с указателем (индексом) от имени файла к номеру записи MFT». На уровне реестра всё сидит на одном механизме.
3. Данные — лишь один из «потоков»
3.1. Один файл, несколько последовательностей байтов
В NTFS один файл может держать несколько потоков данных. То, что обычно читают и пишут ReadFile/WriteFile, — безымянный поток по умолчанию; синтаксисом имя_файла:имя_потока делают альтернативный поток данных (ADS).2
flowchart LR
subgraph F["Файл report.docx (одна запись MFT)"]
D0["Поток по умолчанию (безымянный)<br/>= содержимое, которое обычно видят"]
D1[":Zone.Identifier<br/>сведения об источнике (Mark of the Web)"]
D2[":произвольное имя<br/>собственные добавки приложения"]
end
Рис. 4: Несколько потоков данных. В размере проводника виден только поток по умолчанию.
Самый близкий ADS — Zone.Identifier. У файла, скачанного браузером, в этом потоке записан источник (из интернета и т. п.); это материал решения SmartScreen «Windows защитил ваш компьютер» и защищённого просмотра Office. Лицевая сторона этого механизма — в «Почему Windows показывает сообщение „Windows защитил ваш компьютер“»; изнанка — просто поток NTFS.
3.2. Ловушки, в которые попадает разработчик
- Не видно. Ни в размере проводника, ни в списке
dir. Проверяютdir /rилиstreamsиз Sysinternals.11 - Не увезти. ADS — функция NTFS, при копировании через USB на FAT или через облачное хранилище часто теряется. «Предупреждение о скачивании пропало после копирования» — это оно.
- Своё приложение тоже может открыть. Достаточно двоеточия в пути:
CreateFile("data.txt:meta", ...).2 Удобно, но вместе с этим принимаете свойство «не увезти» из предыдущего пункта — не место для тела бизнес-данных.
4. Имя тоже атрибут — жёсткие ссылки и имена 8.3
4.1. Жёсткая ссылка — несколько имён одной записи
На рис. 2 было «атрибут имени файла может быть несколько». Внутри одного тома несколько путей ссылаются на один файл — это жёсткая ссылка (CreateHardLink / mklink /H).3
flowchart TB
subgraph DIR1["Индекс C:\app\ "]
E1["config.json → запись #1234"]
end
subgraph DIR2["Индекс C:\backup\ "]
E2["config-link.json → запись #1234"]
end
REC["Запись MFT #1234<br/>тело данных (или ссылка на прогоны)<br/>число ссылок: 2"]
E1 --> REC
E2 --> REC
Рис. 5: Жёсткая ссылка. Индекс каталога указывает на одну и ту же запись MFT — оба «настоящие».
Изменение через любое имя — тот же файл, содержимое сразу совпадает.3 И меняется смысл «удаления»: DeleteFile — «снять одно имя», а сущность исчезает, только когда снято последнее имя, закрыты открытые дескрипторы и исчезли все ссылки внутри ядра, включая секции проекции в память. Два этапа из части 1 — cleanup (последний дескриптор) и close (последняя ссылка) — прямо работают и на срок жизни удаления. У отображения атрибутов есть странность: смена атрибута через одну ссылку может оставить видимое отображение других ссылок старым — это официально отмечено.3
4.2. Имя 8.3 — ещё одно скрытое имя
Ради исторической совместимости NTFS может автоматически порождать для длинного имени сокращённое имя формата 8.3 вроде REPORT~1.DOC. Оно тоже живёт в записи как «ещё одно имя». В папке с массой файлов порождение и разбор столкновений сокращённых имён стоит денег, поэтому fsutil 8dot3name умеет выключить порождение и снять уже существующие (если старое приложение записало путь в реестре сокращённым именем, сломается — поэтому перед strip есть проверка, это практичный момент).4
Важно: есть ли сокращённое имя — зависит от среды. Поведение по умолчанию задаёт значение реестра NtfsDisable8dot3NameCreation: 0 (порождать на всех томах), 1 (не порождать ни на одном), 2 (настраивать по томам), 3 (не порождать кроме системного тома).4 При 2 можно переключать по томам, поэтому «в Windows обязательно есть сокращённое вроде PROGRA~1» — неверно. Прежде чем писать код или процедуру, зависящие от сокращённого имени, проверьте текущее состояние: fsutil 8dot3name query C: (без тома — общая настройка по умолчанию).
Ловушки путей и имён (MAX_PATH, зарезервированные имена, конечная точка) подробно — в «MAX_PATH и подводные камни путей/имён файлов в Windows». Разрешение имён части 1 (диспетчер объектов) плюс эта глава (имена внутри файловой системы) дают общую картину «имени» в Windows.
5. Точка повторного разбора — механизм «открыл — и в другом месте»
Файлу или каталогу можно повесить точку повторного разбора. Сущность — атрибут «тег + данные, заданные пользователем». Когда файловая система открывает файл с точкой повторного разбора, обработка перехватывается по тегу: либо драйвер фильтра, который понимает тег, берёт обработку на себя, либо при теге переименования разрешение начинается заново с пути назначения.5
sequenceDiagram
participant App as Приложение
participant IOM as Диспетчер I/O
participant FS as NTFS
App->>IOM: CreateFile("C:\data\link.txt")
IOM->>FS: IRP_MJ_CREATE (мир части 1)
Note over FS: У цели найдена точка повторного разбора<br/>возвращаются тег и данные
alt Символическая ссылка / соединение (переименование)
FS-->>IOM: «Настоящее место — вот»
IOM->>FS: Разрешение заново по пути назначения
else Тег, которым управляет фильтр (облачные файлы и т. п.)
Note over FS: Фильтр, понимающий тег,<br/>берёт обработку (часть 6)
end
Рис. 6: Разрешение точки повторного разбора. Официальный крючок на операцию «открыть».
На одном этом механизме стоят привычные функции.
- Символическая ссылка (
mklink) — указатель, держащий путь назначения. Может указывать на другой том и UNC.6 - Соединение / точка монтирования — старый механизм связать каталог с местом на другом локальном томе.3
- Files On-Demand OneDrive — файл без содержимого под рукой представляют точкой повторного разбора, в момент открытия фильтр скачивает и подставляет содержимое. Суть «в проводнике видно, а при открытии идёт сеть» (сам механизм фильтра — в части 6).
Практическое замечание одно. «То, что в конце пути, не обязательно именно это локальное место». Инструмент, который рекурсивно обходит дерево, зацикливается на соединении; суммирование размеров двоится; резервное копирование массово материализует облако — код, который не знает о точках повторного разбора, наступает сюда. Вход в защиту — проверка атрибута FILE_ATTRIBUTE_REPARSE_POINT семейства FindFirstFile.5
6. Два журнала — $LogFile и USN
«NTFS — журналируемая файловая система» говорят часто, но у NTFS два журнала с разной ролью. Смешаете — неверно прочитаете гарантию.
flowchart TB
subgraph J1["$LogFile ── опережающий журнал (чтобы не сломаться)"]
A1["Операции над метаданными (обновление записи, смена имени и т. п.)<br/>записывают в журнал до выполнения"]
A2["При следующем запуске после сбоя системы<br/>журнал воспроизводят и восстанавливают согласованность структуры"]
A1 --> A2
end
subgraph J2["Журнал USN ── история изменений (чтобы знать, что изменилось)"]
B1["При каждом изменении файла / каталога<br/>записывают содержимое изменения и имя"]
B2["Резервное копирование, поисковый индекс, синхронизация<br/>видят «что изменилось с прошлого раза» без полного обхода"]
B1 --> B2
end
Рис. 7: Два журнала. $LogFile — чтобы «не сломаться», USN — чтобы «знать об изменениях».
Разница таблицей.
| Аспект | $LogFile (журнал транзакций) |
Журнал USN (журнал изменений) |
|---|---|---|
| Цель | После сбоя вернуть структуру файловой системы в согласованное состояние7 | Позже узнать «что изменилось с прошлого раза»8 |
| Что записывают | Опережающий журнал операций над метаданными (обновление записи, смена имени и т. п.). Содержимое файла не цель | При каждом изменении — содержимое изменения и имя целевого файла/каталога8 |
| Кто пользуется | Сама NTFS. Автовосстановление при следующем монтировании | Приложения: резервное копирование, поисковый индекс, синхронизация |
| Как далеко назад | Только диапазон, нужный для восстановления. Фиксированный размер крутится, для истории прошлого не годится | При превышении целевого максимума (MaximumSize) на контрольной точке старые записи отрезаются с начала. Насколько далеко — от настройки размера и объёма обновлений тома12 |
| Как смотреть | Официального чтения содержимого нет (размер смотрят chkdsk /L) |
Состояние — fsutil usn queryjournal, содержимое — fsutil usn readjournal. Из программы — FSCTL_QUERY_USN_JOURNAL / FSCTL_READ_USN_JOURNAL12 |
| Можно ли остановить | Нельзя (часть NTFS) | Администратор может удалить и выключить. Но это заставляет пользующиеся службы делать полный обход — влияние большое12 |
Таблица 2: Два журнала рядом
$LogFile(журнал транзакций) — опережающий журнал операций над метаданными. Даже при сбое системы NTFS при следующем запуске автоматически восстанавливает согласованность файловой системы из этого журнала и сведений контрольной точки.7 Здесь защищена структура. Как в части 4, грязное содержимое данных в кэше при отключении питания может пропасть — правильное чтение: «том не сломается. Но последняя запись может исчезнуть».- Журнал USN (журнал изменений) — реестр, который при каждом изменении файла или каталога на томе записывает содержимое изменения и имя цели.8 Механизм, которым резервное копирование и индексатор берут «только то, что изменилось с прошлого раза» без полного обхода; ещё — чтобы избежать перестройки индекса после сбоя.8 На практике полезно помнить:
fsutil usn readjournalможно взять сверкой, которая дополняет пропускиFileSystemWatcher(«Практическое руководство по FileSystemWatcher»).
7. Разреженные файлы и сжатие — история про два «размера»
В NTFS логическая длина файла и реально выделенная область ведутся отдельно. На экране свойств это «размер» и «размер на диске». Два главных источника разрыва.
Разреженный файл диапазонам из одних нулей реальное место не выделяет — ведёт как «дыры».9 Файл виртуального диска с логическим размером 42 ГБ на диске занимает 500 МБ — обычное дело. Чтение дыры возвращает нули; запись выделяет ровно столько.
flowchart LR
subgraph L["Логический файл (размер: 1 ГБ)"]
R1["Данные 10 МБ"]
H1["Дыра (нули) 500 МБ"]
R2["Данные 5 МБ"]
H2["Дыра (нули) остальное"]
end
subgraph P["Выделение на диске (15 МБ + управленческая информация)"]
A1["Прогон: сущность R1"]
A2["Прогон: сущность R2"]
end
R1 --> A1
R2 --> A2
Рис. 8: Разреженный файл. У «дыры» выделения нет, логический размер и размер на диске расходятся.
Сжатие NTFS хранит данные, сжатые единицами сжатия.10 Прозрачно и удобно, но стоимость тоже не прозрачна: при каждом чтении и записи идут развёртка и повторное сжатие, фрагментация легче растёт. И как в главе 5 части 2, доступ к сжатому файлу не становится асинхронным (файловая система превращает его в синхронный). Одно из мест, которое подозревать, когда «сделали асинхронный I/O, а этот файл не ускорился».
Реальный размер по выделению даёт GetCompressedFileSize. В расследовании «сумма размеров файлов» не сходится с «использованием диска» стандарт — по очереди подозревать разреженность, сжатие, ADS (глава 3) и округление по кластерам.
8. Проверить своими глазами
И на этот раз всё можно наблюдать на своём Windows (часть команд — с правами администратора). Чтобы самим судить, верен ли результат, у каждой команды — куда смотреть и что из этого видно.
:: Смотреть альтернативные потоки данных
dir /r C:\Users\%USERNAME%\Downloads
Куда смотреть: под обычной строкой файла с отступом идут строки вида имя_файла:Zone.Identifier:$DATA с длиной. Если такая строка есть, у файла висит Mark of the Web (глава 3). У скачанных браузером — есть, у созданных самим — нет. Запустите в обоих местах и сравните — наличие ADS станет очевидным.
:: Раскладка файла на MFT (прогоны) и атрибуты
fsutil file layout C:\path\to\file.dat
:: Только экстенты (подкоманда из официальной документации)
fsutil file queryextents C:\path\to\file.dat
Куда смотреть: layout по потокам выводит размер, выделенный размер и, если нерезидентный, список экстентов (пары VCN·LCN·число кластеров). Очень маленький файл без строк экстентов — резидентный (п. 2.2); если строк несколько — фрагментирован. Сравнить текстовый файл в несколько байт и файл в сотни мегабайт — самый короткий способ прочувствовать резидентный / нерезидентный.
:: Настройка порождения имён 8.3 и уже существующие сокращённые
fsutil 8dot3name query C:
dir /x
Куда смотреть: query отвечает, включено ли порождение сокращённых имён на этом томе (без тома — общая настройка по умолчанию).4 dir /x рядом с длинным именем показывает столбец сокращённого: пустой столбец значит сокращённого нет. «Сокращённого имени может не быть» из п. 4.2 проверяется на своей среде.
:: Состояние журнала USN
fsutil usn queryjournal C:
Куда смотреть: идентификатор журнала, диапазон действующих USN (First USN / Next USN), целевой максимум (MaximumSize) и единица выделения (AllocationDelta).12 Создайте какой-нибудь файл и выполните снова — Next USN должен продвинуться: это подтверждение «изменение записано». MaximumSize — ориентир «как далеко назад» из главы 6. На томе с выключенным журналом будет ошибка.
:: Проверка точки повторного разбора (назначение и тег)
dir /aL C:\Users\%USERNAME%
fsutil reparsepoint query "C:\Users\%USERNAME%\OneDrive"
Куда смотреть: то, что вышло в dir /aL, — точки повторного разбора (стоит FILE_ATTRIBUTE_REPARSE_POINT). Символические ссылки и соединения показываются с типом вроде <SYMLINKD> <JUNCTION>. fsutil reparsepoint query выводит значение тега повторного разбора и, если это переименование, путь назначения. Цель без точки повторного разбора даёт ошибку — сама ошибка подтверждает «здесь обычная папка».
Если гнать файловые операции в Procmon, герои этой части идут своими именами (запись в $LogFile, пути с именем потока, обработка повторного разбора). Как пользоваться — в «Практическое руководство по Process Monitor (ProcMon)».
9. Итог
- Центр NTFS — MFT. Все файлы — записи реестра; сведения — «внутри записи» или «во внешней области, на которую запись указывает». Маленькие данные резидентны, большие — ссылка на прогоны; медленность массовой обработки мелких файлов и фрагментация — следствия этой структуры.1
- Потоков данных может быть несколько. Zone.Identifier (Mark of the Web) — просто ADS, виден через
dir /r, за пределы NTFS не уезжает.211 - Имя — атрибут, их может быть несколько. Жёсткая ссылка — равные имена одной записи, имя 8.3 — ещё одно имя ради совместимости. «Удаление = снять имя»; сущность исчезает, когда пропали последнее имя, дескрипторы и ссылки внутри ядра (отображённые секции и т. п.).34
- Точка повторного разбора — официальный крючок на «открыть»; символическая ссылка, соединение и Files On-Demand — его приложения. Код, обходящий дерево, должен учитывать
FILE_ATTRIBUTE_REPARSE_POINT.56 - Журналов два.
$LogFile— восстановление согласованности структуры (не сломаться), USN — история изменений (что изменилось). «Журналируемая — значит и данные в безопасности» — нет: долговечность данных строят инструментами части 4.78 - Логический размер и выделение — разные вещи. Разреженность, сжатие, ADS, округление по кластерам — четыре главных причины «размеры не сходятся». В том числе то, что сжатый файл не становится асинхронным I/O, — ящик расследования производительности.910
Продолжение — финал, часть 6 «Фильтры и минифильтры — почему Procmon и антивирус могут перехватывать I/O». Те, кто с части 1 нет-нет да появлялся «посередине» — антивирус, Procmon, OneDrive, шифрование — как встают в I/O. Финал серии: кто живёт в щелях стека устройств.
Похожие статьи
- Глубины ввода-вывода Windows (часть 1) — каждый обмен становится IRP: общая картина системы I/O
- Глубины ввода-вывода Windows (часть 2) — синхронный и асинхронный I/O: настоящий смысл OVERLAPPED
- Глубины ввода-вывода Windows (часть 4) — диспетчер кэша: когда ваш WriteFile доходит до диска
- Почему Windows показывает сообщение «Windows защитил ваш компьютер»
- MAX_PATH и подводные камни путей/имён файлов в Windows — лимит 260 символов, зарезервированные имена, конечная точка, регистр
- Практическое руководство по FileSystemWatcher — защита от потерянных и повторяющихся уведомлений
- Подводные камни сетевых дисков и UNC-путей ── как бизнес-приложения работают с файловым сервером (общей папкой)
- Практическое руководство по Process Monitor (ProcMon) — как за 10 минут выяснить, почему «настройки не читаются» или возникает ACCESS DENIED
Смежные области консультирования
KomuraSoft LLC занимается проектированием и расследованием Windows-бизнес-приложений, корни которых в устройстве NTFS: странное поведение размера файла и скорости копирования, сбои вокруг ссылок и потоков.
- Разработка приложений для Windows
- Расследование ошибок и причин
- Использование существующих активов и поддержка миграции
- Связаться с нами
Справочные ссылки
-
Microsoft Learn, Master File Table. О том, что у каждого файла на томе NTFS в MFT есть хотя бы одна запись и запись самой MFT тоже входит; о том, что все сведения о файле — размер, метки времени, права доступа, содержимое данных — хранятся внутри записи MFT или в области вне MFT, положение которой описывает запись; о том, что при удалении файла запись помечается свободной и идёт в повторное использование, но размер MFT не уменьшается; о резервировании зоны MFT, чтобы держать MFT непрерывной; о фрагментации MFT по мере выделения. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, File Streams. О том, что данные файла NTFS хранятся как один или несколько потоков; о безымянном потоке данных по умолчанию и именованных альтернативных потоках данных; о открытии потока через CreateFile в форме «имя_файла:имя_потока». ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Hard links and junctions. О том, что жёсткая ссылка — представление файловой системы, при котором несколько путей внутри одного тома ссылаются на один файл; о создании через CreateHardLink; о том, что изменение через любую ссылку сразу видно через другие; о странности отображения: смена атрибутов распространяется на все жёсткие ссылки, но видимое отображение в записи каталога обновляется только у ссылки, через которую меняли; о соединениях (механизм связать каталог с другим локальным томом). ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, fsutil 8dot3name. О том, что NTFS может порождать для длинного имени сокращённое формата 8.3; о том, что fsutil 8dot3name умеет запрашивать и задавать включение/выключение порождения, снимать существующие сокращённые (strip) и сканировать ссылки реестра, которые пострадают при снятии. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Reparse points. О том, что точка повторного разбора — набор данных, заданных пользователем, и тега повторного разбора, однозначно идентифицирующего формат этих данных; о том, что при открытии файла с точкой повторного разбора файловая система пытается обработку, соответствующую тегу (обработку фильтром файловой системы, который понимает тег); о применении в ссылках файловой системы NTFS и удалённом хранилище (иерархическая память); о проверке существования атрибутом FILE_ATTRIBUTE_REPARSE_POINT. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Symbolic links. О том, что символическая ссылка — объект файловой системы, указывающий на другой файл или каталог, и работает как прозрачное перенаправление к назначению; о абсолютных и относительных ссылках и о возможности ссылок через том и на удалённый путь. ↩ ↩2 ↩3
-
Microsoft Learn, NTFS overview. О том, что NTFS пользуется файлом журнала и сведениями контрольной точки и при сбое системы при следующем запуске автоматически восстанавливает согласованность файловой системы, воспроизводя журнал транзакций; о динамическом переназначении плохих секторов и self-healing NTFS, которая в фоне чинит лёгкие повреждения. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Change Journals. О том, что при каждом изменении файла или каталога на томе в журнал изменений USN этого тома записываются содержимое изменения и имя целевого файла/каталога; о ведении журнала на том; о применении для восстановления индекса файловой системы после сбоя и избежания полной переиндексации тома. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Sparse Files. О том, что в разреженном файле большим диапазонам из нулей физическое место на диске не выделяется, а выделяется только частям с данными; о том, что чтение невыделенного диапазона возвращает нули. ↩ ↩2 ↩3
-
Microsoft Learn, File Compression and Decompression. О том, что сжатие файлов NTFS прозрачно и данные сжимаются и хранятся единицами сжатия; о получении размера после сжатия (реального выделения) через GetCompressedFileSize; о стоимости развёртки и повторного сжатия при чтении и записи сжатого файла. ↩ ↩2 ↩3
-
Microsoft Learn, Streams - Sysinternals. О том, что утилита streams из Sysinternals умеет перечислять и удалять альтернативные потоки данных файлов NTFS. ↩ ↩2
-
Microsoft Learn, Creating, Modifying, and Deleting a Change Journal и fsutil usn. О том, что MaximumSize журнала изменений — целевое значение и при превышении суммы MaximumSize и AllocationDelta NTFS обрезает его на контрольной точке; о том, что AllocationDelta — единица добавления в конец журнала и удаления с начала; о просмотре состояния и ёмкости через fsutil usn queryjournal и содержимого через readjournal; о FSCTL_CREATE_USN_JOURNAL / FSCTL_QUERY_USN_JOURNAL / FSCTL_READ_USN_JOURNAL / FSCTL_DELETE_USN_JOURNAL из программы; о том, что удаление и выключение активного журнала сопровождаются обходом всей MFT и заставляют службы, которые журналом пользуются, заново сканировать том. ↩ ↩2 ↩3 ↩4
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Глубины ввода-вывода Windows (часть 4) — диспетчер кэша: когда ваш WriteFile доходит до диска
Часть 4 серии со схемами диспетчера кэша Windows. Кэш как проекция файла, упреждающее чтение и отложенная запись, выбор между FlushFileBu...
Глубины ввода-вывода Windows (часть 6, финал) — фильтры и минифильтры: почему Procmon и антивирус могут перехватывать I/O
Финал серии со схемами фильтров и минифильтров Windows. Диспетчер фильтров и высота (altitude), обратные вызовы pre/post, как Procmon и а...
Практические лучшие практики многопоточности: издание .NET — что решить, прежде чем добавлять потоки
Практический разбор правил проектирования, которые не дают многопоточному коду на .NET/C# «изредка падать или зависать»: не создавать пот...
Теневое копирование тома (VSS): устройство и практика — почему резервное копирование может скопировать файл, который ещё используется
Занятый файл обычно нельзя скопировать из-за нарушения совместного доступа — как тогда это делает ПО резервного копирования? Статья разби...
Аутсорсинг и контрактная разработка Windows-приложения: что стоит прояснить перед заказом
Перед тем как заказать аутсорсинг или контрактную разработку Windows-приложения, разберём, что нужно прояснить: доработка существующего П...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Бизнес-приложения, интеграция оборудования и средства связи — от требований до разработки.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Что такое MFT (главная файловая таблица)?
- Это структура данных в сердце тома NTFS — реестр, в котором у каждого файла на томе есть хотя бы одна запись (запись файла), включая запись самой MFT. Всё о файле — размер, метки времени, права доступа и вплоть до содержимого данных — хранится либо внутри записи MFT, либо в области вне MFT, на которую запись указывает. Маленький файл целиком, вместе с данными, помещается в свою запись MFT (резидентный); у большого в записи только ссылка на раскладку данных (прогон кластеров) (нерезидентный). Удаление файла помечает запись свободной для повторного использования, но размер самой MFT не уменьшается.
- Что за невидимые данные «Zone.Identifier» у файлов?
- Один из нескольких потоков данных NTFS (альтернативный поток данных). В NTFS один файл может держать несколько последовательностей байтов (потоков); то, что обычно читают и пишут, — безымянный поток по умолчанию. Windows записывает, откуда файл (например, скачан из интернета) в дополнительный поток, который задают через двоеточие: «file.txt:Zone.Identifier». Это так называемый «Mark of the Web», на нём SmartScreen и защищённый просмотр Office строят решения. Альтернативные потоки не видны в размере проводника; проверить их можно командой dir /r или инструментом streams из Sysinternals. Важно: при копировании на файловую систему не-NTFS вроде FAT они не сохраняются.
- Чем жёсткая ссылка отличается от символической?
- Жёсткая ссылка — «ещё одно имя равного статуса, указывающее на ту же сущность файла — ту же запись MFT». Создаётся только внутри одного тома, доступ по любому имени даёт тот же файл, удаление одного имени файл не убирает, пока остаётся другое. Символическая ссылка — «указатель, который направляет на другой путь», реализована как точка повторного разбора. Она держит только строку целевого пути, поэтому может указывать на другой том и даже удалённо, но становится тупиком, если цель исчезла. На практике: жёсткая ссылка — чтобы разделить сущность (меняется смысл удаления), символическая — чтобы перенаправить путь (перенос или перенаправление).
- NTFS — журналируемая файловая система, значит данные переживают отключение питания?
- Нужно точно понять, что защищено. Журнал транзакций NTFS ($LogFile) защищает согласованность структуры файловой системы (метаданных). Даже при сбое системы NTFS при следующем запуске автоматически восстанавливает согласованность по журналу и не даёт тому «сломаться так, что не читается». Но само содержимое файла, который писали посередине, от этого не восстанавливается. Как в части 4 серии, грязные данные в кэше при отключении питания пропадают. Правильное понимание — «том не сломается, но содержимое последней записи всё ещё может исчезнуть»; если нужна долговечность самих данных, её нужно строить самим: FlushFileBuffers, WRITE_THROUGH или запись приложения вроде «во временный файл, потом переименовать».
- Почему «размер» файла отличается от «размера на диске»?
- Потому что NTFS ведёт логическую длину файла и реально выделенное место на диске отдельно. Даже у обычного файла есть разница из-за округления выделения до целых кластеров (по умолчанию 4 КБ), но большой разрыв — у разреженных и сжатых файлов. Разреженный файл диапазонам из одних нулей реальное хранилище не выделяет — ведёт их как «дыры» — поэтому файл с логическим размером в несколько гигабайт может занимать на диске лишь несколько мегабайт. У сжатого выделяется только размер после сжатия. Если наоборот «размер на диске» кажется больше, часто виноваты округление по кластерам или альтернативный поток данных.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.