Đáy I/O Windows (kỳ 5) ── cấu trúc trong của NTFS: hiểu hệ thống tệp từ MFT
· Go Komura · Windows, NTFS, I/O, Hệ thống tệp, MFT, Kernel, .NET, Điều tra lỗi
Bốn kỳ đến đây đã xem yêu cầu I/O chảy thế nào (kỳ 1–3) và cache đón thế nào (kỳ 4). Yêu cầu cuối cùng đến hệ thống tệp. Lần này là đại diện đó, NTFS.
Góc nhìn đổi. Đến nay là chuyện động “luồng yêu cầu”. Lần này là chuyện cấu trúc tĩnh: dữ liệu đặt trên đĩa thế nào. Bản chất “Zone.Identifier” vô hình gắn tệp tải về. Lý do sao chép 10.000 tệp nhỏ chậm hơn nhiều so với một tệp cùng tổng kích thước. “NTFS journaling nên yên tâm” đúng đến đâu ── tất cả giải thích từ cấu trúc này.
Kỳ 5 của loạt “Đáy I/O Windows”.
Tiền đề đọc kỳ này: nắm IRP và device stack kỳ 1 thì dễ đọc hơn. Dù vậy đọc đơn cũng không kẹt, nên định nghĩa trước các thuật ngữ kỳ trước xuất hiện trong bài.
| Thuật ngữ | Nói một dòng | Chi tiết |
|---|---|---|
| IRP (I/O Request Packet) | “Phiếu yêu cầu I/O” lời gọi API như ReadFile được chuyển trong kernel. Driver nhận phiếu này rồi xử lý |
Kỳ 1 |
| I/O Manager và device stack | Thành phần kernel tạo IRP, lần lượt đưa xuống stack driver chồng đến thiết bị đích, và chính chồng đó | Kỳ 1 |
| Cache Manager | Thành phần giữ nội dung tệp trên bộ nhớ, ghi nội dung WriteFile ra đĩa sau, gộp. Thủ phạm của “vừa ghi không chắc đã đến đĩa” |
Kỳ 4 |
Bảng 1: Thuật ngữ kỳ trước làm tiền đề kỳ này
Thêm nữa, hai tầng cleanup (khi handle cuối đóng) và close (khi mọi tham chiếu trong kernel biến) đã xử kỳ 1 cũng dùng khi giải thích xóa tệp Chương 4.
1. Kết luận trước
- Trung tâm NTFS là MFT (Master File Table). Mọi tệp được quản như bản ghi trong MFT; mọi thông tin về tệp nằm “trong mục MFT” hoặc “vùng ngoài MFT mà mục chỉ” (Chương 2).1
- Thực thể tệp là “tập thuộc tính”. Tệp nhỏ thì cả dữ liệu nằm gọn trong bản ghi MFT (resident); tệp lớn chỉ giữ tham chiếu dãy cluster (non-resident). Chậm khi xử lý hàng loạt tệp nhỏ giải thích từ đây (Chương 2).1
- Dữ liệu có thể nhiều (nhiều luồng dữ liệu). Dữ liệu thường ngày là “luồng không tên”;
tên-tệp:tênthêm được luồng. Bản chất Zone.Identifier (Mark of the Web) (Chương 3).2 - Tên cũng là thuộc tính. Gắn nhiều tên cùng bản ghi là hard link. Tên rút gọn 8.3 cũng sống cùng như “một tên nữa” (Chương 4).34
- Reparse point là cơ chế chính thức “mở thì sang chỗ khác”. Symbolic link, junction, Files On-Demand của OneDrive đều là ứng dụng dữ liệu gắn tag này (Chương 5).56
- Journal có hai.
$LogFileđể phục hồi nhất quán metadata (log trước để không hỏng), USN journal để ghi lịch sử thay đổi (sổ cái cái gì đã đổi). Vai trò hoàn toàn khác (Chương 6).78 - “Kích thước” và “kích thước trên đĩa” là hai thứ. Tệp sparse và nén sinh lệch. Bối cảnh “tệp nén không thành bất đồng bộ” kỳ 2 đã xem cũng nằm đây (Chương 7).910
Bản đồ tri thức của bài viết này
Trung tâm NTFS là MFT (Master File Table); mọi tệp được quản như bản ghi sổ cái trong MFT. Dữ liệu nhỏ resident trong bản ghi; dữ liệu lớn thành non-resident chỉ giữ tham chiếu dãy cluster (data run) — đó là bản chất chậm khi xử lý hàng loạt tệp nhỏ và phân mảnh. Nhiều luồng dữ liệu, hard link, tên rút gọn 8.3 đều là ứng dụng cùng cơ chế “tập thuộc tính”; reparse point là hook chính thức vào “mở”, chống lưng từ symbolic link đến Files On-Demand của OneDrive. $LogFile giữ nhất quán cấu trúc; độ bền nội dung dữ liệu phải xây riêng bằng công cụ điều khiển cache kỳ 4.
flowchart LR
accTitle: Bản đồ tri thức cấu trúc trong NTFS và MFT
accDescr: Sơ đồ cho thấy quan hệ NTFS, MFT, bản ghi tệp, thuộc tính resident và non-resident, data run, phân mảnh, nhiều luồng dữ liệu, Zone.Identifier, hard link, tên rút gọn 8.3, reparse point (symbolic link, junction, Files On-Demand của OneDrive), $LogFile và USN journal, tệp sparse và nén NTFS
ntfs["NTFS"]
mft["MFT (Master File Table)"]
mft_record["Bản ghi tệp MFT"]
mft_zone["MFT zone"]
fragmentation["Phân mảnh (NTFS)"]
resident_attribute["Thuộc tính resident"]
non_resident_attribute["Thuộc tính non-resident"]
data_run["Data run"]
fsutil["fsutil"]
alternate_data_stream["Luồng dữ liệu thay thế (ADS)"]
zone_identifier["Zone.Identifier (Mark of the Web)"]
streams_tool["streams (Sysinternals)"]
size_disk_usage_mismatch["Lệch kích thước logic và dung lượng đĩa"]
hard_link["Hard link"]
eight_dot_three_name["Tên rút gọn 8.3"]
reparse_point["Reparse point"]
symbolic_link["Symbolic link"]
junction["Junction (mount point)"]
onedrive_files_on_demand["Files On-Demand của OneDrive"]
ntfs_logfile["$LogFile (journal giao dịch NTFS)"]
volume_corruption["Hỏng volume"]
cache_manager["Cache Manager"]
usn_journal["USN journal (journal thay đổi)"]
sparse_file["Tệp sparse"]
ntfs_compression["Nén NTFS"]
asynchronous_io["I/O bất đồng bộ"]
procmon["Process Monitor (procmon.exe)"]
ntfs -->|"sử dụng"| mft
mft -->|"sử dụng"| mft_record
mft -->|"yêu cầu"| mft_zone
mft_zone -.->|"giảm thiểu"| fragmentation
mft_record -->|"sử dụng"| resident_attribute
mft_record -->|"sử dụng"| non_resident_attribute
resident_attribute -->|"không tương thích"| non_resident_attribute
non_resident_attribute -->|"sử dụng"| data_run
data_run -.->|"có thể gây"| fragmentation
fragmentation -->|"xác minh bằng"| fsutil
ntfs -->|"sử dụng"| alternate_data_stream
zone_identifier -->|"lưu trong"| alternate_data_stream
zone_identifier -->|"xác minh bằng"| streams_tool
alternate_data_stream -->|"xác minh bằng"| streams_tool
alternate_data_stream -.->|"có thể gây"| size_disk_usage_mismatch
ntfs -->|"sử dụng"| hard_link
hard_link -->|"yêu cầu"| mft_record
ntfs -.->|"sử dụng"| eight_dot_three_name
eight_dot_three_name -->|"cấu hình bằng"| fsutil
ntfs -->|"sử dụng"| reparse_point
reparse_point -->|"xác minh bằng"| fsutil
symbolic_link -->|"triển khai"| reparse_point
junction -->|"triển khai"| reparse_point
onedrive_files_on_demand -->|"triển khai"| reparse_point
ntfs -->|"sử dụng"| ntfs_logfile
ntfs_logfile -->|"giảm thiểu"| volume_corruption
ntfs -->|"sử dụng"| cache_manager
ntfs -->|"sử dụng"| usn_journal
usn_journal -->|"xác minh bằng"| fsutil
ntfs -->|"sử dụng"| sparse_file
sparse_file -->|"có thể gây"| size_disk_usage_mismatch
ntfs -->|"sử dụng"| ntfs_compression
ntfs_compression -->|"có thể gây"| size_disk_usage_mismatch
ntfs_compression -->|"không tương thích"| asynchronous_io
ntfs -->|"xác minh bằng"| procmon
Trong sơ đồ, đường liền nét biểu thị quan hệ luôn đúng và đường nét đứt biểu thị quan hệ có điều kiện (điều kiện nằm trong phần giải thích từng quan hệ trên trang chi tiết). Danh sách đầy đủ các quan hệ (tổng 35, kèm bằng chứng và mức chắc chắn) cùng định nghĩa các khái niệm chính được tập hợp tại trang chi tiết bản đồ tri thức (bằng tiếng Nhật). Dữ liệu: JSON-LD / Turtle
2. Mọi thứ đều là bản ghi MFT
2.1. Sổ cái của volume
Định dạng volume NTFS thì MFT (master file table) và loạt tệp metadata bắt đầu $ được tạo. MFT có ít nhất một mục cho mọi tệp trên volume, và cả mục của chính MFT.1
flowchart TB
subgraph VOL["Volume NTFS"]
MFT["$MFT ── Master File Table<br/>sổ cái bản ghi mọi tệp (chính nó cũng nằm trên)"]
LOG["$LogFile ── journal giao dịch<br/>thao tác metadata (Chương 6)"]
BITMAP["$Bitmap ── tình trạng dùng cluster"]
OTH["$Boot / $Secure / $UpCase, v.v.<br/>các tệp metadata khác"]
DATA["Vùng dữ liệu người dùng<br/>(chỗ đặt dữ liệu non-resident)"]
end
MFT -->|"bản ghi chỉ vị trí"| DATA
Hình 1: Cấu trúc volume NTFS. Thiết kế NTFS là “thông tin quản lý của chính hệ thống tệp cũng giữ như tệp”
Thông tin về tệp ── kích thước, dấu thời gian, quyền truy cập, đến cả nội dung dữ liệu ── lưu trong mục MFT, hoặc lưu vùng ngoài MFT mà mục MFT mô tả vị trí.1 Xóa tệp thì mục được đánh dấu “trống” và tái sử dụng, nhưng bản thân MFT không co. Thêm nữa, để giữ MFT liên tục, vùng gọi là MFT zone được dành trước; volume đầy dần thì phân mảnh MFT bắt đầu ── chuyện tuổi thọ này cũng ghi trong tài liệu chính thức.1
2.2. Tệp = tập thuộc tính, resident và non-resident
Nội dung bản ghi tệp là danh sách thuộc tính. Thông tin chuẩn (dấu thời gian, v.v.), tên tệp, bảo mật, rồi dữ liệu. Ở đây có nhánh then chốt.
flowchart TB
subgraph REC["Bản ghi tệp MFT (sổ cái một tệp)"]
STD["Thuộc tính thông tin chuẩn<br/>dấu thời gian, cờ thuộc tính"]
FN["Thuộc tính tên tệp<br/>(có thể nhiều ── Chương 4)"]
DATA["Thuộc tính dữ liệu"]
end
Q{"Dữ liệu có nhỏ không"}
RES["Resident<br/>bản thân dữ liệu nằm gọn trong bản ghi<br/>đọc xong chỉ bằng truy cập MFT"]
NONRES["Non-resident<br/>bản ghi chỉ có 'tham chiếu dãy cluster'<br/>dữ liệu thật nằm vùng dữ liệu người dùng"]
DATA --> Q
Q -->|"đến khoảng vài trăm byte"| RES
Q -->|"lớn hơn"| NONRES
Hình 2: Bản ghi tệp là tập thuộc tính. Dữ liệu nhỏ thì “resident” trong bản ghi
Xếp nội dung cùng bản ghi, resident và non-resident đổi thế nào, nhìn cạnh nhau thì dễ.
flowchart LR
subgraph RES2["Resident ── tệp nhỏ"]
RA["Bản ghi tệp MFT (độ dài cố định)<br/>thông tin chuẩn / tên tệp / bảo mật<br/>─────────────<br/>thuộc tính dữ liệu = chính nội dung<br/>'giá trị cài = 1' nằm thẳng đây"]
RB["Trên đĩa không có chỗ đặt riêng<br/>đọc xong chỉ bằng truy cập MFT"]
RA --> RB
end
subgraph NON2["Non-resident ── tệp lớn"]
NA["Bản ghi tệp MFT (độ dài cố định)<br/>thông tin chuẩn / tên tệp / bảo mật<br/>─────────────<br/>thuộc tính dữ liệu = bảng data run<br/>dãy 'từ đâu, bao nhiêu cluster'"]
NB["Vùng dữ liệu người dùng<br/>run 1: cluster liên tục"]
NC["Vùng dữ liệu người dùng<br/>run 2: cluster liên tục chỗ khác"]
NA -->|"chỉ vị trí"| NB
NA -->|"chỉ vị trí"| NC
end
Hình 3: Đối chiếu resident và non-resident. Non-resident thì bản ghi chỉ giữ bảng “dữ liệu thật ở đâu, bao nhiêu” (data run)
Số data run càng nhiều, đọc một tệp càng phải đi qua vùng rải. Đó là bản chất phân mảnh nói tiếp.
Từ cấu trúc này, nhiều hiện tượng gặp tại hiện trường giải thích được.
- Lý do sao chép 10.000 tệp nhỏ chậm. Mỗi tệp phát sinh thao tác metadata: tạo bản ghi MFT, đăng ký tên, đặt bảo mật. Việc sổ cái thống trị hơn chính truyền dữ liệu (và từng cái còn thành đối tượng kiểm của filter kỳ 6 sẽ xem).
- Bản chất phân mảnh. Dữ liệu non-resident ghi như “dãy đoạn cluster liên tục (run)”. Không lấy được vùng liên tục thì số run tăng, seek lúc đọc tăng ── đó là phân mảnh. Dãy run thực xem được bằng
fsutil file layout. - “Thư mục” cũng không đặc biệt. Thư mục là “tệp giữ chỉ mục (index) từ tên tệp sang số bản ghi MFT”. Trên sổ cái, mọi thứ ngồi trên cùng một cơ chế.
3. Dữ liệu chỉ là một trong các “stream”
3.1. Một tệp, nhiều dãy byte
Trên NTFS, một tệp có thể giữ nhiều luồng dữ liệu. Thứ ReadFile/WriteFile đọc ghi hàng ngày là luồng mặc định không tên; cú pháp tên-tệp:tên-stream tạo được luồng dữ liệu thay thế (ADS).2
flowchart LR
subgraph F["Tệp report.docx (một bản ghi MFT)"]
D0["Luồng mặc định (không tên)<br/>= nội dung thường thấy"]
D1[":Zone.Identifier<br/>thông tin nguồn (Mark of the Web)"]
D2[":tên tùy ý<br/>thông tin thêm riêng ứng dụng"]
end
Hình 4: Nhiều luồng dữ liệu. Kích thước Explorer hiện chỉ luồng mặc định
ADS gần nhất là Zone.Identifier. Tệp tải bằng trình duyệt được ghi nguồn (từ Internet, v.v.) vào luồng này, thành nguyên liệu SmartScreen “Windows đã bảo vệ PC của bạn” và Protected View của Office. Mặt trước cơ chế này đã xử trong “Vì sao Windows hiện ‘Windows đã bảo vệ PC của bạn’” ── bản chất mặt sau chỉ là stream NTFS.
3.2. Bẫy nhà phát triển giẫm
- Không thấy. Không hiện kích thước Explorer, cũng không danh sách
dir. Xem bằngdir /rhoặcstreamscủa Sysinternals.11 - Không mang đi được. ADS là chức năng NTFS nên sao chép qua USB FAT hay lưu trữ đám mây hay mất. “Cảnh báo tải về biến khi sao chép” là đây.
- Ứng dụng mình cũng mở được. Chỉ cần đường có dấu hai chấm kiểu
CreateFile("data.txt:meta", ...)là đọc ghi được.2 Tiện, nhưng nhận luôn tính “không mang đi được” mục trước, nên không phải chỗ đặt thân dữ liệu nghiệp vụ.
4. Tên cũng là thuộc tính ── hard link và tên 8.3
4.1. Hard link ── nhiều tên cùng một bản ghi
Hình 2 đã viết “thuộc tính tên tệp có thể nhiều”. Trong cùng volume, nhiều đường tham chiếu một tệp ── đó là hard link (CreateHardLink / mklink /H).3
flowchart TB
subgraph DIR1["Chỉ mục C:\app\ "]
E1["config.json → bản ghi #1234"]
end
subgraph DIR2["Chỉ mục C:\backup\ "]
E2["config-link.json → bản ghi #1234"]
end
REC["Bản ghi MFT #1234<br/>thân dữ liệu (hoặc tham chiếu run)<br/>số liên kết: 2"]
E1 --> REC
E2 --> REC
Hình 5: Hard link. Chỉ mục thư mục chỉ cùng bản ghi MFT; bên nào cũng là “thật”
Đổi từ tên nào cũng cùng tệp nên nội dung khớp ngay.3 Và nghĩa “xóa” đổi ── DeleteFile là “gỡ một tên”, thực thể chỉ mất khi tên cuối đã gỡ, handle đang mở đã đóng, và mọi tham chiếu trong kernel như section memory-map cũng biến hết. Hai tầng cleanup (handle cuối) và close (tham chiếu cuối) kỳ 1 đã xem, cũng đúng nguyên vào tuổi thọ xóa. Hiển thị thuộc tính còn tính khí: đổi thuộc tính qua một liên kết, hiển thị qua liên kết khác vẫn cũ ── hành vi được chú thích chính thức.3
4.2. Tên 8.3 ── một tên ẩn nữa
Vì tương thích lịch sử, NTFS có thể tự sinh tên rút gọn dạng 8.3 kiểu REPORT~1.DOC cho tên dài. Cái này cũng sống cùng bản ghi như “một tên nữa”. Thư mục nhiều tệp thì sinh tên rút gọn và tránh đụng thành chi phí, nên fsutil 8dot3name tắt sinh hoặc gỡ tên rút gọn sẵn có được (ứng dụng cũ ghi đường registry bằng tên rút gọn thì hỏng, nên có chức năng quét trước khi strip ── điểm thực tế).4
Điểm cần nhớ: có tên rút gọn hay không phụ thuộc môi trường. Hành vi mặc định quyết bởi giá trị registry NtfsDisable8dot3NameCreation: 0 (sinh mọi volume), 1 (không sinh mọi volume), 2 (cài theo volume), 3 (không sinh ngoài volume hệ thống).4 Chọn 2 thì chuyển theo volume được, nên không phải “Windows thì chắc chắn có tên rút gọn kiểu PROGRA~1”. Trước khi viết mã hay quy trình phụ thuộc tên rút gọn, hãy xác nhận trạng thái hiện tại bằng fsutil 8dot3name query C: (bỏ volume thì ra cài đặt mặc định chung mọi volume).
Bẫy quanh đường và tên (MAX_PATH, tên dành riêng, dấu chấm cuối) xử chi tiết trong “MAX_PATH và bẫy đường, tên tệp trên Windows”. Ghép phân giải tên kỳ 1 (Object Manager) với chương này (tên trong hệ thống tệp) thì thành toàn cảnh “tên” trên Windows.
5. Reparse point ── cơ chế “mở thì sang chỗ khác”
Tệp và thư mục gắn được reparse point. Thực thể là thuộc tính “tag + dữ liệu người dùng định nghĩa”. Hệ thống tệp mở tệp có reparse point thì xử lý bị cướp theo tag ── filter driver hiểu tag nhận xử lý, hoặc tag kiểu đổi tên thì phân giải lại bằng đường đích.5
sequenceDiagram
participant App as Ứng dụng
participant IOM as I/O Manager
participant FS as NTFS
App->>IOM: CreateFile("C:\\data\\link.txt")
IOM->>FS: IRP_MJ_CREATE (thế giới kỳ 1)
Note over FS: Phát hiện reparse point trên đích<br/>trả tag và dữ liệu
alt Symbolic link / junction (đổi tên)
FS-->>IOM: "chỗ thật là đây"
IOM->>FS: phân giải lại bằng đường đích
else Tag do filter quản (tệp đám mây, v.v.)
Note over FS: Filter hiểu tag<br/>nhận xử lý (kỳ 6)
end
Hình 6: Phân giải reparse point. Thành hook chính thức cắt vào thao tác “mở”
Trên một cơ chế này, các chức năng quen xếp hàng.
- Symbolic link (
mklink) ── biển giữ đường đích. Chỉ được volume khác hay đường UNC.6 - Junction / mount point ── cơ chế kỳ cựu nối thư mục sang vị trí volume cục bộ khác.3
- Files On-Demand của OneDrive ── tệp chưa có dữ liệu thật trong tay biểu diễn bằng reparse point; lúc mở, filter tải rồi đưa nội dung. Bản chất “Explorer thấy mà mở thì chạy mạng” (bản thân cơ chế filter ở kỳ 6).
Lưu ý thực tế một. “Đích đường chưa chắc là đúng chỗ cục bộ đó”. Công cụ đi cây đệ quy vòng vì junction, tổng kích thước đếm kép, sao lưu kích hoạt ồ ạt thực thể hóa đám mây ── mã không biết reparse point giẫm những cái này. Xác nhận thuộc tính FILE_ATTRIBUTE_REPARSE_POINT của họ FindFirstFile là cửa vào đối phó.5
6. Hai journal ── $LogFile và USN
Hay nói “NTFS là hệ thống tệp journaling”, nhưng NTFS có hai journal vai trò khác. Nhầm thì đọc sai bảo đảm.
flowchart TB
subgraph J1["$LogFile ── log trước (để không hỏng)"]
A1["Ghi thao tác metadata (cập nhật bản ghi, đổi tên, v.v.)<br/>vào log trước khi thực thi"]
A2["Lần khởi động sau sự cố hệ thống<br/>phát lại log để phục hồi nhất quán cấu trúc"]
A1 --> A2
end
subgraph J2["USN journal ── lịch sử đổi (để biết cái gì đã đổi)"]
B1["Mỗi lần đổi tệp/thư mục<br/>ghi nội dung đổi và tên"]
B2["Công cụ sao lưu, chỉ mục tìm, đồng bộ<br/>nắm 'từ lần trước cái gì đổi' mà không quét hết"]
B1 --> B2
end
Hình 7: Hai journal. $LogFile để “không hỏng”, USN để “biết thay đổi”
Khác biệt thành bảng như sau.
| Góc nhìn | $LogFile (journal giao dịch) |
USN journal (journal thay đổi) |
|---|---|---|
| Mục đích | Sau sự cố đưa cấu trúc hệ thống tệp về trạng thái nhất quán7 | Sau này biết “từ lần trước cái gì đã đổi”8 |
| Nội dung ghi | Log trước thao tác metadata (cập nhật bản ghi, đổi tên, v.v.). Nội dung tệp không thuộc đối tượng | Mỗi lần đổi, nội dung đổi và tên tệp/thư mục đích8 |
| Ai dùng | Chính NTFS. Dùng phục hồi tự động lúc mount lần sau | Ứng dụng như sao lưu, chỉ mục tìm, công cụ đồng bộ |
| Truy ngược đến đâu | Chỉ phạm vi cần phục hồi. Dùng lại kích thước cố định, không dùng để lần lịch sử cũ | Vượt kích thước đích (MaximumSize) thì lúc checkpoint cắt bản ghi cũ. Phạm vi truy ngược tùy cài kích thước và lượng cập nhật volume12 |
| Cách xem | Không có cách chính thức đọc nội dung (kích thước xem bằng chkdsk /L) |
Trạng thái bằng fsutil usn queryjournal, nội dung bằng fsutil usn readjournal. Chương trình dùng FSCTL_QUERY_USN_JOURNAL / FSCTL_READ_USN_JOURNAL12 |
| Dừng được không | Không dừng được (một phần NTFS) | Quản trị viên xóa, tắt được. Nhưng ép dịch vụ đang dùng phải quét hết nên ảnh hưởng lớn12 |
Bảng 2: Đối chiếu hai journal
$LogFile(journal giao dịch) là log trước thao tác metadata. Sự cố hệ thống xảy ra, NTFS lần khởi động sau tự phục hồi nhất quán hệ thống tệp từ log này và thông tin checkpoint.7 Chỗ được giữ là cấu trúc. Như kỳ 4, nội dung dữ liệu dirty trên cache có thể mất khi mất điện ── đọc đúng là “volume không hỏng. Nhưng ghi cuối có thể mất”.- USN journal (journal thay đổi) là sổ cái, mỗi lần tệp hay thư mục trong volume đổi thì ghi nội dung đổi và tên đích.8 Cơ chế để sao lưu hay indexer lấy “chỉ cái đổi từ lần trước” mà không quét hết, cũng dùng tránh dựng lại chỉ mục sau sự cố.8 Thực tế, nhớ dùng được
fsutil usn readjournallàm sổ đối chiếu bổ cho sót củaFileSystemWatcher(“Hướng dẫn thực tế FileSystemWatcher”) thì hữu ích lúc điều tra.
7. Sparse và nén ── chuyện “kích thước” có hai
Trên NTFS, độ dài logic của tệp và vùng thực sự được cấp được quản riêng. Đó là “Size” và “Size on disk” trên màn hình thuộc tính. Hai đại diện sinh lệch.
Tệp sparse không cấp vùng thật cho khoảng toàn số không, quản như “lỗ”.9 Tệp đĩa ảo kích thước logic 42GB, trên đĩa chỉ dùng 500MB ── xảy ra thường. Đọc lỗ thì trả không; ghi thì cấp đúng phần đó.
flowchart LR
subgraph L["Tệp logic (kích thước: 1GB)"]
R1["Dữ liệu 10MB"]
H1["Lỗ (số không) 500MB"]
R2["Dữ liệu 5MB"]
H2["Lỗ (số không) phần còn"]
end
subgraph P["Cấp trên đĩa (15MB + thông tin quản lý)"]
A1["Run: thực thể R1"]
A2["Run: thực thể R2"]
end
R1 --> A1
R2 --> A2
Hình 8: Tệp sparse. “Lỗ” không có cấp phát; kích thước logic và kích thước trên đĩa lệch
Nén NTFS nén dữ liệu theo đơn vị nén rồi lưu.10 Trong suốt, tiện, nhưng chi phí cũng không trong suốt ── mỗi đọc ghi chạy bung và nén lại, phân mảnh cũng dễ tiến. Và như mục 5 kỳ 2, truy cập tệp nén không thành bất đồng bộ (hệ thống tệp chuyển sang đồng bộ). Một chỗ đáng nghi khi “đã làm I/O bất đồng bộ mà tệp nào đó không nhanh”.
Kích thước thật theo cấp lấy được bằng GetCompressedFileSize. Điều tra “tổng kích thước tệp” và “dung lượng đĩa dùng” không khớp thì lần lượt nghi sparse, nén, ADS (Chương 3), làm tròn cluster ── đó là lối quen.
8. Tự mắt thấy
Kỳ này cũng quan sát hết trên Windows trong tay (một phần cần quyền quản trị viên). Để tự phán đầu ra đúng không, mỗi lệnh kèm nhìn chỗ nào thì biết gì.
:: Xem luồng dữ liệu thay thế
dir /r C:\Users\%USERNAME%\Downloads
Nhìn đây: dưới dòng tệp thường, dòng dạng tên-tệp:Zone.Identifier:$DATA thụt vào xếp kèm độ dài. Có dòng đó thì tệp đang gắn Mark of the Web (Chương 3). Tệp tải bằng trình duyệt thì có; tệp tự tạo thì không. Chạy cả hai chỗ rồi so thì ADS có hay không rõ.
:: Xem bố trí (run) và thuộc tính của tệp trên MFT
fsutil file layout C:\path\to\file.dat
:: Chỉ xem extent (lệnh con ghi trong tài liệu chính thức)
fsutil file queryextents C:\path\to\file.dat
Nhìn đây: layout theo từng stream xếp kích thước, kích thước cấp, và nếu non-resident thì danh sách extent (bộ VCN, LCN, số cluster). Tệp rất nhỏ không ra dòng extent là resident (mục 2.2); tách nhiều dòng thì đang phân mảnh. Chạy so tệp văn bản vài byte và tệp vài trăm MB là cách ngắn nhất cảm resident/non-resident.
:: Cài đặt sinh tên rút gọn 8.3, và tên rút gọn sẵn có
fsutil 8dot3name query C:
dir /x
Nhìn đây: query trả volume đó sinh tên rút gọn đang bật hay tắt (bỏ volume thì ra cài đặt mặc định chung mọi volume).4 dir /x hiện cột tên rút gọn cạnh tên dài, cột trống thì tên rút gọn chưa được tạo. Tự xác nhận “không chắc có tên rút gọn” mục 4.2 trên môi trường mình.
:: Trạng thái của USN journal
fsutil usn queryjournal C:
Nhìn đây: hiện journal ID, phạm vi USN hiệu lực (First USN / Next USN), kích thước đích (MaximumSize) và đơn vị cấp (AllocationDelta).12 Tạo tệp rồi chạy lần nữa thì Next USN phải tiến; đó là xác nhận “thay đổi đang được ghi”. MaximumSize là thước “truy ngược đến đâu” Chương 6 đã nói. Volume journal tắt thì lỗi.
:: Xác nhận reparse point (đường đích và tag)
dir /aL C:\Users\%USERNAME%
fsutil reparsepoint query "C:\Users\%USERNAME%\OneDrive"
Nhìn đây: cái dir /aL ra là reparse point (FILE_ATTRIBUTE_REPARSE_POINT đang bật). Symbolic link hay junction hiện kèm loại kiểu <SYMLINKD> <JUNCTION>. fsutil reparsepoint query hiện giá trị reparse tag, và nếu kiểu đổi tên thì đường đích. Chỉ định đích không phải reparse point thì lỗi, nên chính việc lỗi là xác nhận “đây là thư mục thường”.
Theo thao tác tệp bằng Procmon, nhân vật kỳ này chảy đúng tên thật (ghi $LogFile, đường kèm tên stream, xử lý reparse). Cách dùng xem “Hướng dẫn thực tế Process Monitor (ProcMon)”.
9. Tóm tắt
- Trung tâm NTFS là MFT. Mọi tệp là bản ghi sổ cái; thông tin nằm “trong bản ghi” hoặc “vùng ngoài bản ghi chỉ”. Dữ liệu nhỏ thì resident, dữ liệu lớn thì tham chiếu run; chậm xử lý hàng loạt tệp nhỏ và phân mảnh là hệ quả cấu trúc này.1
- Luồng dữ liệu có thể nhiều. Zone.Identifier (Mark of the Web) chỉ là ADS; thấy bằng
dir /r, không mang ra ngoài NTFS.211 - Tên là thuộc tính, có thể nhiều. Hard link là tên ngang hàng cùng bản ghi; tên 8.3 là tên nữa vì tương thích. “Xóa = gỡ tên”; thực thể mất khi tên cuối, handle, tham chiếu trong kernel (section đã map, v.v.) đều hết.34
- Reparse point là hook chính thức vào “mở”; symbolic link, junction, Files On-Demand đều là ứng dụng này. Mã đi cây cần ý thức
FILE_ATTRIBUTE_REPARSE_POINT.56 - Journal có hai.
$LogFilephục hồi nhất quán cấu trúc (không hỏng), USN lịch sử đổi (cái gì đã đổi). Không phải “journaling nên dữ liệu cũng an toàn” ── độ bền dữ liệu xây bằng công cụ kỳ 4.78 - Kích thước logic và cấp phát là hai thứ. Sparse, nén, ADS, làm tròn cluster là bốn nguyên nhân lớn “kích thước không khớp”. Kể cả chuyện tệp nén không thành I/O bất đồng bộ, thành ngăn kéo điều tra hiệu năng.910
Tiếp là kỳ cuối, kỳ 6 “Filter driver và minifilter ── vì sao Procmon và quét virus cắt được vào I/O”. Những kẻ chen giữa ── chống virus, Procmon, OneDrive, mã hóa ── đã thỉnh thoảng lộ mặt từ kỳ 1, cắt vào I/O thế nào. Tổng kết loạt, vạch mặt cư dân đứng khe device stack.
Bài viết liên quan
- Đáy I/O Windows (kỳ 1) ── mọi đọc ghi thành IRP: toàn cảnh hệ thống I/O
- Đáy I/O Windows (kỳ 2) ── I/O đồng bộ và bất đồng bộ: nghĩa thật của OVERLAPPED
- Đáy I/O Windows (kỳ 4) ── Cache Manager: WriteFile của bạn đến đĩa khi nào
- Vì sao Windows hiện “Windows đã bảo vệ PC của bạn”
- MAX_PATH và bẫy đường, tên tệp trên Windows ── giới hạn 260 ký tự, tên dành riêng, dấu chấm cuối, hoa thường
- Hướng dẫn thực tế FileSystemWatcher ── đối phó sót và trùng
- Bẫy ổ mạng và đường UNC ── thực tế ứng dụng nghiệp vụ xử lý file server (thư mục chia sẻ)
- Hướng dẫn thực tế Process Monitor (ProcMon) ── xác định “cài đặt không được đọc”, “ACCESS DENIED” trong 10 phút
Lĩnh vực tư vấn liên quan
KomuraSoft LLC nhận thiết kế và điều tra ứng dụng nghiệp vụ Windows bám cơ chế NTFS, kiểu hành vi kích thước tệp hay hiệu năng sao chép khó hiểu, lỗi dính liên kết hay stream.
- Phát triển ứng dụng Windows
- Điều tra lỗi và nguyên nhân
- Tái sử dụng và di chuyển tài sản hiện có
- Liên hệ với chúng tôi
Liên kết tham khảo
-
Microsoft Learn, Master File Table. Về mọi tệp trên volume NTFS có ít nhất một mục trên MFT, cả mục của chính MFT; mọi thông tin gồm kích thước, dấu thời gian, quyền truy cập, nội dung dữ liệu lưu trong mục MFT hoặc vùng ngoài MFT mà mục mô tả vị trí; lúc xóa tệp mục được đánh dấu trống và tái sử dụng nhưng kích thước MFT không co; MFT zone được dành trước để giữ MFT liên tục; cấp phát tiến thì phân mảnh MFT xảy ra. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, File Streams. Về dữ liệu tệp NTFS lưu thành một hay nhiều stream; có luồng dữ liệu mặc định (không tên) và luồng dữ liệu thay thế có tên; chỉ định stream dạng “tên-tệp:tên-stream” rồi mở bằng CreateFile. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Hard links and junctions. Về hard link là biểu diễn hệ thống tệp nhiều đường trong cùng volume tham chiếu một tệp; tạo bằng CreateHardLink; đổi qua liên kết nào cũng thấy ngay từ liên kết khác; đổi thuộc tính lan mọi hard link, nhưng hiển thị trên mục thư mục chỉ cập nhật ở liên kết đã đổi; và về junction (cơ chế nối thư mục sang volume cục bộ khác). ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, fsutil 8dot3name. Về NTFS sinh được tên rút gọn dạng 8.3 cho tên dài; fsutil 8dot3name tra và cài bật/tắt sinh tên rút gọn, gỡ (strip) tên rút gọn sẵn có, quét tham chiếu registry bị ảnh hưởng nếu gỡ. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Reparse points. Về reparse point là tập dữ liệu người dùng định nghĩa và reparse tag nhận diện duy nhất dạng dữ liệu đó; lúc mở tệp có reparse point, hệ thống tệp thử xử lý tương ứng tag (xử lý bởi file system filter hiểu tag); dùng triển khai liên kết hệ thống tệp NTFS và lưu trữ từ xa (lưu trữ phân tầng); xác nhận tồn tại bằng thuộc tính FILE_ATTRIBUTE_REPARSE_POINT. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Symbolic links. Về symbolic link là đối tượng hệ thống tệp chỉ tệp hay thư mục khác, hoạt động như chuyển hướng trong suốt tới đích; có liên kết tuyệt đối và tương đối, tham chiếu xuyên volume hay đường remote được. ↩ ↩2 ↩3
-
Microsoft Learn, NTFS overview. Về NTFS dùng tệp log và thông tin checkpoint, sự cố hệ thống xảy ra thì lần khởi động sau phát lại journal giao dịch để tự phục hồi nhất quán hệ thống tệp; có remap động sector hỏng và self-healing NTFS sửa hỏng nhẹ nền. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Change Journals. Về mỗi lần tệp hay thư mục trong volume bị đổi, nội dung đổi và tên tệp/thư mục đích được ghi vào USN change journal của volume đó; journal duy trì theo volume; dùng phục hồi chỉ mục hệ thống tệp sau sự cố, tránh lập chỉ mục lại cả volume. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Sparse Files. Về tệp sparse không cấp vùng đĩa vật lý cho khoảng lớn toàn số không, chỉ cấp vùng phần chứa dữ liệu; đọc khoảng không cấp thì trả không. ↩ ↩2 ↩3
-
Microsoft Learn, File Compression and Decompression. Về nén tệp NTFS diễn ra trong suốt, dữ liệu được nén và lưu theo đơn vị nén; GetCompressedFileSize lấy kích thước sau nén (cấp thật); đọc ghi tệp nén kèm chi phí bung và nén lại. ↩ ↩2 ↩3
-
Microsoft Learn, Streams - Sysinternals. Về tiện ích streams của Sysinternals liệt kê và xóa được luồng dữ liệu thay thế của tệp NTFS. ↩ ↩2
-
Microsoft Learn, Creating, Modifying, and Deleting a Change Journal và fsutil usn. Về MaximumSize của change journal là giá trị đích; kích thước vượt tổng MaximumSize và AllocationDelta thì bị cắt lúc checkpoint NTFS; AllocationDelta là đơn vị thêm cuối journal và xóa từ đầu; fsutil usn queryjournal xem trạng thái và dung lượng journal, readjournal xem nội dung ghi; chương trình dùng FSCTL_CREATE_USN_JOURNAL / FSCTL_QUERY_USN_JOURNAL / FSCTL_READ_USN_JOURNAL / FSCTL_DELETE_USN_JOURNAL; xóa hay tắt journal đang hoạt động kèm quét cả MFT, ép dịch vụ đang dùng journal phải quét lại volume. ↩ ↩2 ↩3 ↩4
Bài viết liên quan
Các bài viết gần đây có cùng thẻ để tìm hiểu sâu hơn những chủ đề lân cận.
Đáy I/O Windows (kỳ 4) ── Cache Manager: WriteFile của bạn đến đĩa khi nào
Kỳ 4 loạt bài hình giải Cache Manager của Windows. Sắp xếp cache triển khai như file mapping, đọc trước và ghi trễ, cách dùng FlushFileBu...
Đáy I/O Windows (kỳ 6, cuối) ── filter driver và minifilter: vì sao Procmon và quét virus cắt được vào I/O
Kỳ cuối loạt bài hình giải filter driver và minifilter của Windows. Sắp xếp Filter Manager và altitude, callback pre/post, cơ chế Procmon...
Thực hành tốt nhất về đa luồng: ấn bản .NET — Những gì cần quyết trước khi thêm luồng
Tổng hợp các định thức thiết kế ngăn «thêm luồng rồi thỉnh thoảng crash hoặc treo» cho .NET/C#. Bài viết đi từ việc dựa trên Task thay vì...
Volume Shadow Copy (VSS): cơ chế và thực hành — vì sao phần mềm sao lưu copy được tệp đang dùng
Tệp đang dùng thường không copy được vì vi phạm chia sẻ — vậy phần mềm sao lưu làm thế nào? Bài viết giải thích vai trò requester, writer...
OneDrive "Tệp theo yêu cầu" và ứng dụng nghiệp vụ — giả định mà chỗ giữ chỗ phá vỡ và cách xử lý
CSV trên máy tính không mở được, hoặc nhập thất bại với "không tìm thấy tệp" — nguyên nhân có thể là Known Folder Move và Tệp theo yêu cầ...
Chủ đề liên quan
Các trang này đặt chủ đề trong bối cảnh rộng hơn của dịch vụ và quyết định.
Chủ đề kỹ thuật Windows
Cổng vào phát triển Windows, điều tra lỗi và khai thác tài sản hiện có.
Dịch vụ liên quan đến chủ đề này
Bài viết liên quan trực tiếp đến các dịch vụ sau.
Phát triển ứng dụng Windows
Ứng dụng nghiệp vụ, tích hợp thiết bị và công cụ liên lạc, từ yêu cầu đến phát triển.
Câu hỏi thường gặp
Các câu hỏi thường gặp khi tư vấn về chủ đề của bài viết.
- MFT (Master File Table) là gì?
- Cấu trúc dữ liệu trái tim của volume NTFS; sổ cái giữ ít nhất một mục (bản ghi tệp) cho mọi tệp trên volume. Cả mục của chính MFT cũng nằm trong đó. Mọi thông tin về tệp ── kích thước, dấu thời gian, quyền truy cập, đến bản thân dữ liệu ── nằm trong mục MFT hoặc vùng ngoài MFT mà mục đó chỉ. Tệp nhỏ thì cả dữ liệu nằm gọn trong mục MFT (resident); tệp lớn thì mục chỉ ghi tham chiếu chỗ đặt dữ liệu (dãy cluster) (non-resident). Xóa tệp thì mục được đánh dấu trống và tái sử dụng, nhưng kích thước bản thân MFT không co.
- Dữ liệu vô hình "Zone.Identifier" gắn tệp là gì?
- Một trong các luồng dữ liệu thay thế (alternate data stream) của NTFS. NTFS cho một tệp giữ nhiều dãy byte (stream); thứ ta đọc ghi hàng ngày là luồng mặc định không tên. Luồng thêm chỉ định kiểu "file.txt:Zone.Identifier" (phân tách bằng dấu hai chấm) được Windows ghi nguồn tệp (tải từ Internet, v.v.). Đó chính là "Mark of the Web", nguyên liệu SmartScreen cảnh báo và Protected View của Office phán đoán. Luồng thay thế không hiện trong kích thước Explorer; xem bằng lệnh dir /r hoặc công cụ streams của Sysinternals. Chú ý: sao chép sang hệ thống tệp khác NTFS (FAT, v.v.) thì không giữ.
- Hard link và symbolic link khác nhau thế nào?
- Hard link là "thêm một tên ngang hàng chỉ cùng thực thể tệp (cùng bản ghi MFT)". Chỉ tạo trong cùng volume; truy cập từ tên nào cũng cùng tệp; xóa một tên, tên khác còn thì tệp không mất. Symbolic link là "biển chỉ đường sang đường khác", triển khai như reparse point. Chỉ giữ chuỗi đường đích, nên chỉ được volume khác hay remote, nhưng đích mất thì thành đường cụt. Thực tế: hard link dùng chia sẻ thực thể (nghĩa xóa đổi), symbolic link dùng đổi đường (dời, chuyển hướng).
- NTFS là hệ thống tệp journaling nên mất điện dữ liệu cũng không mất?
- Cần hiểu đúng phạm vi được giữ. Journal giao dịch ($LogFile) của NTFS giữ tính nhất quán cấu trúc (metadata) của hệ thống tệp. Sự cố hệ thống xảy ra, lần khởi động sau dùng log tự phục hồi nhất quán, ngăn "volume hỏng không đọc được". Nhưng nội dung dữ liệu của tệp đang ghi dở không được khôi phục. Như kỳ 4, dữ liệu dirty trên cache mất khi mất điện. Hiểu đúng là "volume không hỏng, nhưng nội dung ghi cuối có thể mất"; cần độ bền của chính dữ liệu thì phải xây bằng FlushFileBuffers, WRITE_THROUGH, hoặc thiết kế ghi phía ứng dụng (tệp tạm + đổi tên, v.v.).
- "Kích thước" tệp và "kích thước trên đĩa" khác nhau vì sao?
- Vì NTFS quản riêng độ dài logic của tệp và vùng đĩa thực sự được cấp. Tệp thường cũng cấp làm tròn theo cluster (mặc định 4KB) nên đã lệch; lệch lớn là tệp sparse và tệp nén. Tệp sparse không cấp vùng thật cho khoảng toàn số không, quản như "lỗ", nên kích thước logic vài GB mà trên đĩa vài MB vẫn xảy ra. Tệp nén thì chỉ cấp kích thước sau nén. Ngược lại "kích thước trên đĩa" trông lớn hơn thì có thể do làm tròn cluster hoặc luồng dữ liệu thay thế.