Phiên bản của câu trả lời này với một TOC hay và nhiều nội dung hơn .
Tôi sẽ sửa chữa bất kỳ lỗi nào được báo cáo. Nếu bạn muốn thực hiện các sửa đổi lớn hoặc bổ sung một khía cạnh còn thiếu, hãy tự tạo chúng trên câu trả lời của riêng bạn để nhận được đại diện xứng đáng. Các chỉnh sửa nhỏ có thể được hợp nhất trực tiếp trong.
Mã mẫu
Ví dụ tối thiểu: https://github.com/cirosantilli/x86-bare-metal-examples/blob/5c672f73884a487414b3e21bd9e579c67cd77621/paging.S
Giống như mọi thứ khác trong lập trình, cách duy nhất để thực sự hiểu điều này là sử dụng các ví dụ tối thiểu.
Điều làm cho chủ đề này trở thành một chủ đề "khó" là ví dụ tối thiểu có dung lượng lớn vì bạn cần tạo một hệ điều hành nhỏ của riêng mình.
Hướng dẫn sử dụng Intel
Mặc dù không thể hiểu được nếu không có các ví dụ trong đầu, hãy cố gắng làm quen với sách hướng dẫn càng sớm càng tốt.
Intel mô tả phân trang trong Hướng dẫn lập trình hệ thống Tập 3 bằng tay Intel - 325384-056US Tháng 9 năm 2015 Chương 4 "Phân trang".
Đặc biệt thú vị là Hình 4-4 "Định dạng CR3 và các mục nhập cấu trúc phân trang với phân trang 32-bit", cung cấp các cấu trúc dữ liệu chính.
MMU
Phân trang được thực hiện bởi phần Đơn vị Quản lý Bộ nhớ (MMU) của CPU. Giống như nhiều người khác (ví dụ như bộ đồng xử lý x87 , APIC ), điều này từng là bởi chip riêng biệt vào những ngày đầu, sau này được tích hợp vào CPU. Nhưng thuật ngữ này vẫn được sử dụng.
Sự thật chung
Địa chỉ logic là địa chỉ bộ nhớ được sử dụng trong mã đất người dùng "thông thường" (ví dụ: nội dung của rsi
in mov eax, [rsi]
).
Phân đoạn đầu tiên chuyển chúng thành địa chỉ tuyến tính, sau đó phân trang sau đó chuyển địa chỉ tuyến tính thành địa chỉ vật lý.
(logical) ------------------> (linear) ------------> (physical)
segmentation paging
Hầu hết thời gian, chúng ta có thể coi địa chỉ vật lý là lập chỉ mục các ô nhớ phần cứng RAM thực tế, nhưng điều này không đúng 100% vì:
Phân trang chỉ khả dụng ở chế độ được bảo vệ. Việc sử dụng phân trang trong chế độ được bảo vệ là tùy chọn. Phân trang được bật khi PG
bit của thanh cr0
ghi được thiết lập.
Phân trang so với phân đoạn
Một điểm khác biệt chính giữa phân trang và phân đoạn là:
- phân trang chia RAM thành các phần có kích thước bằng nhau được gọi là trang
- phân đoạn chia bộ nhớ thành nhiều phần có kích thước tùy ý
Đây là ưu điểm chính của phân trang, vì các phần có kích thước bằng nhau giúp mọi thứ dễ quản lý hơn.
Phân trang đã trở nên phổ biến hơn nhiều đến mức hỗ trợ phân đoạn đã bị loại bỏ trong x86-64 ở chế độ 64-bit, chế độ hoạt động chính của phần mềm mới, nơi nó chỉ tồn tại ở chế độ tương thích, mô phỏng IA32.
Ứng dụng
Phân trang được sử dụng để thực hiện các quy trình không gian địa chỉ ảo trên hệ điều hành hiện đại. Với các địa chỉ ảo, hệ điều hành có thể phù hợp với hai hoặc nhiều quy trình đồng thời trên một RAM theo cách:
- cả hai chương trình không cần biết gì về chương trình kia
- bộ nhớ của cả hai chương trình có thể phát triển và thu nhỏ khi cần thiết
- chuyển đổi giữa các chương trình rất nhanh
- một chương trình không bao giờ có thể truy cập bộ nhớ của quá trình khác
Lịch sử phân trang xuất hiện sau khi phân đoạn và phần lớn thay thế nó cho việc triển khai bộ nhớ ảo trong các hệ điều hành hiện đại như Linux vì dễ dàng quản lý các phần bộ nhớ có kích thước cố định của các trang thay vì các phân đoạn có độ dài thay đổi.
Triển khai phần cứng
Giống như phân đoạn trong chế độ được bảo vệ (trong đó việc sửa đổi thanh ghi phân đoạn sẽ kích hoạt tải từ GDT hoặc LDT), phần cứng phân trang sử dụng cấu trúc dữ liệu trong bộ nhớ để thực hiện công việc của nó (bảng trang, thư mục trang, v.v.).
Định dạng của các cấu trúc dữ liệu đó là do phần cứng cố định , nhưng việc thiết lập và quản lý các cấu trúc dữ liệu đó trên RAM một cách chính xác là tùy thuộc vào HĐH, đồng thời cho phần cứng biết nơi tìm chúng (thông qua cr3
).
Một số kiến trúc khác để phân trang gần như hoàn toàn trong tay phần mềm, do đó, TLB bỏ lỡ chạy một chức năng do hệ điều hành cung cấp để xem các bảng trang và chèn ánh xạ mới vào TLB. Điều này khiến các định dạng bảng trang được chọn bởi HĐH, nhưng khiến phần cứng khó có thể chồng chéo các lần xem trang với việc thực thi không theo thứ tự các hướng dẫn khác, theo cách mà x86 có thể làm .
Ví dụ: lược đồ phân trang cấp một đơn giản
Đây là một ví dụ về cách phân trang hoạt động trên phiên bản đơn giản hóa của kiến trúc x86 để triển khai không gian bộ nhớ ảo.
Bảng trang
Hệ điều hành có thể cung cấp cho họ các bảng trang sau:
Bảng trang được OS cung cấp cho quy trình 1:
RAM location physical address present
----------------- ----------------- --------
PT1 + 0 * L 0x00001 1
PT1 + 1 * L 0x00000 1
PT1 + 2 * L 0x00003 1
PT1 + 3 * L 0
... ...
PT1 + 0xFFFFF * L 0x00005 1
Bảng trang được hệ điều hành cung cấp cho quy trình 2:
RAM location physical address present
----------------- ----------------- --------
PT2 + 0 * L 0x0000A 1
PT2 + 1 * L 0x0000B 1
PT2 + 2 * L 0
PT2 + 3 * L 0x00003 1
... ... ...
PT2 + 0xFFFFF * L 0x00004 1
Ở đâu:
PT1
và PT2
: vị trí ban đầu của bảng 1 và 2 trên RAM.
Giá trị mẫu: 0x00000000
, 0x12345678
vv
Chính HĐH quyết định những giá trị đó.
L
: độ dài của mục nhập bảng trang.
present
: cho biết rằng trang đó có trong bộ nhớ.
Bảng trang nằm trên RAM. Ví dụ, chúng có thể được đặt dưới dạng:
--------------> 0xFFFFFFFF
--------------> PT1 + 0xFFFFF * L
Page Table 1
--------------> PT1
--------------> PT2 + 0xFFFFF * L
Page Table 2
--------------> PT2
--------------> 0x0
Các vị trí ban đầu trên RAM cho cả hai bảng trang là tùy ý và được kiểm soát bởi Hệ điều hành. Đó là tùy thuộc vào hệ điều hành để đảm bảo rằng chúng không trùng lặp!
Mỗi quy trình không thể chạm trực tiếp vào bất kỳ bảng trang nào, mặc dù nó có thể đưa ra các yêu cầu tới HĐH khiến bảng trang được sửa đổi, ví dụ như yêu cầu phân đoạn ngăn xếp hoặc đống lớn hơn.
Một trang là một đoạn 4KB (12 bit) và vì địa chỉ có 32 bit nên chỉ cần 20 bit (20 + 12 = 32, do đó 5 ký tự trong ký hiệu thập lục phân) được yêu cầu để xác định mỗi trang. Giá trị này được cố định bởi phần cứng.
Mục nhập bảng trang
Một bảng trang là ... một bảng các mục trong bảng!
Định dạng chính xác của các mục trong bảng được cố định bởi phần cứng .
Trong ví dụ đơn giản này, các mục nhập bảng trang chỉ chứa hai trường:
bits function
----- -----------------------------------------
20 physical address of the start of the page
1 present flag
vì vậy trong ví dụ này, các nhà thiết kế phần cứng có thể đã chọn L = 21
.
Hầu hết các mục trong bảng trang thực đều có các trường khác.
Sẽ là không thực tế nếu căn chỉnh mọi thứ ở 21 bit vì bộ nhớ có thể định địa chỉ theo byte chứ không phải bit. Do đó, ngay cả khi chỉ cần 21 bit trong trường hợp này, các nhà thiết kế phần cứng có thể sẽ chọn L = 32
cách làm cho việc truy cập nhanh hơn và chỉ dự trữ các bit còn lại để sử dụng sau này. Giá trị thực cho L
trên x86 là 32 bit.
Dịch địa chỉ trong lược đồ cấp đơn
Sau khi hệ điều hành thiết lập các bảng trang, việc dịch địa chỉ giữa địa chỉ tuyến tính và địa chỉ vật lý được thực hiện bởi phần cứng .
Khi hệ điều hành muốn kích hoạt quá trình 1, nó đặt cr3
để PT1
, sự bắt đầu của bảng cho quá trình một.
Nếu Quy trình 1 muốn truy cập địa chỉ tuyến tính 0x00000001
, mạch phần cứng phân trang sẽ tự động thực hiện những việc sau đối với Hệ điều hành:
chia địa chỉ tuyến tính thành hai phần:
| page (20 bits) | offset (12 bits) |
Vì vậy, trong trường hợp này, chúng tôi sẽ có:
- trang = 0x00000
- bù đắp = 0x001
nhìn vào bảng Trang 1 vì cr3
chỉ vào nó.
nhìn mục nhập 0x00000
vì đó là phần trang.
Phần cứng biết rằng mục này nằm ở địa chỉ RAM PT1 + 0 * L = PT1
.
kể từ khi nó hiện diện, quyền truy cập hợp lệ
bởi bảng trang, vị trí của số trang 0x00000
là tại 0x00001 * 4K = 0x00001000
.
để tìm địa chỉ thực cuối cùng, chúng ta chỉ cần thêm phần bù:
00001 000
+ 00000 001
-----------
00001 001
vì 00001
là địa chỉ vật lý của trang được tra cứu trên bảng và 001
là phần bù.
Như tên cho biết, phần bù luôn được thêm vào địa chỉ vật lý của trang.
sau đó phần cứng nhận bộ nhớ tại vị trí thực đó.
Theo cách tương tự, các bản dịch sau sẽ xảy ra cho quy trình 1:
linear physical
--------- ---------
00000 002 00001 002
00000 003 00001 003
00000 FFF 00001 FFF
00001 000 00000 000
00001 001 00000 001
00001 FFF 00000 FFF
00002 000 00002 000
FFFFF 000 00005 000
Ví dụ, khi truy cập địa chỉ 00001000
, phần trang là 00001
phần cứng biết rằng mục nhập bảng trang của nó nằm ở địa chỉ RAM: PT1 + 1 * L
( 1
vì phần trang), và đó là nơi nó sẽ tìm kiếm.
Khi HĐH muốn chuyển sang tiến trình 2, tất cả những gì nó cần làm là chuyển cr3
sang trang 2. Đơn giản vậy thôi!
Bây giờ các bản dịch sau sẽ xảy ra cho quy trình 2:
linear physical
--------- ---------
00000 002 00001 002
00000 003 00001 003
00000 FFF 00001 FFF
00001 000 00000 000
00001 001 00000 001
00001 FFF 00000 FFF
00003 000 00003 000
FFFFF 000 00004 000
Cùng một địa chỉ tuyến tính chuyển thành các địa chỉ vật lý khác nhau cho các quá trình khác nhau , chỉ phụ thuộc vào giá trị bên trong cr3
.
Bằng cách này, mọi chương trình có thể mong đợi dữ liệu của nó bắt đầu 0
và kết thúc tại FFFFFFFF
, mà không cần lo lắng về địa chỉ vật lý chính xác.
Lỗi trang
Điều gì sẽ xảy ra nếu Quy trình 1 cố gắng truy cập một địa chỉ bên trong một trang không có?
Phần cứng thông báo cho phần mềm thông qua một Ngoại lệ Lỗi Trang.
Sau đó, hệ điều hành thường phải đăng ký một trình xử lý ngoại lệ để quyết định những gì phải được thực hiện.
Có thể việc truy cập một trang không có trên bảng là lỗi lập trình:
int is[1];
is[2] = 1;
nhưng có thể có những trường hợp được chấp nhận, ví dụ như trong Linux khi:
chương trình muốn tăng ngăn xếp của nó.
Nó chỉ cố gắng truy cập một byte nhất định trong một phạm vi nhất định có thể, và nếu hệ điều hành hài lòng, nó sẽ thêm trang đó vào không gian địa chỉ quy trình.
trang đã được đổi sang đĩa.
Hệ điều hành sẽ cần thực hiện một số công việc đằng sau các quy trình để đưa trang trở lại RAM.
Hệ điều hành có thể phát hiện ra rằng đây là trường hợp dựa trên nội dung của phần còn lại của mục nhập bảng trang, vì nếu cờ hiện tại rõ ràng, các mục nhập khác của mục nhập bảng trang hoàn toàn được để lại cho Hệ điều hành theo ý muốn.
Ví dụ trên Linux, khi hiện tại = 0:
nếu tất cả các trường của mục nhập bảng trang là 0, địa chỉ không hợp lệ.
khác, trang đã được hoán đổi sang đĩa và giá trị thực của các trường đó mã hóa vị trí của trang trên đĩa.
Trong mọi trường hợp, Hệ điều hành cần biết địa chỉ nào đã tạo ra Lỗi trang để có thể xử lý sự cố. Đây là lý do tại sao các nhà phát triển IA32 tốt bụng đặt giá trị của cr2
địa chỉ đó bất cứ khi nào xảy ra Lỗi trang. Sau đó, trình xử lý ngoại lệ có thể chỉ cần xem xét cr2
để lấy địa chỉ.
Đơn giản hóa
Đơn giản hóa thực tế giúp ví dụ này dễ hiểu hơn:
tất cả các mạch phân trang thực sử dụng phân trang nhiều cấp để tiết kiệm không gian, nhưng điều này cho thấy một sơ đồ đơn cấp đơn giản.
bảng trang chỉ chứa hai trường: địa chỉ 20 bit và cờ hiện tại 1 bit.
Bảng trang thực có tổng cộng 12 trường và do đó các tính năng khác đã bị bỏ qua.
Ví dụ: lược đồ phân trang nhiều cấp
Vấn đề với sơ đồ phân trang mức đơn là nó sẽ chiếm quá nhiều RAM: 4G / 4K = 1M mục nhập cho mỗi quá trình. Nếu mỗi mục nhập dài 4 byte, điều đó sẽ tạo ra 4M cho mỗi quá trình , quá nhiều ngay cả đối với máy tính để bàn: ps -A | wc -l
nói rằng tôi đang chạy 244 quy trình ngay bây giờ, vì vậy sẽ chiếm khoảng 1GB RAM của tôi!
Vì lý do này, các nhà phát triển x86 đã quyết định sử dụng lược đồ đa cấp để giảm mức sử dụng RAM.
Nhược điểm của hệ thống này là có thời gian truy cập hơi cao.
Trong sơ đồ phân trang 3 cấp đơn giản được sử dụng cho bộ xử lý 32 bit không có PAE, 32 bit địa chỉ được chia như sau:
| directory (10 bits) | table (10 bits) | offset (12 bits) |
Mỗi quy trình phải có một và chỉ một thư mục trang được liên kết với nó, do đó, nó sẽ chứa ít nhất 2^10 = 1K
các mục nhập thư mục trang, tốt hơn nhiều so với 1M tối thiểu được yêu cầu trên một sơ đồ cấp đơn.
Bảng trang chỉ được cấp phát khi cần thiết bởi Hệ điều hành. Mỗi bảng 2^10 = 1K
trang có các mục nhập thư mục trang
Thư mục trang chứa ... mục thư mục trang! Các mục nhập thư mục trang giống như mục nhập bảng trang ngoại trừ việc chúng trỏ đến địa chỉ RAM của bảng trang thay vì địa chỉ vật lý của bảng . Vì các địa chỉ đó chỉ rộng 20 bit, các bảng trang phải ở đầu các trang 4KB.
cr3
bây giờ trỏ đến vị trí trên RAM của thư mục trang của tiến trình hiện tại thay vì bảng trang.
Các mục nhập của bảng trang hoàn toàn không thay đổi so với lược đồ một cấp.
Bảng trang thay đổi so với lược đồ cấp đơn vì:
- mỗi quy trình có thể có tới 1K bảng trang, một mục nhập thư mục trên mỗi trang.
- mỗi bảng trang chứa đúng 1K mục nhập thay vì 1 triệu mục nhập.
Lý do sử dụng 10 bit ở hai cấp độ đầu tiên (và không phải, chẳng hạn 12 | 8 | 12
) là mỗi mục nhập Bảng trang dài 4 byte. Sau đó, 2 ^ 10 mục nhập của thư mục Trang và Bảng Trang sẽ vừa khít với các trang 4Kb. Điều này có nghĩa là việc phân bổ và phân bổ các trang cho mục đích đó nhanh hơn và đơn giản hơn.
Dịch địa chỉ trong lược đồ nhiều cấp
Thư mục trang được hệ điều hành cung cấp cho quy trình 1:
RAM location physical address present
--------------- ----------------- --------
PD1 + 0 * L 0x10000 1
PD1 + 1 * L 0
PD1 + 2 * L 0x80000 1
PD1 + 3 * L 0
... ...
PD1 + 0x3FF * L 0
Bảng trang được HĐH cung cấp cho quá trình 1 ở PT1 = 0x10000000
( 0x10000
* 4K):
RAM location physical address present
--------------- ----------------- --------
PT1 + 0 * L 0x00001 1
PT1 + 1 * L 0
PT1 + 2 * L 0x0000D 1
... ...
PT1 + 0x3FF * L 0x00005 1
Bảng trang được HĐH cung cấp cho quá trình 1 ở PT2 = 0x80000000
( 0x80000
* 4K):
RAM location physical address present
--------------- ----------------- --------
PT2 + 0 * L 0x0000A 1
PT2 + 1 * L 0x0000C 1
PT2 + 2 * L 0
... ...
PT2 + 0x3FF * L 0x00003 1
Ở đâu:
PD1
: vị trí ban đầu của thư mục trang của tiến trình 1 trên RAM.
PT1
và PT2
: vị trí ban đầu của bảng trang 1 và bảng trang 2 cho tiến trình 1 trên RAM.
Vì vậy, trong ví dụ này, thư mục trang và bảng trang có thể được lưu trữ trong RAM như sau:
----------------> 0xFFFFFFFF
----------------> PT2 + 0x3FF * L
Page Table 1
----------------> PT2
----------------> PD1 + 0x3FF * L
Page Directory 1
----------------> PD1
----------------> PT1 + 0x3FF * L
Page Table 2
----------------> PT1
----------------> 0x0
Hãy dịch địa chỉ tuyến tính 0x00801004
từng bước.
Chúng tôi cho rằng cr3 = PD1
, nghĩa là nó trỏ đến thư mục trang vừa được mô tả.
Trong hệ nhị phân, địa chỉ tuyến tính là:
0 0 8 0 1 0 0 4
0000 0000 1000 0000 0001 0000 0000 0100
Phân nhóm như 10 | 10 | 12
cho:
0000000010 0000000001 000000000100
0x2 0x1 0x4
mang lại:
- mục nhập thư mục trang = 0x2
- mục nhập bảng trang = 0x1
- bù đắp = 0x4
Vì vậy, phần cứng sẽ tìm mục nhập 2 của thư mục trang.
Bảng thư mục trang cho biết rằng bảng trang được đặt tại 0x80000 * 4K = 0x80000000
. Đây là lần truy cập RAM đầu tiên của quá trình.
Vì là mục nhập bảng trang 0x1
, nên phần cứng sẽ nhìn vào mục nhập 1 của bảng trang 0x80000000
, điều này cho nó biết rằng trang vật lý được đặt tại địa chỉ 0x0000C * 4K = 0x0000C000
. Đây là lần truy cập RAM thứ hai của quá trình.
Cuối cùng, phần cứng phân trang thêm phần bù vào và địa chỉ cuối cùng là 0x0000C004
.
Các ví dụ khác về địa chỉ đã dịch là:
linear 10 10 12 split physical
-------- --------------- ----------
00000001 000 000 001 00001001
00001001 000 001 001 page fault
003FF001 000 3FF 001 00005001
00400000 001 000 000 page fault
00800001 002 000 001 0000A001
00801008 002 001 008 0000C008
00802008 002 002 008 page fault
00B00001 003 000 000 page fault
Lỗi trang xảy ra nếu mục nhập thư mục trang hoặc mục nhập bảng trang không có.
Nếu hệ điều hành muốn chạy một quá trình khác đồng thời, nó sẽ cung cấp cho quá trình thứ hai một thư mục trang riêng biệt và liên kết thư mục đó với các bảng trang riêng biệt.
Kiến trúc 64-bit
64 bit vẫn còn quá nhiều địa chỉ cho kích thước RAM hiện tại, vì vậy hầu hết các kiến trúc sẽ sử dụng ít bit hơn.
x86_64 sử dụng 48 bit (256 TiB) và PAE của chế độ kế thừa đã cho phép địa chỉ 52 bit (4 PiB).
12 trong số 48 bit đó đã được dành riêng cho phần bù, còn lại 36 bit.
Nếu phương pháp tiếp cận 2 mức được thực hiện, sự phân chia tốt nhất sẽ là hai mức 18 bit.
Nhưng điều đó có nghĩa là thư 2^18 = 256K
mục trang sẽ có các mục nhập, sẽ chiếm quá nhiều RAM: gần bằng phân trang mức đơn cho các kiến trúc 32 bit!
Do đó, kiến trúc 64 bit tạo ra các cấp trang thậm chí còn xa hơn, thường là 3 hoặc 4.
x86_64 sử dụng 4 cấp trong một 9 | 9 | 9 | 12
lược đồ, do đó cấp trên chỉ chiếm 2^9
các mục nhập cấp cao hơn.
PAE
Phần mở rộng địa chỉ thực.
Với 32 bit, chỉ có 4GB RAM có thể được giải quyết.
Điều này bắt đầu trở thành một hạn chế đối với các máy chủ lớn, vì vậy Intel đã giới thiệu cơ chế PAE cho Pentium Pro.
Để giải quyết vấn đề, Intel đã thêm 4 dòng địa chỉ mới, để 64GB có thể được giải quyết.
Cấu trúc bảng trang cũng bị thay đổi nếu bật PAE. Cách chính xác mà nó được thay đổi phụ thuộc vào việc PSE đang bật hay tắt.
PAE được bật và tắt thông qua PAE
bit cr4
.
Ngay cả khi tổng bộ nhớ địa chỉ là 64GB, quá trình cá nhân vẫn chỉ có thể sử dụng tối đa 4GB. Tuy nhiên, hệ điều hành có thể đặt các quy trình khác nhau trên các khối 4GB khác nhau.
PSE
Phần mở rộng kích thước trang.
Cho phép các trang có độ dài 4M (hoặc 2M nếu bật PAE) thay vì 4K.
PSE được bật và tắt thông qua PAE
bit cr4
.
Lược đồ bảng trang PAE và PSE
Nếu PAE và PSE đang hoạt động, các lược đồ mức phân trang khác nhau sẽ được sử dụng:
không có PAE và không có PSE: 10 | 10 | 12
không PAE và PSE: 10 | 22
.
22 là độ lệch trong trang 4Mb, vì 22 bit địa chỉ 4Mb.
PAE và không có PSE: 2 | 9 | 9 | 12
Lý do thiết kế tại sao 9 được sử dụng hai lần thay vì 10 là bây giờ các mục nhập không thể phù hợp với 32 bit nữa, tất cả đã được lấp đầy bởi 20 bit địa chỉ và 12 bit cờ có nghĩa hoặc dành riêng.
Lý do là 20 bit không còn đủ để biểu diễn địa chỉ của các bảng trang: 24 bit bây giờ là cần thiết do có thêm 4 dây dẫn vào bộ xử lý.
Do đó, các nhà thiết kế quyết định tăng kích thước mục nhập lên 64 bit, và để làm cho chúng vừa với một bảng trang duy nhất, cần giảm số mục nhập xuống 2 ^ 9 thay vì 2 ^ 10.
Cấp độ thứ 2 bắt đầu là cấp độ Trang mới được gọi là Bảng con trỏ thư mục trang (PDPT), vì nó trỏ đến các thư mục trang và điền vào địa chỉ tuyến tính 32 bit. PDPT cũng rộng 64 bit.
cr3
bây giờ trỏ đến các PDPT phải có trên bốn 4GB bộ nhớ và được căn chỉnh trên bội số 32 bit để xử lý hiệu quả. Điều này có nghĩa là bây giờ cr3
có 27 bit có ý nghĩa thay vì 20: 2 ^ 5 cho 32 bội số * 2 ^ 27 để hoàn thành 2 ^ 32 của 4GB đầu tiên.
PAE và PSE: 2 | 9 | 21
Các nhà thiết kế quyết định giữ một trường rộng 9 bit để làm cho nó vừa với một trang duy nhất.
Điều này để lại 23 bit. Để lại 2 cho PDPT để giữ mọi thứ đồng nhất với trường hợp PAE mà không có PSE để lại 21 cho bù đắp, có nghĩa là các trang có chiều rộng 2M thay vì 4M.
TLB
Bộ đệm tìm kiếm bản dịch (TLB) là một bộ đệm cho các địa chỉ phân trang.
Vì nó là một bộ nhớ cache, nó chia sẻ nhiều vấn đề về thiết kế của bộ nhớ cache CPU, chẳng hạn như mức độ liên kết.
Phần này sẽ mô tả một TLB kết hợp đầy đủ được đơn giản hóa với 4 mục nhập địa chỉ duy nhất. Lưu ý rằng giống như các bộ nhớ đệm khác, TLB thực thường không liên kết hoàn toàn.
Hoạt động cơ bản
Sau khi dịch giữa địa chỉ tuyến tính và địa chỉ vật lý xảy ra, nó được lưu trữ trên TLB. Ví dụ: TLB 4 mục bắt đầu ở trạng thái sau:
valid linear physical
------ ------- ---------
> 0 00000 00000
0 00000 00000
0 00000 00000
0 00000 00000
Dấu >
chỉ mục nhập hiện tại sẽ được thay thế.
và sau khi địa chỉ tuyến tính của trang 00003
được dịch sang địa chỉ thực 00005
, TLB sẽ trở thành:
valid linear physical
------ ------- ---------
1 00003 00005
> 0 00000 00000
0 00000 00000
0 00000 00000
và sau khi một bản dịch thứ hai của 00007
để 00009
nó trở thành:
valid linear physical
------ ------- ---------
1 00003 00005
1 00007 00009
> 0 00000 00000
0 00000 00000
Bây giờ nếu 00003
cần được dịch lại, trước tiên phần cứng sẽ tra cứu TLB và tìm ra địa chỉ của nó bằng một quyền truy cập RAM duy nhất 00003 --> 00005
.
Tất nhiên, 00000
không có trên TLB vì không có mục nhập hợp lệ nào chứa 00000
làm khóa.
Chính sách thay thế
Khi TLB được lấp đầy, các địa chỉ cũ hơn sẽ bị ghi đè. Cũng giống như đối với bộ nhớ cache của CPU, chính sách thay thế là một hoạt động có khả năng phức tạp, nhưng một phương pháp đơn giản và hợp lý là loại bỏ mục nhập ít được sử dụng gần đây nhất (LRU).
Với LRU, bắt đầu từ trạng thái:
valid linear physical
------ ------- ---------
> 1 00003 00005
1 00007 00009
1 00009 00001
1 0000B 00003
thêm vào 0000D -> 0000A
sẽ cho:
valid linear physical
------ ------- ---------
1 0000D 0000A
> 1 00007 00009
1 00009 00001
1 0000B 00003
CAM
Sử dụng TLB giúp dịch nhanh hơn, vì bản dịch ban đầu có một quyền truy cập cho mỗi cấp TLB , nghĩa là 2 trên sơ đồ 32 bit đơn giản, nhưng 3 hoặc 4 trên kiến trúc 64 bit.
TLB thường được thực hiện dưới dạng một loại RAM đắt tiền được gọi là bộ nhớ địa chỉ theo nội dung (CAM). CAM thực hiện một bản đồ liên kết trên phần cứng, nghĩa là, một cấu trúc đã cho một khóa (địa chỉ tuyến tính), truy xuất một giá trị.
Ánh xạ cũng có thể được thực hiện trên địa chỉ RAM, nhưng ánh xạ CAM có thể yêu cầu ít mục nhập hơn nhiều so với ánh xạ RAM.
Ví dụ, một bản đồ trong đó:
- cả khóa và giá trị đều có 20 bit (trường hợp của lược đồ phân trang đơn giản)
- nhiều nhất 4 giá trị cần được lưu trữ tại mỗi thời điểm
có thể được lưu trữ trong một TLB với 4 mục nhập:
linear physical
------- ---------
00000 00001
00001 00010
00010 00011
FFFFF 00000
Tuy nhiên, để thực hiện điều này với RAM, cần phải có 2 ^ 20 địa chỉ :
linear physical
------- ---------
00000 00001
00001 00010
00010 00011
... (from 00011 to FFFFE)
FFFFF 00000
thậm chí còn đắt hơn sử dụng TLB.
Mục nhập không hợp lệ
Khi cr3
thay đổi, tất cả các mục nhập TLB đều bị vô hiệu, vì một bảng trang mới cho một quy trình mới sẽ được sử dụng, vì vậy không chắc rằng bất kỳ mục nhập cũ nào có bất kỳ ý nghĩa nào.
X86 cũng cung cấp invlpg
hướng dẫn làm mất hiệu lực rõ ràng một mục nhập TLB. Các kiến trúc khác cung cấp nhiều hướng dẫn hơn cho các mục nhập TLB đã bị vô hiệu, chẳng hạn như làm vô hiệu tất cả các mục nhập trên một phạm vi nhất định.
Một số CPU x86 vượt ra ngoài các yêu cầu của đặc tả x86 và cung cấp tính chặt chẽ hơn những gì nó đảm bảo, giữa việc sửa đổi mục nhập bảng trang và sử dụng nó, khi nó chưa được lưu trong bộ nhớ cache trong TLB . Rõ ràng Windows 9x đã dựa vào điều đó để xác định tính chính xác, nhưng các CPU AMD hiện đại không cung cấp các bước chuyển trang mạch lạc. CPU Intel làm được, mặc dù họ phải phát hiện ra những suy đoán sai để làm như vậy. Lợi dụng điều này có lẽ là một ý tưởng tồi, vì có lẽ không thu được gì nhiều và có nguy cơ lớn gây ra các vấn đề nhạy cảm về thời gian mà khó có thể gỡ rối.
Sử dụng nhân Linux
Nhân Linux sử dụng rộng rãi các tính năng phân trang của x86 để cho phép chuyển đổi quy trình nhanh với phân mảnh dữ liệu nhỏ.
Trong v4.2
, hãy xem dưới arch/x86/
:
include/asm/pgtable*
include/asm/page*
mm/pgtable*
mm/page*
Dường như không có cấu trúc nào được xác định để đại diện cho các trang, chỉ có macro: include/asm/page_types.h
đặc biệt thú vị. Trích:
#define _PAGE_BIT_PRESENT 0 /* is present */
#define _PAGE_BIT_RW 1 /* writeable */
#define _PAGE_BIT_USER 2 /* userspace addressable */
#define _PAGE_BIT_PWT 3 /* page write through */
arch/x86/include/uapi/asm/processor-flags.h
xác định CR0
, và đặc biệt là PG
vị trí bit:
#define X86_CR0_PG_BIT 31 /* Paging */
Thư mục
Miễn phí:
Không tự do: