Linux ไม่ได้ใช้การแบ่งกลุ่ม แต่แบ่งหน้าเท่านั้น


24

Linux Programming Interfaceแสดงโครงร่างของพื้นที่ที่อยู่เสมือนของกระบวนการ แต่ละภูมิภาคในแผนภาพเป็นเซ็กเมนต์หรือไม่

ป้อนคำอธิบายรูปภาพที่นี่

จากความเข้าใจ Linux Kernel ,

ถูกต้องหรือไม่ว่าต่อไปนี้หมายความว่าหน่วยการแบ่งเซ็กเมนต์ใน MMU แมปเซ็กเมนต์และออฟเซ็ตภายในเซ็กเมนต์ไปยังที่อยู่หน่วยความจำเสมือนและหน่วยเพจจะแมปที่อยู่หน่วยความจำเสมือนกับที่อยู่หน่วยความจำทางกายภาพ

หน่วยความจำการจัดการหน่วย (MMU) แปลงที่อยู่เชิงตรรกะเป็นที่อยู่เชิงเส้นโดยใช้วงจรของฮาร์ดแวร์ที่เรียกว่าหน่วยการแบ่งส่วน; ต่อมาวงจรฮาร์ดแวร์ตัวที่สองที่เรียกว่าหน่วยเพจจะแปลงที่อยู่เชิงเส้นเป็นที่อยู่ทางกายภาพ (ดูรูปที่ 2-1)

ป้อนคำอธิบายรูปภาพที่นี่

ถ้าอย่างนั้นทำไมมันถึงบอกว่าลีนุกซ์ไม่ได้ใช้การแบ่งกลุ่ม แต่เป็นเพจจิ้งเท่านั้น?

การแบ่งกลุ่มได้รวมอยู่ในไมโครโปรเซสเซอร์ 80x86 เพื่อสนับสนุนให้โปรแกรมเมอร์แยกแอปพลิเคชันของพวกเขาออกเป็นเอนทิตีที่เกี่ยวข้องกับเหตุผลเช่นรูทีนย่อยหรือพื้นที่ข้อมูลทั่วโลกและท้องถิ่น อย่างไรก็ตาม Linux ใช้การแบ่งกลุ่มในวิธีที่ จำกัด มาก ในความเป็นจริงการแบ่งส่วนและการแบ่งหน้าค่อนข้างซ้ำซ้อนเนื่องจากทั้งสองสามารถใช้เพื่อแยกพื้นที่ที่อยู่ทางกายภาพของกระบวนการ: การแบ่งส่วนสามารถกำหนดพื้นที่ที่อยู่เชิงเส้นที่แตกต่างกันให้กับแต่ละกระบวนการในขณะที่เพจจิ้งสามารถแมป . Linux ต้องการแบ่งหน้าเป็นการแบ่งกลุ่มด้วยเหตุผลต่อไปนี้:

•การจัดการหน่วยความจำง่ายขึ้นเมื่อกระบวนการทั้งหมดใช้ค่าการลงทะเบียนเซกเมนต์เดียวกันนั่นคือเมื่อพวกเขาแชร์ที่อยู่เชิงเส้นชุดเดียวกัน

•หนึ่งในวัตถุประสงค์การออกแบบของ Linux คือการพกพาไปยังสถาปัตยกรรมที่หลากหลาย โดยเฉพาะอย่างยิ่งสถาปัตยกรรม RISC ได้รับการสนับสนุนอย่าง จำกัด สำหรับการแบ่งส่วน

Linux เวอร์ชัน 2.6 ใช้การแบ่งส่วนเมื่อจำเป็นโดยสถาปัตยกรรม 80x86 เท่านั้น


คุณช่วยระบุรุ่นได้ไหม การระบุชื่อผู้แต่งอาจเป็นประโยชน์ อย่างน้อยฉันก็รู้ว่าอย่างแรกคือจากบุคคลที่มีชื่อเสียง อย่างไรก็ตามชื่อหนังสือทั้งสองชื่อเป็นชื่อสามัญมันไม่ชัดเจนสำหรับฉันในตอนแรกที่คุณพูดถึงหนังสือ :-)
sourcejedi

2
เรื่อง "การแบ่งส่วนได้รวมอยู่ในไมโครโปรเซสเซอร์ 80x86 ... ": นั่นไม่เป็นความจริง มันเป็นมรดกตกทอดของโปรเซสเซอร์ 808x ซึ่งมีพอยน์เตอร์ข้อมูล 16 บิตและเซ็กเมนต์หน่วยความจำ 64 Kb ตัวชี้แบ่งกลุ่มช่วยให้คุณสามารถสลับกลุ่มเพื่อระบุหน่วยความจำเพิ่มเติม สถาปัตยกรรมนั้นดำเนินไปจนถึง 80x86 (โดยขนาดตัวชี้เพิ่มขึ้นเป็น 33 บิต) ทุกวันนี้ในรุ่น x86_64 คุณมีพอยน์เตอร์ 64 บิตซึ่งในทางทฤษฎี (ฉันคิดว่ามีเพียง 48 บิตเท่านั้นที่ใช้จริง) ที่อยู่ 16 เมกะไบต์ดังนั้นจึงไม่จำเป็นต้องแบ่งกลุ่ม
jamesqf

2
@jamesqf แล้วโหมดการป้องกันแบบ 32 บิตใน 386 นั้นสนับสนุนส่วนที่แตกต่างจากตัวชี้ขนาด 16 ไบต์ที่อยู่ใน 8086 ดังนั้นมันจึงไม่ใช่แค่มรดกธรรมดา นั่นไม่ได้พูดอะไรเกี่ยวกับประโยชน์ของพวกเขาแน่นอน
ilkkachu

@jamesqf 80186 มีหน่วยความจำแบบเดียวกับ 8086 ไม่มี "33 บิต"
Jasen

ไม่มีค่าพอที่จะตอบดังนั้นจึงเป็นเพียงความเห็น: ส่วนและหน้าจะเปรียบเทียบได้เฉพาะในบริบทของการแลกเปลี่ยน (เช่นการสลับหน้ากับการแลกเปลี่ยนส่วน) และในบริบทนั้น หากคุณสลับส่วนเข้า / ออกคุณจะต้องสลับส่วนทั้งหมดซึ่งอาจเป็น 2-4GB สิ่งนี้ไม่เคยเป็นเรื่องจริงที่จะนำมาใช้กับ x86 ด้วยหน้าเว็บคุณสามารถทำงานกับหน่วย 4KB ได้ตลอดเวลา เมื่อมันมาถึงการเข้าถึงหน่วยความจำแล้วส่วนและหน้ามีความสัมพันธ์ผ่านตารางหน้าและการเปรียบเทียบจะเป็นแอปเปิ้ลกับส้ม
vhu

คำตอบ:


20

สถาปัตยกรรม x86-64 ไม่ได้ใช้การแบ่งส่วนในโหมดยาว (โหมด 64 บิต)

การลงทะเบียนสี่ส่วน: CS, SS, DS และ ES ถูกบังคับให้เป็น 0 และ จำกัด ไว้ที่ 2 ^ 64

https://en.wikipedia.org/wiki/X86_memory_segmentation#Later_developments

ระบบปฏิบัติการไม่สามารถ จำกัด ช่วงของ "ที่อยู่เชิงเส้น" ได้อีกต่อไป ดังนั้นจึงไม่สามารถใช้การแบ่งส่วนเพื่อป้องกันหน่วยความจำ มันจะต้องพึ่งพาทั้งหมดในการเพจ

ไม่ต้องกังวลกับรายละเอียดของ x86 CPUs ซึ่งจะใช้เฉพาะเมื่อทำงานในโหมด 32 บิตดั้งเดิม Linux สำหรับโหมด 32 บิตไม่ได้ใช้งานมาก มันอาจได้รับการพิจารณาว่า "อยู่ในสถานะของการละเลยอย่างอ่อนโยนเป็นเวลาหลายปี" ดูการสนับสนุน 32-bit x86 ใน Fedora [LWN.net, 2017]

(มันเกิดขึ้นที่ Linux แบบ 32 บิตไม่ได้ใช้การแบ่งกลุ่มเช่นกัน แต่คุณไม่จำเป็นต้องเชื่อใจฉันในเรื่องนี้คุณเพียงแค่เพิกเฉย :-)


