รุ่นของคำตอบนี้กับ TOC ดีและเนื้อหาเพิ่มเติม
ฉันจะแก้ไขข้อผิดพลาดที่รายงาน หากคุณต้องการปรับเปลี่ยนขนาดใหญ่หรือเพิ่มแง่มุมที่ขาดหายไปให้ทำตามคำตอบของคุณเองเพื่อรับตัวแทนที่สมควรได้รับ การแก้ไขเล็กน้อยสามารถรวมเข้าด้วยกันได้โดยตรง
โค้ดตัวอย่าง
ตัวอย่างน้อยที่สุด: https://github.com/cirosantilli/x86-bare-metal-examples/blob/5c672f73884a487414b3e21bd9e579c67cd77621/paging.S
เช่นเดียวกับสิ่งอื่น ๆ ในการเขียนโปรแกรมวิธีเดียวที่จะเข้าใจสิ่งนี้คือการเล่นกับตัวอย่างน้อยที่สุด
สิ่งที่ทำให้เรื่องนี้ "ยาก" ก็คือตัวอย่างที่เรียบง่ายนั้นมีขนาดใหญ่เนื่องจากคุณต้องสร้าง OS ขนาดเล็กของคุณเอง
คู่มือ Intel
แม้ว่าจะเป็นไปไม่ได้ที่จะเข้าใจหากไม่มีตัวอย่างในใจให้พยายามทำความคุ้นเคยกับคู่มือโดยเร็วที่สุด
Intel อธิบายการเพจในคู่มือการเขียนโปรแกรมระบบ Intel Manual Volume 3 - 325384-056US กันยายน 2015บทที่ 4 "การเพจ"
ที่น่าสนใจเป็นพิเศษคือ Figure 4-4 "Formats of CR3 and Paging-Structure Entries with 32-Bit Paging" ซึ่งให้โครงสร้างข้อมูลที่สำคัญ
MMU
การเพจทำได้โดยส่วนMemory Management Unit (MMU) ของ CPU เช่นเดียวกับคนอื่น ๆ อีกมากมาย (เช่นx87 co-processor , APIC ) สิ่งนี้เคยเป็นโดยชิปแยกในช่วงแรก ๆ ซึ่งต่อมาได้รวมเข้ากับ CPU แต่ยังคงใช้คำนี้อยู่
ข้อเท็จจริงทั่วไป
โลจิคัลแอดเดรสคือที่อยู่หน่วยความจำที่ใช้ในรหัสที่ดินของผู้ใช้ "ปกติ" (เช่นเนื้อหาของrsi
in mov eax, [rsi]
)
การแบ่งส่วนแรกจะแปลเป็นที่อยู่เชิงเส้นจากนั้นการแบ่งหน้าจะแปลที่อยู่เชิงเส้นเป็นที่อยู่ทางกายภาพ
(logical) ------------------> (linear) ------------> (physical)
segmentation paging
โดยส่วนใหญ่เราสามารถคิดว่าที่อยู่จริงเป็นดัชนีเซลล์หน่วยความจำฮาร์ดแวร์ RAM จริง แต่สิ่งนี้ไม่เป็นความจริง 100% เนื่องจาก:
เพจใช้ได้เฉพาะในโหมดป้องกันเท่านั้น การใช้เพจในโหมดป้องกันเป็นทางเลือก เพจเปิดอยู่ iff PG
บิตของcr0
รีจิสเตอร์ถูกตั้งค่า
การแบ่งหน้าและการแบ่งส่วน
ความแตกต่างที่สำคัญอย่างหนึ่งระหว่างการแบ่งหน้าและการแบ่งส่วนคือ:
- การแบ่งหน้าแบ่ง RAM ออกเป็นส่วนขนาดเท่า ๆ กันที่เรียกว่าเพจ
- การแบ่งส่วนแบ่งหน่วยความจำออกเป็นชิ้นส่วนขนาดตามอำเภอใจ
นี่เป็นข้อได้เปรียบหลักของการเพจเนื่องจากชิ้นส่วนที่มีขนาดเท่ากันทำให้จัดการสิ่งต่างๆได้ง่ายขึ้น
การเพจได้รับความนิยมมากขึ้นจนการรองรับการแบ่งส่วนลดลงใน x86-64 ในโหมด 64 บิตซึ่งเป็นโหมดหลักของการทำงานสำหรับซอฟต์แวร์ใหม่ซึ่งมีเฉพาะในโหมดความเข้ากันได้ซึ่งเลียนแบบ IA32
ใบสมัคร
การเพจถูกใช้เพื่อปรับใช้พื้นที่แอดเดรสเสมือนของกระบวนการบนระบบปฏิบัติการสมัยใหม่ ด้วยที่อยู่เสมือนระบบปฏิบัติการสามารถปรับกระบวนการทำงานพร้อมกันสองกระบวนการขึ้นไปบน RAM เดียวในลักษณะที่:
- ทั้งสองโปรแกรมไม่จำเป็นต้องรู้อะไรเกี่ยวกับโปรแกรมอื่น ๆ
- หน่วยความจำของทั้งสองโปรแกรมสามารถขยายและลดขนาดได้ตามต้องการ
- การสลับระหว่างโปรแกรมนั้นรวดเร็วมาก
- โปรแกรมหนึ่งไม่สามารถเข้าถึงหน่วยความจำของกระบวนการอื่นได้
ในอดีตการแบ่งหน้าเกิดขึ้นหลังจากการแบ่งส่วนและส่วนใหญ่ถูกแทนที่สำหรับการใช้งานหน่วยความจำเสมือนในระบบปฏิบัติการสมัยใหม่เช่น Linux เนื่องจากง่ายต่อการจัดการหน่วยความจำขนาดคงที่ของหน้าแทนที่จะเป็นส่วนความยาวที่หลากหลาย
การใช้งานฮาร์ดแวร์
เช่นเดียวกับการแบ่งส่วนในโหมดป้องกัน (ซึ่งการแก้ไขการลงทะเบียนเซ็กเมนต์จะทริกเกอร์การโหลดจาก GDT หรือ LDT) ฮาร์ดแวร์การเพจจะใช้โครงสร้างข้อมูลในหน่วยความจำเพื่อทำงานของมัน (ตารางเพจไดเร็กทอรีเพจ ฯลฯ )
รูปแบบของโครงสร้างข้อมูลเหล่านั้นได้รับการแก้ไขโดยฮาร์ดแวร์แต่ขึ้นอยู่กับระบบปฏิบัติการที่จะตั้งค่าและจัดการโครงสร้างข้อมูลเหล่านั้นบน RAM อย่างถูกต้องและเพื่อบอกฮาร์ดแวร์ว่าจะหาได้จากที่ใด (ผ่านcr3
)
สถาปัตยกรรมอื่น ๆ บางส่วนปล่อยให้เพจอยู่ในมือของซอฟต์แวร์เกือบทั้งหมดดังนั้น TLB miss จึงเรียกใช้ฟังก์ชันที่มาจาก OS เพื่อเดินตารางเพจและแทรกการแมปใหม่ลงใน TLB นี้รูปแบบตารางใบหน้าจะได้รับเลือกโดย OS แต่ทำให้มันไม่น่าฮาร์ดแวร์เพื่อให้สามารถทับซ้อนเดินหน้ากับการดำเนินการออกจากคำสั่งของคำแนะนำอื่น ๆ วิธีที่สามารถ
ตัวอย่าง: โครงร่างการเพจระดับเดียวที่เรียบง่าย
นี่คือตัวอย่างของวิธีการดำเนินการเพจบนสถาปัตยกรรม x86 เวอร์ชันที่เรียบง่ายเพื่อใช้พื้นที่หน่วยความจำเสมือน
ตารางหน้า
ระบบปฏิบัติการสามารถให้ตารางหน้าต่อไปนี้:
ตารางหน้าที่กำหนดให้กับกระบวนการ 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
ตารางหน้าที่กำหนดให้กับกระบวนการ 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
ที่ไหน:
PT1
และPT2
: ตำแหน่งเริ่มต้นของตาราง 1 และ 2 บน RAM
ค่าตัวอย่าง: 0x00000000
, 0x12345678
ฯลฯ
เป็นระบบปฏิบัติการที่ตัดสินค่าเหล่านั้น
L
: ความยาวของรายการตารางหน้า
present
: แสดงว่าเพจอยู่ในหน่วยความจำ
ตารางหน้าตั้งอยู่บน RAM ตัวอย่างเช่นอาจตั้งอยู่เป็น:
--------------> 0xFFFFFFFF
--------------> PT1 + 0xFFFFF * L
Page Table 1
--------------> PT1
--------------> PT2 + 0xFFFFF * L
Page Table 2
--------------> PT2
--------------> 0x0
ตำแหน่งเริ่มต้นบน RAM สำหรับทั้งสองตารางเพจเป็นไปตามอำเภอใจและควบคุมโดย OS ขึ้นอยู่กับระบบปฏิบัติการเพื่อให้แน่ใจว่าจะไม่ทับซ้อนกัน!
แต่ละกระบวนการไม่สามารถสัมผัสกับตารางหน้าใด ๆ ได้โดยตรงแม้ว่าจะสามารถร้องขอไปยังระบบปฏิบัติการที่ทำให้ตารางเพจถูกแก้ไขได้เช่นขอให้มีสแต็กที่ใหญ่ขึ้นหรือส่วนฮีป
เพจเป็นกลุ่ม 4KB (12 บิต) และเนื่องจากแอดเดรสมี 32 บิตจึงต้องใช้เพียง 20 บิต (20 + 12 = 32 ดังนั้น 5 อักขระในสัญกรณ์ฐานสิบหก) เพื่อระบุแต่ละเพจ ค่านี้ได้รับการแก้ไขโดยฮาร์ดแวร์
รายการตารางหน้า
ตารางหน้าคือ ... ตารางหน้ารายการตาราง!
รูปแบบที่แน่นอนของรายการตารางได้รับการแก้ไขโดยฮาร์ดแวร์
ในตัวอย่างที่เรียบง่ายนี้รายการตารางเพจมีเพียงสองฟิลด์:
bits function
----- -----------------------------------------
20 physical address of the start of the page
1 present flag
ดังนั้นในตัวอย่างนี้นักออกแบบฮาร์ดแวร์สามารถเลือกL = 21
ได้
รายการตารางหน้าจริงส่วนใหญ่มีฟิลด์อื่น ๆ
การจัดตำแหน่งสิ่งต่าง ๆ ที่ 21 บิตเป็นไปไม่ได้เนื่องจากหน่วยความจำสามารถระบุแอดเดรสได้โดยไบต์ไม่ใช่บิต ดังนั้นแม้ในกรณีนี้จะต้องการเพียง 21 บิต แต่นักออกแบบฮาร์ดแวร์อาจเลือกที่L = 32
จะเข้าถึงได้เร็วขึ้นและเพียงแค่จองบิตที่เหลือไว้เพื่อใช้งานในภายหลัง ค่าจริงสำหรับL
บน x86 คือ 32 บิต
การแปลที่อยู่ในรูปแบบระดับเดียว
เมื่อตารางหน้าได้รับการจัดตั้งขึ้นโดย OS ที่แปลที่อยู่ระหว่างที่อยู่เส้นตรงและทางกายภาพจะทำโดยฮาร์ดแวร์
เมื่อระบบปฏิบัติการต้องการขั้นตอนการเปิดใช้งาน 1 ก็กำหนดcr3
ที่จะPT1
เริ่มต้นของตารางสำหรับกระบวนการหนึ่ง
หากกระบวนการ 1 ต้องการเข้าถึงที่อยู่เชิงเส้นวงจรฮาร์ดแวร์การ0x00000001
เพจจะทำสิ่งต่อไปนี้สำหรับระบบปฏิบัติการโดยอัตโนมัติ:
แบ่งที่อยู่เชิงเส้นออกเป็นสองส่วน:
| page (20 bits) | offset (12 bits) |
ดังนั้นในกรณีนี้เราจะมี:
- หน้า = 0x00000
- ชดเชย = 0x001
ดูในตารางหน้า 1 เพราะcr3
ชี้ไปที่มัน
ดูรายการ0x00000
เพราะนั่นคือส่วนของหน้า
ฮาร์ดแวร์รู้ว่ารายการนี้จะอยู่ที่ RAM PT1 + 0 * L = PT1
ที่อยู่
เนื่องจากมีอยู่การเข้าถึงจึงถูกต้อง
โดยตารางหน้าตั้งของหมายเลขหน้าอยู่ที่0x00000
0x00001 * 4K = 0x00001000
ในการค้นหาที่อยู่จริงสุดท้ายเราต้องเพิ่มออฟเซ็ต:
00001 000
+ 00000 001
-----------
00001 001
เนื่องจาก00001
เป็นที่อยู่จริงของหน้าเว็บที่ค้นหาบนตารางและ001
เป็นค่าชดเชย
ตามชื่อที่ระบุออฟเซ็ตจะถูกเพิ่มเพียงที่อยู่จริงของเพจเสมอ
จากนั้นฮาร์ดแวร์จะได้รับหน่วยความจำที่ตำแหน่งทางกายภาพนั้น
ในทำนองเดียวกันการแปลต่อไปนี้จะเกิดขึ้นสำหรับกระบวนการ 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
ตัวอย่างเช่นเมื่อเข้าถึงที่อยู่00001000
ส่วนของหน้าคือ00001
ฮาร์ดแวร์ที่รู้ว่ารายการตารางเพจอยู่ที่ที่อยู่ RAM: PT1 + 1 * L
( 1
เนื่องจากส่วนของหน้า) และนั่นคือที่ที่จะค้นหา
เมื่อระบบปฏิบัติการต้องการเปลี่ยนไปใช้กระบวนการ 2 สิ่งที่ต้องทำคือcr3
ชี้ไปที่หน้า 2 ง่ายๆแค่นั้นเอง!
ตอนนี้การแปลต่อไปนี้จะเกิดขึ้นสำหรับกระบวนการที่ 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
ที่อยู่เชิงเส้นเดียวกันจะแปลเป็นที่อยู่ทางกายภาพที่แตกต่างกันสำหรับกระบวนการที่แตกต่างกันขึ้นอยู่กับค่าภายในcr3
เท่านั้น
ด้วยวิธีนี้ทุกโปรแกรมสามารถคาดหวังว่าข้อมูลจะเริ่มต้น0
และสิ้นสุดที่FFFFFFFF
โดยไม่ต้องกังวลเกี่ยวกับที่อยู่จริงที่แน่นอน
ความผิดของเพจ
จะเกิดอะไรขึ้นถ้ากระบวนการ 1 พยายามเข้าถึงที่อยู่ภายในหน้าที่ไม่มีอยู่
ฮาร์ดแวร์จะแจ้งซอฟต์แวร์ผ่านข้อยกเว้นของ Page Fault
โดยปกติแล้วระบบปฏิบัติการจะต้องลงทะเบียนตัวจัดการข้อยกเว้นเพื่อตัดสินใจว่าจะต้องทำอะไร
เป็นไปได้ว่าการเข้าถึงหน้าที่ไม่ได้อยู่บนโต๊ะนั้นเป็นข้อผิดพลาดในการเขียนโปรแกรม
int is[1];
is[2] = 1;
แต่อาจมีบางกรณีที่ยอมรับได้เช่นใน Linux เมื่อ:
โปรแกรมต้องการเพิ่มสแต็ก
เพียงแค่พยายามเข้าถึงไบต์ที่กำหนดในช่วงที่เป็นไปได้ที่กำหนดและหากระบบปฏิบัติการพอใจก็จะเพิ่มหน้านั้นในพื้นที่แอดเดรสของกระบวนการ
หน้าถูกสลับไปยังดิสก์
ระบบปฏิบัติการจะต้องทำงานบางอย่างเบื้องหลังกระบวนการต่างๆเพื่อดึงหน้ากลับเข้าสู่ RAM
ระบบปฏิบัติการสามารถค้นพบว่ากรณีนี้ขึ้นอยู่กับเนื้อหาของรายการตารางหน้าส่วนที่เหลือเนื่องจากหากค่าสถานะปัจจุบันชัดเจนรายการอื่น ๆ ของรายการตารางหน้าจะถูกทิ้งไว้อย่างสมบูรณ์เพื่อให้ระบบปฏิบัติการเป็นไปตามที่ต้องการ
บน Linux เช่นเมื่อปัจจุบัน = 0:
ไม่ว่าในกรณีใด OS จำเป็นต้องทราบว่าที่อยู่ใดสร้าง Page Fault เพื่อให้สามารถจัดการกับปัญหาได้ นี่คือเหตุผลที่นักพัฒนา IA32 ที่ดีตั้งค่าcr2
เป็นที่อยู่นั้นเมื่อใดก็ตามที่เกิด Page Fault ขึ้น ตัวจัดการข้อยกเว้นสามารถตรวจสอบcr2
เพื่อรับที่อยู่ได้
การลดความซับซ้อน
ความเรียบง่ายสู่ความเป็นจริงที่ทำให้ตัวอย่างนี้เข้าใจง่ายขึ้น:
วงจรการเพจจริงทั้งหมดใช้การเพจหลายระดับเพื่อประหยัดเนื้อที่ แต่สิ่งนี้แสดงให้เห็นถึงรูปแบบระดับเดียวที่เรียบง่าย
ตารางเพจมีเพียงสองฟิลด์: แอดเดรส 20 บิตและแฟล็กปัจจุบัน 1 บิต
ตารางหน้าจริงมีทั้งหมด 12 ช่องดังนั้นคุณลักษณะอื่น ๆ ที่ถูกละเว้น
ตัวอย่าง: โครงร่างการเพจหลายระดับ
ปัญหาเกี่ยวกับรูปแบบการเพจระดับเดียวคือจะใช้ RAM มากเกินไป: 4G / 4K = 1M รายการต่อกระบวนการ หากแต่ละรายการมีความยาว 4 ไบต์นั่นจะทำให้ 4M ต่อกระบวนการซึ่งมากเกินไปสำหรับคอมพิวเตอร์เดสก์ท็อป: ps -A | wc -l
บอกว่าตอนนี้ฉันใช้ 244 กระบวนการดังนั้นจะต้องใช้ RAM ประมาณ 1GB!
ด้วยเหตุนี้นักพัฒนา x86 จึงตัดสินใจใช้รูปแบบหลายระดับที่ลดการใช้ RAM
ข้อเสียของระบบนี้คือมีเวลาในการเข้าถึงที่สูงขึ้นเล็กน้อย
ในรูปแบบการเพจแบบ 3 ระดับที่ใช้สำหรับโปรเซสเซอร์ 32 บิตที่ไม่มี PAE บิตแอดเดรส 32 บิตจะถูกแบ่งออกเป็นดังนี้:
| directory (10 bits) | table (10 bits) | offset (12 bits) |
แต่ละกระบวนการต้องมีไดเร็กทอรีเพจเดียวและหนึ่งเพจเท่านั้นที่เชื่อมโยงอยู่ดังนั้นจึงจะมี2^10 = 1K
รายการไดเร็กทอรีเพจเป็นอย่างน้อยซึ่งดีกว่า 1M ขั้นต่ำที่จำเป็นในรูปแบบระดับเดียว
ตารางหน้าจะถูกจัดสรรตามที่ OS ต้องการเท่านั้น แต่ละตาราง2^10 = 1K
เพจมีรายการไดเร็กทอรีเพจ
ไดเรกทอรีหน้าประกอบด้วย ... รายการไดเรกทอรีหน้า! รายการไดเรกทอรีหน้าเป็นเช่นเดียวกับรายการตารางเพจยกเว้นว่าพวกเขาชี้ไปยังที่อยู่ของตาราง RAM หน้าแทนการอยู่ทางกายภาพของตาราง เนื่องจากแอดเดรสเหล่านี้มีความกว้างเพียง 20 บิตตารางเพจจึงต้องอยู่ที่จุดเริ่มต้นของเพจ 4KB
cr3
ตอนนี้ชี้ไปที่ตำแหน่งบน RAM ของไดเรกทอรีเพจของกระบวนการปัจจุบันแทนที่จะเป็นตารางเพจ
รายการตารางหน้าไม่เปลี่ยนแปลงเลยจากโครงร่างระดับเดียว
ตารางหน้าเปลี่ยนจากโครงร่างระดับเดียวเนื่องจาก:
- แต่ละกระบวนการอาจมีได้ถึง 1K ตารางเพจหนึ่งรายการต่อหน้า
- แต่ละตารางหน้าจะมีรายการ 1K แทนรายการ 1 ล้านรายการ
เหตุผลในการใช้ 10 บิตในสองระดับแรก (ไม่ใช่เช่นนั้น12 | 8 | 12
) คือแต่ละรายการของตารางเพจมีความยาว 4 ไบต์ จากนั้นรายการ 2 ^ 10 ของไดเรกทอรีหน้าและตารางหน้าจะพอดีกับหน้า 4Kb ซึ่งหมายความว่าจะจัดสรรและยกเลิกการจัดสรรเพจสำหรับวัตถุประสงค์นั้นได้เร็วและง่ายขึ้น
การแปลที่อยู่ในรูปแบบหลายระดับ
ไดเร็กทอรีเพจที่กำหนดให้กับกระบวนการ 1 โดย OS:
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
ตารางหน้าที่กำหนดให้กับกระบวนการ 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
ตารางหน้าที่กำหนดให้กับกระบวนการ 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
ที่ไหน:
PD1
: ตำแหน่งเริ่มต้นของไดเรกทอรีหน้าของกระบวนการ 1 บน RAM
PT1
และPT2
: ตำแหน่งเริ่มต้นของตารางหน้า 1 และตารางหน้า 2 สำหรับกระบวนการ 1 บน RAM
ดังนั้นในตัวอย่างนี้ไดเร็กทอรีเพจและตารางเพจสามารถเก็บไว้ใน RAM ได้เช่น:
----------------> 0xFFFFFFFF
----------------> PT2 + 0x3FF * L
Page Table 1
----------------> PT2
----------------> PD1 + 0x3FF * L
Page Directory 1
----------------> PD1
----------------> PT1 + 0x3FF * L
Page Table 2
----------------> PT1
----------------> 0x0
มาแปลที่อยู่เชิงเส้น0x00801004
ทีละขั้นตอน
เราคิดว่าcr3 = PD1
นั่นคือมันชี้ไปที่ไดเรกทอรีเพจที่อธิบายไว้
ในไบนารีแอดเดรสเชิงเส้นคือ:
0 0 8 0 1 0 0 4
0000 0000 1000 0000 0001 0000 0000 0100
การจัดกลุ่มตามที่10 | 10 | 12
ให้:
0000000010 0000000001 000000000100
0x2 0x1 0x4
ซึ่งจะช่วยให้:
- รายการไดเรกทอรีหน้า = 0x2
- รายการตารางหน้า = 0x1
- ชดเชย = 0x4
ดังนั้นฮาร์ดแวร์จึงมองหารายการ 2 ของไดเรกทอรีเพจ
0x80000 * 4K = 0x80000000
ตารางไดเรกทอรีหน้ากล่าวว่าตารางหน้าตั้งอยู่ที่ นี่เป็นการเข้าถึง RAM ครั้งแรกของกระบวนการ
ตั้งแต่รายการตารางหน้าเป็น0x1
ฮาร์ดแวร์ที่มีลักษณะที่รายการ 1 ของตารางหน้าที่ซึ่งจะบอกว่าที่หน้าทางกายภาพตั้งอยู่ที่อยู่0x80000000
0x0000C * 4K = 0x0000C000
นี่เป็นการเข้าถึง RAM ครั้งที่สองของกระบวนการ
0x0000C004
สุดท้ายฮาร์ดแวร์เพจเพิ่มชดเชยและที่อยู่สุดท้ายคือ
ตัวอย่างอื่น ๆ ของที่อยู่ที่แปล ได้แก่ :
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
ข้อบกพร่องของเพจเกิดขึ้นหากไม่มีรายการไดเร็กทอรีเพจหรือรายการตารางเพจ
หากระบบปฏิบัติการต้องการเรียกใช้กระบวนการอื่นไปพร้อมกันระบบจะให้กระบวนการที่สองแยกไดเรกทอรีหน้าและเชื่อมโยงไดเรกทอรีนั้นเพื่อแยกตารางหน้า
สถาปัตยกรรม 64 บิต
64 บิตยังคงเป็นแอดเดรสที่มากเกินไปสำหรับขนาด RAM ในปัจจุบันดังนั้นสถาปัตยกรรมส่วนใหญ่จะใช้บิตน้อยลง
x86_64 ใช้ 48 บิต (256 TiB) และ PAE ของโหมดดั้งเดิมอนุญาตที่อยู่ 52 บิต (4 PiB) แล้ว
12 จาก 48 บิตเหล่านั้นถูกสงวนไว้สำหรับออฟเซ็ตซึ่งเหลือ 36 บิต
หากใช้แนวทาง 2 ระดับการแบ่งที่ดีที่สุดคือสองระดับ 18 บิต
แต่นั่นหมายความว่าไดเร็กทอรีเพจจะมี2^18 = 256K
รายการซึ่งจะใช้ RAM มากเกินไป: ใกล้เคียงกับการเพจระดับเดียวสำหรับสถาปัตยกรรม 32 บิต!
ดังนั้นสถาปัตยกรรม 64 บิตจึงสร้างระดับหน้าได้มากขึ้นโดยทั่วไปคือ 3 หรือ 4
x86_64 ใช้ 4 ระดับใน9 | 9 | 9 | 12
แบบแผนดังนั้นระดับบนจะขึ้นเฉพาะ2^9
รายการระดับที่สูงกว่าเท่านั้น
PAE
ส่วนขยายที่อยู่ทางกายภาพ
ด้วย 32 บิตแรม 4GB เท่านั้นที่สามารถจัดการได้
สิ่งนี้เริ่มกลายเป็นข้อ จำกัด สำหรับเซิร์ฟเวอร์ขนาดใหญ่ดังนั้น Intel จึงนำกลไก PAE มาใช้กับ Pentium Pro
เพื่อบรรเทาปัญหา Intel ได้เพิ่มบรรทัดที่อยู่ใหม่ 4 บรรทัดเพื่อให้สามารถแก้ไขปัญหาได้ที่ 64GB
โครงสร้างตารางหน้าจะเปลี่ยนแปลงด้วยหากเปิด PAE วิธีที่แน่นอนในการเปลี่ยนแปลงนั้นขึ้นอยู่กับว่า PSE เปิดหรือปิดอยู่
PAE เปิดและปิดผ่านทางPAE
บิตของcr4
.
แม้ว่าหน่วยความจำแอดเดรสทั้งหมดจะเป็น 64GB แต่แต่ละกระบวนการก็ยังสามารถใช้งานได้สูงสุด 4GB เท่านั้น อย่างไรก็ตามระบบปฏิบัติการสามารถวางกระบวนการที่แตกต่างกันในชิ้น 4GB ที่แตกต่างกัน
PSE
ส่วนขยายขนาดหน้า
อนุญาตให้เพจมีความยาว 4M (หรือ 2M ถ้า PAE เปิดอยู่) แทนที่จะเป็น 4K
PSE เปิดและปิดผ่านPAE
บิตของcr4
.
โครงร่างตารางเพจ PAE และ PSE
หาก PAE และ PSE ทำงานอยู่จะใช้รูปแบบระดับการเพจที่แตกต่างกัน:
ไม่มี PAE และไม่มี PSE: 10 | 10 | 12
ไม่มี PAE และ 10 | 22
PSE:
22 คือค่าชดเชยภายในเพจ 4Mb เนื่องจาก 22 บิตแอดเดรส 4Mb
PAE และไม่มี PSE: 2 | 9 | 9 | 12
เหตุผลในการออกแบบว่าทำไมต้องใช้ 9 สองครั้งแทนที่จะเป็น 10 คือตอนนี้รายการไม่สามารถใส่เป็น 32 บิตได้อีกต่อไปซึ่งทั้งหมดถูกเติมด้วยบิตแอดเดรส 20 บิตและบิตแฟล็กที่มีความหมายหรือสงวนไว้ 12 บิต
เหตุผลก็คือ 20 บิตไม่เพียงพอที่จะแสดงที่อยู่ของตารางเพจอีกต่อไปแล้วตอนนี้จำเป็นต้องใช้ 24 บิตเนื่องจากมีการเพิ่มสายไฟพิเศษ 4 สายลงในโปรเซสเซอร์
ดังนั้นนักออกแบบจึงตัดสินใจเพิ่มขนาดรายการเป็น 64 บิตและเพื่อให้พอดีกับตารางหน้าเดียวจึงจำเป็นต้องลดจำนวนรายการลงเหลือ 2 ^ 9 แทนที่จะเป็น 2 ^ 10
2 เริ่มต้นคือระดับเพจใหม่ที่เรียกว่า Page Directory Pointer Table (PDPT) เนื่องจากชี้ไปที่ไดเรกทอรีเพจและกรอกแอดเดรสเชิงเส้น 32 บิต PDPT ยังมีความกว้าง 64 บิต
cr3
ตอนนี้ชี้ไปที่ PDPT ซึ่งต้องอยู่ในหน่วยความจำ 4GB สี่กำปั้นและจัดเรียงบน 32 บิตทวีคูณเพื่อประสิทธิภาพในการจัดการ ซึ่งหมายความว่าตอนนี้cr3
มี 27 บิตสำคัญแทนที่จะเป็น 20: 2 ^ 5 สำหรับ 32 ทวีคูณ * 2 ^ 27 เพื่อทำให้ 2 ^ 32 ของ 4GB แรกสมบูรณ์
PAE และ PSE: 2 | 9 | 21
นักออกแบบตัดสินใจที่จะเก็บช่องกว้าง 9 บิตเพื่อให้พอดีกับหน้าเดียว
ซึ่งจะเหลือ 23 บิต การออกจาก 2 สำหรับ PDPT เพื่อให้สิ่งต่าง ๆ สม่ำเสมอด้วยเคส PAE โดยไม่มี PSE ออกจาก 21 สำหรับออฟเซ็ตหมายความว่าหน้ากว้าง 2M แทนที่จะเป็น 4M
TLB
Translation Lookahead Buffer (TLB) เป็นแคชสำหรับเพจแอดเดรส
เนื่องจากเป็นแคชจึงแชร์ปัญหาการออกแบบหลายอย่างของแคชของ CPU เช่นระดับการเชื่อมโยง
ส่วนนี้จะอธิบาย TLB ที่เชื่อมโยงอย่างสมบูรณ์แบบง่ายพร้อมด้วยรายการที่อยู่เดียว 4 รายการ โปรดทราบว่าเช่นเดียวกับแคชอื่น ๆ TLB จริงมักจะไม่เชื่อมโยงกันอย่างสมบูรณ์
การทำงานพื้นฐาน
หลังจากการแปลระหว่างที่อยู่เชิงเส้นและที่อยู่จริงเกิดขึ้นจะถูกจัดเก็บไว้ใน TLB ตัวอย่างเช่น TLB 4 รายการเริ่มต้นในสถานะต่อไปนี้:
valid linear physical
------ ------- ---------
> 0 00000 00000
0 00000 00000
0 00000 00000
0 00000 00000
>
บ่งชี้รายการปัจจุบันจะถูกแทนที่
และหลังจากที่อยู่เชิงเส้นของหน้า00003
ถูกแปลเป็นที่อยู่จริง00005
TLB จะกลายเป็น:
valid linear physical
------ ------- ---------
1 00003 00005
> 0 00000 00000
0 00000 00000
0 00000 00000
และหลังจากแปลเป็นครั้งที่สอง00007
จะ00009
กลายเป็น:
valid linear physical
------ ------- ---------
1 00003 00005
1 00007 00009
> 0 00000 00000
0 00000 00000
ตอนนี้ถ้า00003
ต้องมีการแปลอีกครั้งฮาร์ดแวร์แรกมีลักษณะขึ้น TLB 00003 --> 00005
และพบว่าที่อยู่ที่มีการเข้าถึง
แน่นอน00000
ว่าไม่ได้อยู่ใน TLB เนื่องจากไม่มีรายการที่ถูกต้อง00000
เป็นคีย์
นโยบายการเปลี่ยน
เมื่อเติม TLB แล้วที่อยู่เก่าจะถูกเขียนทับ เช่นเดียวกับแคชของ CPU นโยบายการแทนที่คือการดำเนินการที่อาจซับซ้อน แต่การหาสาเหตุที่เรียบง่ายและสมเหตุสมผลคือการลบรายการที่ใช้น้อยที่สุด (LRU)
ด้วย LRU เริ่มต้นจากรัฐ:
valid linear physical
------ ------- ---------
> 1 00003 00005
1 00007 00009
1 00009 00001
1 0000B 00003
การเพิ่ม0000D -> 0000A
จะให้:
valid linear physical
------ ------- ---------
1 0000D 0000A
> 1 00007 00009
1 00009 00001
1 0000B 00003
ลูกเบี้ยว
การใช้ TLB ทำให้การแปลเร็วขึ้นเนื่องจากการแปลเริ่มต้นใช้การเข้าถึงหนึ่งครั้งต่อระดับ TLBซึ่งหมายถึง 2 ในรูปแบบ 32 บิตแบบธรรมดา แต่ 3 หรือ 4 ในสถาปัตยกรรม 64 บิต
TLB มักจะใช้เป็นแรมราคาแพงที่เรียกว่า content-addressable memory (CAM) CAM ใช้การเชื่อมโยงแผนที่บนฮาร์ดแวร์นั่นคือโครงสร้างที่กำหนดคีย์ (ที่อยู่เชิงเส้น) ดึงค่า
นอกจากนี้ยังสามารถใช้การแมปกับที่อยู่ RAM ได้ แต่การแมป CAM อาจต้องใช้รายการน้อยกว่าการแมป RAM
ตัวอย่างเช่นแผนที่ที่:
- ทั้งคีย์และค่ามี 20 บิต (ในกรณีของโครงร่างการเพจทั่วไป)
- ต้องจัดเก็บไม่เกิน 4 ค่าในแต่ละครั้ง
สามารถจัดเก็บใน TLB ได้ 4 รายการ:
linear physical
------- ---------
00000 00001
00001 00010
00010 00011
FFFFF 00000
อย่างไรก็ตามในการใช้สิ่งนี้กับ RAM จำเป็นต้องมีที่อยู่ 2 ^ 20 :
linear physical
------- ---------
00000 00001
00001 00010
00010 00011
... (from 00011 to FFFFE)
FFFFF 00000
ซึ่งจะแพงกว่าการใช้ TLB ด้วยซ้ำ
รายการไม่ถูกต้อง
เมื่อมีcr3
การเปลี่ยนแปลงรายการ TLB ทั้งหมดจะไม่ถูกต้องเนื่องจากจะมีการใช้ตารางหน้าใหม่สำหรับกระบวนการใหม่ดังนั้นจึงไม่น่าเป็นไปได้ที่รายการเก่า ๆ จะมีความหมาย
x86 ยังมีinvlpg
คำสั่งที่ทำให้รายการ TLB รายการเดียวเป็นโมฆะอย่างชัดเจน สถาปัตยกรรมอื่นเสนอคำแนะนำเพิ่มเติมสำหรับรายการ TLB ที่ไม่ถูกต้องเช่นการทำให้รายการทั้งหมดในช่วงที่กำหนด
บางซีพียู x86 ไปเกินความต้องการของสเปค x86 และให้การเชื่อมโยงกันมากขึ้นกว่าที่รับประกัน, ระหว่างการปรับเปลี่ยนรายการตารางหน้าและใช้มันเมื่อมันถูกไม่แคแล้วใน TLB เห็นได้ชัดว่า Windows 9x ใช้สิ่งนั้นเพื่อความถูกต้อง แต่ซีพียู AMD ที่ทันสมัยไม่ได้ให้การเดินหน้าที่สอดคล้องกัน ซีพียูของ Intel ทำได้แม้ว่าจะต้องตรวจจับการคาดเดาที่ผิดพลาดเพื่อทำเช่นนั้น การใช้ประโยชน์จากสิ่งนี้อาจเป็นความคิดที่ไม่ดีเนื่องจากอาจมีไม่มากนักที่จะได้รับและมีความเสี่ยงสูงที่จะก่อให้เกิดปัญหาที่ละเอียดอ่อนเกี่ยวกับเวลาซึ่งยากที่จะแก้ไขข้อบกพร่อง
การใช้งานเคอร์เนล Linux
เคอร์เนลลินุกซ์ใช้ประโยชน์จากคุณสมบัติการเพจของ x86 อย่างกว้างขวางเพื่อให้สามารถสลับกระบวนการที่รวดเร็วด้วยการกระจายข้อมูลขนาดเล็ก
ในv4.2
ดูภายใต้arch/x86/
:
include/asm/pgtable*
include/asm/page*
mm/pgtable*
mm/page*
ดูเหมือนว่าจะไม่มีโครงสร้างที่กำหนดเพื่อแสดงเพจมีเพียงมาโครเท่านั้นที่include/asm/page_types.h
น่าสนใจเป็นพิเศษ ข้อความที่ตัดตอนมา:
#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
กำหนดCR0
และโดยเฉพาะPG
ตำแหน่งบิต:
#define X86_CR0_PG_BIT 31 /* Paging */
บรรณานุกรม
ฟรี:
ไม่ฟรี: