Mở đầu: mỗi tiến trình sống trong ảo tưởng của riêng nó

Bài 7 tăng tốc truy cập bộ nhớ VẬT LÝ bằng cache. Nhưng địa chỉ mà một chương trình THẤY (vd con trỏ trong C, biến trong JS engine) hiếm khi là địa chỉ VẬT LÝ thật trên thanh RAM — hệ điều hành chèn một tầng gián tiếp: mỗi tiến trình có một không gian địa chỉ ẢO (virtual) riêng, hoàn toàn cách ly với tiến trình khác, được dịch sang địa chỉ VẬT LÝ (physical) thật ở mỗi lần truy cập. Cái giá của tầng gián tiếp này — và cách phần cứng che giấu nó — là chủ đề bài này.


📚 Điều kiện tiên quyết
Bắt buộc đọc Bài 7 (Cache) — TLB trong bài này CHÍNH LÀ một cache, chỉ khác đối tượng lưu trữ (bản dịch địa chỉ thay vì dữ liệu chương trình).

1. Bộ nhớ ảo (Virtual Memory) & Phân trang (Paging)

Bộ nhớ ảo cho phép: (1) chạy chương trình LỚN HƠN RAM vật lý (phần không dùng tới nằm trên đĩa, nạp vào RAM khi cần), và (2) CÔ LẬP an toàn — tiến trình A không thể vô tình (hay cố ý) đọc/ghi bộ nhớ của tiến trình B, vì địa chỉ ảo của A và B được dịch sang các VÙNG vật lý khác nhau hoàn toàn. Bộ nhớ được chia thành các trang (page) kích thước cố định — mặc định thường 4KB — và Page Table (bảng trang) ghi nhớ mỗi trang ẢO đang trỏ tới trang VẬT LÝ (khung trang — page frame) nào.

virtual_address_layout.txt (cấu trúc địa chỉ ảo, trang 4KB)
Dia chi ao (32-bit vi du):
+----------------------------+---------------+
|   VPN (Virtual Page Number)|   Offset      |
|          20 bit            |    12 bit     |
+----------------------------+---------------+
        tra Page Table            byte TRONG trang (khong doi khi dich)

# VPN tra Page Table -> PFN (Physical Frame Number)
# Dia chi vat ly = (PFN << 12) | Offset  (offset GIU NGUYEN, chi VPN duoc dich)
⚠️ Cạm bẫy: Thrashing khi thiếu RAM vật lý
Khi tổng nhu cầu bộ nhớ của mọi tiến trình vượt quá RAM vật lý, hệ điều hành phải liên tục "tráo" trang giữa RAM và đĩa (swap). Nếu chương trình truy cập các trang theo kiểu không có locality tốt (Bài 7), CPU dành gần như TOÀN BỘ thời gian để tráo trang thay vì tính toán thật — gọi là Thrashing, khiến hệ thống trông như "đơ" dù CPU vẫn đang chạy hết công suất (chỉ là chạy công việc tráo trang, không phải công việc hữu ích).

2. Tính toán cấu trúc & dung lượng Page Table

Với địa chỉ ảo $A$ bit và trang $O$ bit ($2^O$ byte/trang), số trang ảo TỐI ĐA có thể có là $2^{A-O}$. Page Table ĐƠN CẤP ngây thơ nhất cấp phát ĐỦ chỗ cho MỌI trang ảo có thể có — kể cả những trang KHÔNG BAO GIỜ được chương trình dùng tới:

$$\text{Single-level Page Table Size} = 2^{A-O} \times \text{Entry Size}$$

Verified thật: với địa chỉ ảo 32-bit, trang 4KB ($O=12$), mục bảng (PTE) 4-byte — Page Table đơn cấp tốn đúng 4.194.304 byte = 4MB cho MỖI tiến trình. Nghe có vẻ chấp nhận được — nhưng với địa chỉ 64-bit (thực tế CPU hiện đại dùng 48-bit khả dụng), CÙNG công thức đó cho ra 256GB — hoàn toàn BẤT KHẢ THI cho một cấu trúc dữ liệu phải tồn tại RIÊNG cho MỖI tiến trình đang chạy.

single_level_page_table.js (trích engine dùng chung cpu-core.js)
function pageTableEntryCount(addressBits, pageOffsetBits) {
  return Math.pow(2, addressBits - pageOffsetBits);
}
function singleLevelPageTableSizeBytes(addressBits, pageOffsetBits, entryBytes) {
  return pageTableEntryCount(addressBits, pageOffsetBits) * entryBytes;
}
// Verified: singleLevelPageTableSizeBytes(32, 12, 4) = 4.194.304 byte (4MB)
// Verified: singleLevelPageTableSizeBytes(48, 12, 4) / 1024^3 = 256 (GB - bat kha thi!)

Giải pháp thật: Page Table ĐA CẤP (kiểu x86 32-bit chia 10-10-12 bit) — bảng cấp 1 LUÔN được cấp phát (nhỏ, cố định), nhưng bảng cấp 2 CHỈ được cấp phát cho những VÙNG THẬT SỰ có trang đang dùng. Verified thật: với 512 trang đang dùng (2MB không gian địa chỉ thật sự dùng tới, trên tổng 4GB khả dụng của không gian 32-bit) — Page Table 2 cấp chỉ tốn 8.192 byte (8KB), RẺ HƠN đơn cấp (4MB) đúng 512 lần.

two_level_page_table.js (trích engine dùng chung cpu-core.js)
function twoLevelPageTableSizeBytes(numUsedPages, entriesPerTable, entryBytes) {
  const firstLevelBytes = entriesPerTable * entryBytes;             // LUON cap phat
  const numSecondLevelTables = Math.ceil(numUsedPages / entriesPerTable); // CHI vung dang dung
  const tableBytes = entriesPerTable * entryBytes;
  return firstLevelBytes + numSecondLevelTables * tableBytes;
}
// Verified: twoLevelPageTableSizeBytes(512, 1024, 4) = 8192 byte (8KB)
// So voi don cap 4MB cho CUNG khong gian 32-bit -> re hon 512 lan
⚠️ Cạm bẫy: Page Table đơn cấp lãng phí RAM cho trang chưa dùng
Đại đa số chương trình thực tế chỉ dùng một phần RẤT NHỎ trong toàn bộ không gian địa chỉ khả dụng (4GB với 32-bit, hàng TB với 64-bit) — vùng code, vùng heap, vùng stack chỉ chiếm vài MB tới vài trăm MB. Page Table đơn cấp buộc phải cấp phát chỗ cho MỌI trang có thể có, kể cả 99,9% trang KHÔNG BAO GIỜ được dùng tới — đây chính là lý do mọi hệ điều hành thực tế đều dùng cấu trúc đa cấp (hoặc các biến thể khác như bảng trang đảo — inverted page table).

3. Khối tăng tốc dịch địa chỉ TLB

Vấn đề: Page Table BẢN THÂN nó cũng nằm trong RAM — nghĩa là MỖI lần CPU cần dịch địa chỉ ảo sang vật lý, nó phải tốn THÊM một lần truy cập RAM để đọc Page Table, TRƯỚC KHI truy cập RAM lần THỨ HAI để lấy dữ liệu thật sự cần. TLB (Translation Lookaside Buffer) giải quyết bằng cách đóng vai trò một cache CHO Page Table: lưu các cặp (VPN → PFN) đã dịch GẦN ĐÂY, y hệt cấu trúc Set-Associative + LRU của Bài 7.

translate_address.js (trích engine dùng chung cpu-core.js)
function translateAddress(virtualAddress, pageOffsetBits, tlb, pageTable) {
  const { vpn, offset } = splitVirtualAddress(virtualAddress, pageOffsetBits);
  let pfn = tlb.lookup(vpn);
  if (pfn !== null) return { physicalAddress: (pfn << pageOffsetBits) | offset, tlbHit: true, pageFault: false };
  if (pageTable.has(vpn)) {
    pfn = pageTable.get(vpn);
    tlb.insert(vpn, pfn); // luu lai cho lan sau
    return { physicalAddress: (pfn << pageOffsetBits) | offset, tlbHit: false, pageFault: false };
  }
  return { physicalAddress: null, tlbHit: false, pageFault: true }; // VPN chua duoc anh xa
}
// Verified: lan dau truy cap 1 VPN -> tlbHit=false (phai tra Page Table)
// Verified: lan HAI truy cap CUNG VPN -> tlbHit=true (da luu trong TLB)
// Verified: truy cap VPN chua anh xa -> pageFault=true
⚠️ TLB Flush khi chuyển ngữ cảnh (Context Switch)
Mỗi tiến trình có Page Table RIÊNG — bản dịch VPN→PFN của tiến trình A hoàn toàn VÔ NGHĨA (thậm chí NGUY HIỂM nếu dùng nhầm) với tiến trình B. Vì vậy, khi hệ điều hành chuyển CPU từ tiến trình A sang tiến trình B (context switch), TLB phải được XOÁ SẠCH (flush) — mọi bản dịch đã học đều mất, và tiến trình B phải trả giá bằng một loạt TLB miss ở những lần truy cập ĐẦU TIÊN sau khi được lập lịch chạy lại. Đây là lý do context switch có chi phí ẩn lớn hơn nhiều so với "chỉ đổi vài thanh ghi".

4. Thực hành: Dịch địa chỉ ảo động & mô phỏng TLB

Nhập địa chỉ ảo (hex) để dịch qua TLB + Page Table THẬT (3 trang đã ánh xạ sẵn: VPN 5, 6, 9) — thử lại CÙNG địa chỉ để thấy TLB chuyển từ MISS sang HIT, hoặc thử VPN khác để thấy Page Fault. Máy tính bên dưới so sánh trực tiếp dung lượng Page Table đơn cấp vs 2 cấp:

🗺️ Bộ dịch địa chỉ ảo + Máy tính dung lượng Page Table

Dịch địa chỉ ảo (trang đã ánh xạ: VPN 5, 6, 9 — thử VPN khác để thấy Page Fault)

Máy tính dung lượng Page Table

Tóm lược

  • ✅ Bộ nhớ ảo cho phép chương trình lớn hơn RAM vật lý và cô lập an toàn giữa các tiến trình, thông qua Page Table dịch VPN → PFN.
  • ✅ Verified: Page Table đơn cấp 32-bit tốn 4MB; không gian 48-bit (64-bit thực tế) cần 256GB — bất khả thi, buộc phải dùng cấu trúc đa cấp.
  • ✅ Verified: Page Table 2 cấp chỉ tốn 8KB khi thực dùng 2MB không gian địa chỉ — rẻ hơn đơn cấp 512 lần, vì bảng cấp 2 chỉ cấp phát cho vùng THẬT SỰ đang dùng.
  • ✅ TLB là cache CHO Page Table — verified: lần đầu truy cập 1 trang là TLB miss, lần sau CÙNG trang là TLB hit; truy cập trang chưa ánh xạ gây Page Fault.
  • ✅ Pitfall: TLB phải bị xoá sạch (flush) mỗi lần context switch — chi phí ẩn của việc đổi tiến trình.

Trắc nghiệm ôn tập

Câu 1

Verified: Page Table đơn cấp 32-bit tốn 4MB, nhưng không gian 48-bit cần tới 256GB. Vì sao chênh lệch khủng khiếp vậy?

Câu 2

Page Table 2 cấp tiết kiệm RAM hơn đơn cấp bằng cơ chế nào?

Câu 3

Verified: lần đầu truy cập một VPN là TLB MISS (phải tra Page Table), lần sau truy cập CÙNG VPN đó là TLB HIT. Vì sao TLB tồn tại nếu Page Table đã có đủ thông tin?

Câu 4

Vì sao TLB phải bị xoá sạch (flush) khi hệ điều hành chuyển ngữ cảnh (context switch) giữa 2 tiến trình?

Tải file code thực hành minh họa bài học

File JavaScript CPUJS — thư viện kiến trúc máy tính mini dùng xuyên suốt cả 12 bài, Bài 8 vừa thêm splitVirtualAddress(), makeTLB(), translateAddress(), singleLevelPageTableSizeBytes(), twoLevelPageTableSizeBytes() — dịch địa chỉ ảo qua TLB + Page Table và tính dung lượng bảng trang, kèm self-test đối chiếu đúng mọi con số trong bài (chạy node cpu-core.js, không cần cài thêm gì):

Tải về cpu-core.js

📖 Tài liệu tham khảo

Bài viết liên quan trong series

Bài 7: Phân Cấp Bộ Nhớ & Kiến Trúc Cache Bài 9: Apple Silicon & Kiến Trúc Bộ Nhớ Thống Nhất (UMA) Quay lại Lộ trình Kiến Trúc Máy Tính