นั่นเป็นบิตของการคุยโว base / limit ได้รับการแก้ไขที่ 0 / -1 ในโหมด long สำหรับกลุ่มดั้งเดิม -8086 ดั้งเดิม (CS / DS / ES / SS) แต่ FS และ GS ยังคงมีเซ็กเมนต์ตามอำเภอใจ และตัวบอกเซ็กเมนต์โหลดลงใน CS จะกำหนดว่า CPU ทำงานในโหมด 32 หรือ 64 บิตหรือไม่ และพื้นที่ผู้ใช้บน x86-64 Linux ใช้ FS สำหรับหน่วยเก็บข้อมูลเธรดโลคัล ( mov eax, [fs:rdi + 16]) เคอร์เนลใช้ GS (หลังswapgs) เพื่อค้นหาเคอร์เนลของกระบวนการปัจจุบันในsyscallจุดเริ่มต้น แต่ใช่การแบ่งส่วนไม่ได้ใช้เป็นส่วนหนึ่งของกลไกการจัดการหน่วยความจำ OS หลัก / กลไกการป้องกันหน่วยความจำ
Peter Cordes

นี่เป็นสิ่งที่คำพูดในคำถามมีความหมายโดย "รุ่น 2.6 ของ Linux ใช้การแบ่งส่วนเมื่อจำเป็นโดยสถาปัตยกรรม 80x86 เท่านั้น" แต่ย่อหน้าที่ 2 ของคุณนั้นผิด Linux ใช้การแบ่งกลุ่มโดยทั่วไปในโหมด 32 และ 64 บิต ie base = 0 / limit = 2 ^ 32 หรือ 2 ^ 64 สำหรับส่วนคลาสสิก (CS / DS / ES / SS) ที่ใช้โดยนัยตามคำแนะนำปกติ ไม่มีอะไร "พิเศษ" ที่ต้องกังวลเกี่ยวกับในรหัส Linux แบบ 32 บิต; ฟังก์ชั่น HW มี แต่ไม่ได้ใช้
Peter Cordes

@PeterCordes โดยทั่วไปคุณจะตีความคำตอบที่ผิด :-) ดังนั้นฉันจึงแก้ไขให้พยายามและทำให้ข้อโต้แย้งของฉันไม่ชัดเจน
sourcejedi

การปรับปรุงที่ดีตอนนี้ก็ไม่ได้ทำให้เข้าใจผิด ฉันเห็นด้วยกับจุดที่แท้จริงของคุณซึ่งคุณสามารถทำได้และควรเพิกเฉยต่อการแบ่งกลุ่ม x86 โดยสิ้นเชิงเพราะมันใช้สำหรับการจัดการระบบ osdev และ TLS เท่านั้น หากคุณต้องการเรียนรู้เกี่ยวกับมันในที่สุดมันจะง่ายกว่าที่จะเข้าใจหลังจากคุณเข้าใจ x86 asm ด้วยโมเดลหน่วยความจำแบบแบนแล้ว
Peter Cordes

8

เนื่องจาก x86 มีเซ็กเมนต์จึงไม่สามารถใช้งานได้ แต่ที่อยู่พื้นฐานทั้งสองcs(ส่วนรหัส) และds(ส่วนข้อมูล) ถูกตั้งค่าเป็นศูนย์ดังนั้นการแบ่งส่วนจะไม่ได้ใช้จริง ข้อยกเว้นคือเธรดข้อมูลโลคัลหนึ่งในเซ็กเมนต์ที่ไม่ได้ใช้ตามปกติจะชี้ไปยังเธรดข้อมูลโลคัล แต่นั่นก็เพื่อหลีกเลี่ยงการจองหนึ่งในวัตถุประสงค์ทั่วไปที่ลงทะเบียนสำหรับงานนี้

ไม่ได้บอกว่า Linux ไม่ได้ใช้การแบ่งกลุ่มใน x86 เพราะจะเป็นไปไม่ได้ คุณรู้อยู่แล้วไฮไลต์ส่วนหนึ่งLinux ใช้การแบ่งส่วนในทางที่ ส่วนที่สองคือLinux ใช้การแบ่งส่วนเมื่อจำเป็นโดยสถาปัตยกรรม 80x86 เท่านั้น

คุณได้อ้างเหตุผลแล้วการเพจนั้นง่ายกว่าและพกพาได้มากกว่า


7

แต่ละภูมิภาคในแผนภาพเป็นเซ็กเมนต์หรือไม่

เลขที่

ในขณะที่ระบบแบ่งส่วน (ในโหมดป้องกัน 32- บิตบน x86) ได้รับการออกแบบมาเพื่อรองรับรหัสข้อมูลและสแต็กเซ็กเมนต์แยกในทางปฏิบัติทุกเซกเมนต์ถูกตั้งค่าเป็นพื้นที่หน่วยความจำเดียวกัน นั่นคือพวกเขาเริ่มต้นที่ 0 และสิ้นสุดในตอนท้ายของหน่วยความจำ(*) นั่นทำให้ที่อยู่เชิงตรรกะและที่อยู่เชิงเส้นเท่ากัน

สิ่งนี้เรียกว่าแบบจำลองหน่วยความจำแบบ "แบน" และค่อนข้างง่ายกว่าแบบจำลองที่คุณมีเซกเมนต์ที่แตกต่างกัน โดยเฉพาะอย่างยิ่งโมเดลที่แบ่งกลุ่มจะต้องมีพอยน์เตอร์ที่ยาวกว่าเนื่องจากตัวเลือกกลุ่มจะต้องรวมอยู่นอกเหนือไปจากตัวชี้ออฟเซ็ต (ตัวเลือกเซกเมนต์ 16 บิต + ออฟเซ็ต 32 บิตสำหรับตัวชี้ทั้งหมด 48 บิตเทียบกับตัวชี้แบบแบน 32 บิต)

โหมดยาว 64 บิตไม่รองรับการแบ่งส่วนอื่น ๆ นอกเหนือจากรุ่นหน่วยความจำแบบแบน

หากคุณต้องการโปรแกรมในโหมดป้องกัน 16 บิตใน 286 คุณจะต้องมีเซกเมนต์มากขึ้นเนื่องจากพื้นที่ที่อยู่คือ 24 บิต แต่ตัวชี้มีเพียง 16 บิต

(* โปรดทราบว่าฉันจำไม่ได้ว่า Linux 32 บิตจัดการกับการแยกเคอร์เนล / ผู้ใช้อย่างไรการแบ่งส่วนจะช่วยให้ผ่านการตั้งค่าการ จำกัด กลุ่มผู้ใช้เพื่อให้พวกเขาไม่รวมพื้นที่เคอร์เนล ระดับการป้องกันต่อหน้า)

ถ้าอย่างนั้นทำไมมันถึงบอกว่าลีนุกซ์ไม่ได้ใช้การแบ่งกลุ่ม แต่เป็นเพจจิ้งเท่านั้น?

x86 ยังคงมีกลุ่มและคุณไม่สามารถปิดการใช้งานได้ พวกมันใช้น้อยที่สุด ในโหมดการป้องกันแบบ 32 บิตกลุ่มจะต้องมีการตั้งค่าสำหรับรุ่นแบนและแม้กระทั่งในโหมด 64 บิตที่ยังคงมีอยู่


อืมฉันเดาว่าเคอร์เนลแบบ 32 บิตอาจช่วยลด Meltdown ได้ราคาถูกกว่าการเปลี่ยนตารางหน้าโดยการตั้งค่าการ จำกัด เซ็กเมนต์ใน CS / DS / ES / SS ที่ป้องกันผู้ใช้พื้นที่จากการเข้าถึง 2G หรือ 3G เหนือ (Meltdown vuln เป็นวิธีแก้ปัญหาสำหรับเคอร์เนล / ผู้ใช้บิตในรายการหน้าตารางทำให้ผู้ใช้พื้นที่ในการอ่านจากหน้าเว็บที่แมปเคอร์เนลเท่านั้น) หน้า VDSO อาจถูกแมปที่ด้านบนของ 4G แม้ว่า: / wrfsbaseเป็นสิ่งผิดกฎหมายในโหมดป้องกัน / เข้ากันได้เฉพาะโหมดยาวดังนั้นในพื้นที่ผู้ใช้เคอร์เนล 32 บิตไม่สามารถตั้งค่าฐาน FS สูงได้
Peter Cordes

