แต่ละภูมิภาคในแผนภาพเป็นเซ็กเมนต์หรือไม่
นี่คือการใช้คำว่า "เซ็กเมนต์" เกือบทั้งหมดโดยสิ้นเชิง
- x86 แบ่งส่วน / ส่วนการลงทะเบียน: ระบบปฏิบัติการ x86 ที่ทันสมัยใช้รูปแบบแบนหน่วยความจำที่ทุกกลุ่มมีฐานเดียวกัน = 0 และ จำกัด = สูงสุดในโหมด 32 บิตเช่นเดียวกับการบังคับใช้ฮาร์ดแวร์ที่ในโหมด 64 บิตทำให้ชนิดแบ่งส่วนของร่องรอย . (ยกเว้น FS หรือ GS ใช้สำหรับการจัดเก็บในเธรดโลคัลแม้ในโหมด 64 บิต)
- Linker / program-loader ส่วน / เซกเมนต์ ( ความแตกต่างของส่วนและส่วนในรูปแบบไฟล์เอลฟ์คืออะไร )
ประเพณีที่มีต้นกำเนิดที่พบบ่อย: ถ้าคุณได้รับการใช้หน่วยความจำแบบแบ่งกลุ่ม (โดยเฉพาะโดยไม่ต้องหน่วยความจำเสมือนเพจ) คุณอาจมีข้อมูลและที่อยู่ BSS จะเทียบกับฐาน DS ส่วนกองเทียบกับฐานเอสเอสและรหัสเทียบกับ ที่อยู่ฐาน CS
ดังนั้นจึงสามารถโหลดโปรแกรมที่แตกต่างกันมากมายไปยังที่อยู่เชิงเส้นที่แตกต่างกันหรือแม้กระทั่งย้ายหลังจากเริ่มต้นโดยไม่ต้องเปลี่ยนออฟเซ็ต 16 หรือ 32 บิตเทียบกับฐานเซ็กเมนต์
แต่คุณต้องรู้ว่าตัวชี้ส่วนใดสัมพันธ์กับดังนั้นคุณจึงมี "พอยน์เตอร์พอยน์เตอร์" และอื่น ๆ (ที่จริงแล้วโปรแกรม x86 แบบ 16 บิตมักไม่จำเป็นต้องเข้าถึงรหัสของพวกเขาเป็นข้อมูลดังนั้นสามารถใช้เซ็กเมนต์รหัส 64k บางแห่งและอาจเป็นอีกบล็อก 64k ที่มี DS = SS โดยที่สแต็กเพิ่มขึ้นจากออฟเซ็ตสูงและข้อมูลที่ ด้านล่างหรือโมเดลรหัสขนาดจิ๋วที่มีทุกเซกเมนต์เท่ากัน)
การแบ่งกลุ่ม x86 โต้ตอบกับการเพจอย่างไร
การจับคู่ที่อยู่ในโหมด 32/64 บิตคือ:
- เซกเมนต์: ออฟเซ็ต (ฐานเซกเมนต์แสดงถึงการลงทะเบียนที่ถือออฟเซ็ตหรือแทนที่ด้วยคำนำหน้าคำสั่ง)
- ที่อยู่เสมือนเชิงเส้น 32 หรือ 64 บิต = เบส + ออฟเซ็ต (ในรูปแบบหน่วยความจำแบบแบนเช่น Linux ใช้ตัวชี้ / offsets = ที่อยู่เชิงเส้นด้วยเช่นกันยกเว้นเมื่อเข้าถึง TLS ที่เกี่ยวข้องกับ FS หรือ GS)
ตารางหน้า (ที่แคชโดย TLB) ทำแผนที่เป็นเส้นตรงถึง 32 (โหมดดั้งเดิม), 36 (มรดก PAE) หรือที่อยู่ทางกายภาพ 52 บิต (x86-64) ( /programming/46509152/why-in-64bit-the-virtual-address-are-4-bits-short-48bit-long-compared-with-the )
ขั้นตอนนี้เป็นทางเลือก: เพจจะต้องเปิดใช้งานในระหว่างการบูทเครื่องโดยการตั้งค่าบิตในรีจิสเตอร์ควบคุม ที่อยู่เชิงเส้นคือที่อยู่ทางกายภาพ
ขอให้สังเกตว่าการแบ่งส่วนไม่อนุญาตให้คุณใช้พื้นที่แอดเดรสเสมือนมากกว่า 32 หรือ 64 บิตในกระบวนการเดียว (หรือเธรด)เนื่องจากพื้นที่แอดเดรสแบบเรียบ (เป็นเส้นตรง) ทุกอย่างถูกแมปเข้าด้วยกันจะมีจำนวนบิตเท่ากันเมื่อออฟเซ็ตตัวเอง (นี่ไม่ใช่กรณีสำหรับ x86 แบบ 16 บิตซึ่งการแบ่งส่วนนั้นมีประโยชน์จริง ๆ สำหรับการใช้หน่วยความจำมากกว่า 64k โดยส่วนใหญ่จะเป็นการลงทะเบียนและออฟเซ็ตแบบ 16 บิต)
CPU แคชตัวอธิบายส่วนโหลดจาก GDT (หรือ LDT) รวมถึงฐานส่วน เมื่อคุณยกเลิกการลงทะเบียนตัวชี้ขึ้นอยู่กับสิ่งที่ลงทะเบียนมันจะมีค่าเริ่มต้นเป็น DS หรือ SS เป็นเซกเมนต์ ค่าลงทะเบียน (ตัวชี้) ถือเป็นออฟเซ็ตจากฐานเซกเมนต์
เนื่องจากฐานของเซกเมนต์เป็นศูนย์โดยปกติซีพียูจึงต้องทำกรณีพิเศษนี้ หรือจากมุมมองอื่นถ้าคุณทำมีไม่ใช่ศูนย์ฐานส่วนโหลดมีความล่าช้าเป็นพิเศษเพราะ "พิเศษ" (ปกติ) กรณีที่ผ่านการเพิ่มที่อยู่ฐานใช้ไม่ได้
Linux ตั้งค่าการลงทะเบียนเซกเมนต์ x86 อย่างไร:
ฐานและขีด จำกัด ของ CS / DS / ES / SS เป็น 0 / -1 ทั้งหมดในโหมด 32 และ 64 บิต สิ่งนี้เรียกว่าแบบจำลองหน่วยความจำแบบแบนเนื่องจากพอยน์เตอร์ทั้งหมดชี้ไปยังพื้นที่ที่อยู่เดียวกัน
(สถาปนิกซีพียูของ AMD ใช้การแบ่งเซ็กเมนต์โดยการบังคับใช้โมเดลหน่วยความจำแบบแบนสำหรับโหมด 64 บิตเนื่องจากระบบปฏิบัติการหลักไม่ได้ใช้งานอยู่ยกเว้นการป้องกันแบบไม่มีผู้บริหารซึ่งให้วิธีที่ดีกว่ามากโดยการเพจกับ PAE หรือ x86- รูปแบบตารางหน้า 64)
TLS (Thread Local Storage): FS และ GS ไม่ได้รับการแก้ไขที่ base = 0 ในโหมดยาว (เป็นรุ่นใหม่ที่มี 386 และไม่ได้ใช้โดยนัยโดยคำแนะนำใด ๆ แม้แต่rep
คำสั่ง -string ที่ใช้ ES) x86-64 Linux ตั้งค่าที่อยู่ฐาน FS สำหรับแต่ละเธรดเป็นที่อยู่ของบล็อก TLS
เช่นmov eax, [fs: 16]
โหลดค่า 32- บิตจาก 16 ไบต์ลงในบล็อก TLS สำหรับเธรดนี้
ตัวอธิบายเซ็กเมนต์ CS เลือกโหมดที่ CPU อยู่ (โหมดการป้องกัน 16/32/64 บิต / โหมดยาว) Linux ใช้รายการ GDT เดียวสำหรับกระบวนการพื้นที่ผู้ใช้ 64 บิตทั้งหมดและรายการ GDT อื่นสำหรับกระบวนการพื้นที่ผู้ใช้แบบ 32 บิตทั้งหมด (เพื่อให้ CPU ทำงานถูกต้อง DS / ES ต้องถูกตั้งค่าเป็นรายการที่ถูกต้องและ SS ก็เช่นกัน) นอกจากนี้ยังเลือกระดับสิทธิ์ (เคอร์เนล (แหวน 0) กับผู้ใช้ (แหวน 3)) ดังนั้นแม้เมื่อกลับไปที่พื้นที่ผู้ใช้ 64- บิตเคอร์เนลยังคงต้องจัดให้ CS เปลี่ยนใช้iret
หรือsysret
แทนที่จะเป็นปกติ กระโดดหรือสั่งสอน ret
ใน x86-64 syscall
จุดเริ่มต้นใช้swapgs
เพื่อพลิก GS จาก GS ของพื้นที่ผู้ใช้เป็นเคอร์เนลซึ่งใช้เพื่อค้นหาเคอร์เนลสแต็กสำหรับเธรดนี้ (กรณีพิเศษของหน่วยเก็บเธรดโลคัล) syscall
การเรียนการสอนไม่ได้เปลี่ยนสแตกชี้ไปยังจุดที่แตกเคอร์เนล; ก็ยังคงชี้ไปที่กองผู้ใช้เมื่อเคอร์เนลถึงจุดเริ่มต้นที่1
DS / ES / SS ต้องถูกตั้งค่าเป็น descriptor เซ็กเมนต์ที่ถูกต้องเพื่อให้ CPU ทำงานในโหมดที่ได้รับการป้องกัน / โหมดยาวแม้ว่าฐาน / ลิมิตจาก descriptor เหล่านั้นจะถูกเพิกเฉยในโหมดยาว
ดังนั้นโดยทั่วไปการแบ่งส่วน x86 นั้นใช้สำหรับ TLS และสำหรับสิ่งที่จำเป็นต้องมีสำหรับ x86 osdev ที่ฮาร์ดแวร์ต้องการให้คุณทำ
เชิงอรรถ 1: ความสนุกในประวัติศาสตร์: มีการจัดเก็บรายชื่อผู้รับจดหมายของข้อความระหว่างเคอร์เนล devs และสถาปนิกของ AMD จากสองสามปีก่อนที่จะเปิดตัวซิลิกอน AMD64 ซึ่งส่งผลให้การออกแบบปรับแต่งเพื่อsyscall
ให้สามารถใช้งานได้ ดูลิงก์ในคำตอบนี้สำหรับรายละเอียด