บนเคอร์เนล 64 บิตพื้นที่ผู้ใช้ 32 บิตอาจข้ามไปยังเซ็กเมนต์รหัส 64 บิตได้ดังนั้นคุณจึงไม่สามารถขึ้นอยู่กับข้อ จำกัด ของเซ็กเมนต์สำหรับการป้องกัน Meltdown อาจเป็นเพียงเคอร์เนล 32 บิตบริสุทธิ์ (ซึ่งมีข้อเสียอย่างมากในเครื่องที่มี RAM จริงเช่นจำนวนหน่วยความจำที่เหลือน้อยสำหรับเธรด) อย่างไรก็ตามใช่ Linux ปกป้องหน่วยความจำเคอร์เนลด้วยการเพจทิ้งฐาน / จำกัด = 0 / -1 ในพื้นที่ผู้ใช้ปกติ เซกเมนต์ (ไม่ใช่ FS / GS ซึ่งใช้สำหรับการจัดเก็บภายในเธรด)
Peter Cordes

ก่อนที่จะรองรับ NX บิตในหน้าฮาร์ดแวร์ตาราง (PAE) บางโปรแกรมแก้ไขด้านความปลอดภัยก่อนหน้านี้ใช้การแบ่งส่วนเพื่อสร้างสแต็กที่ไม่สามารถเรียกใช้งานได้สำหรับรหัสพื้นที่ผู้ใช้ เช่นlinux.com/news/exec-shield-new-linux-security-feature (โพสต์ของ Ingo Molnar กล่าวถึง "Solar Designer ยอดเยี่ยม" non-exec stack stack "".)
Peter Cordes

3

Linux x86 / 32 ไม่ได้ใช้การแบ่งกลุ่มในแง่ที่ว่าจะเริ่มต้นทุกส่วนให้ตรงกับที่อยู่เชิงเส้นและขีด จำกัด เดียวกัน สถาปัตยกรรม x86 ต้องการให้โปรแกรมมีเซกเมนต์: โค้ดสามารถเรียกใช้จากเซ็กเมนต์โค้ดเท่านั้นสแต็กสามารถอยู่ในสแต็กเซกเมนต์เท่านั้นข้อมูลสามารถจัดการได้ในหนึ่งในเซกเมนต์ข้อมูลเท่านั้น Linux ข้ามกลไกนี้โดยการตั้งค่ากลุ่มทั้งหมดในลักษณะเดียวกัน (ยกเว้นข้อยกเว้นที่หนังสือของคุณไม่ได้กล่าวถึง) เพื่อให้ที่อยู่แบบลอจิคัลเดียวกันนั้นใช้ได้ในทุกส่วน นี่เป็นความจริงที่เทียบเท่ากับการไม่มีเซ็กเมนต์เลย


2

แต่ละภูมิภาคในแผนภาพเป็นเซ็กเมนต์หรือไม่

นี่คือการใช้คำว่า "เซ็กเมนต์" เกือบทั้งหมดโดยสิ้นเชิง

  • 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 บิตคือ:

  1. เซกเมนต์: ออฟเซ็ต (ฐานเซกเมนต์แสดงถึงการลงทะเบียนที่ถือออฟเซ็ตหรือแทนที่ด้วยคำนำหน้าคำสั่ง)
  2. ที่อยู่เสมือนเชิงเส้น 32 หรือ 64 บิต = เบส + ออฟเซ็ต (ในรูปแบบหน่วยความจำแบบแบนเช่น Linux ใช้ตัวชี้ / offsets = ที่อยู่เชิงเส้นด้วยเช่นกันยกเว้นเมื่อเข้าถึง TLS ที่เกี่ยวข้องกับ FS หรือ GS)
  3. ตารางหน้า (ที่แคชโดย 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ให้สามารถใช้งานได้ ดูลิงก์ในคำตอบนี้สำหรับรายละเอียด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.