บทที่ 2 · Memory Management ออก

ชีท memory_mng(3_1) หน้า 70–81 (12 หน้า) — อาจารย์เปิดในคลิปนาที 6:10–17:00 · ถ้ายังไม่คล่องเรื่อง address / process / RR ให้อ่าน บทที่ 0 ก่อน

บทนี้เรียนเรื่องอะไร (ภาพรวม)

โปรแกรมจะรันได้ต้องอยู่ใน RAM แต่ RAM มีก้อนเดียว และต้องใส่หลายโปรแกรมพร้อมกัน OS จึงต้องตอบ 3 คำถาม:

  1. โปรแกรมอ้างข้อมูลด้วยเลข address แบบไหน แล้วแปลงเป็นตำแหน่งจริงใน RAM อย่างไร → 2.1 address binding, 2.2 logical vs physical
  2. จะวางโปรแกรมไว้ตรงไหนของ RAM ถ้าที่ไม่พอทำยังไง → 2.3–2.7 (loading/linking, overlay, swapping, partition, fit)
  3. พอวางไปเรื่อยๆ แล้วที่ว่างแตกเป็นชิ้นเล็กๆ จะแก้ยังไง → 2.8 compaction และคำตอบที่ดีที่สุดคือ 2.9 paging

2.1 Address binding (การผูก address) ไม่เน้น

อาจารย์บอกว่า "อันนี้จะไม่เน้น" ให้อ่านแค่พอเข้าใจ แล้วจำตาราง 3 จังหวะด้านล่างก็พอ เพราะเป็นพื้นฐานของหัวข้อ 2.2

🤔 คืออะไร

ตอนเราเขียนโปรแกรม เราอ้างข้อมูลด้วยชื่อ เช่น ตัวแปร x หรือฟังก์ชัน sum() (เรียกว่า symbolic address) แต่ CPU ไม่รู้จักชื่อ CPU รู้จักแค่เลขช่องใน RAM (physical address)
Address binding = ขั้นตอนที่ตัดสินใจว่า "ชื่อนี้จะไปอยู่ที่ address เลขอะไร"

❓ ทำไมต้องสนใจ

เพราะตอนเขียนโปรแกรม เราไม่รู้ว่าโปรแกรมจะถูกวางไว้ตรงไหนของ RAM (ขึ้นอยู่กับว่าตอนนั้นมีที่ว่างตรงไหน) จึงต้องเลือกว่าจะ "กำหนดเลขจริง" ในจังหวะไหน ยิ่งกำหนดช้า ยิ่งยืดหยุ่น

⚙️ เส้นทางของโปรแกรม ตั้งแต่เขียนจนรัน (รูป 3.1.A)

Source program (.c)→ compiler/assembler →Object module (.obj)→ linkage editor (linker) + object อื่น →Load module (.exe)→ loader + system library →อยู่ใน RAM พร้อมรัน
  • Compiler = แปลภาษาโปรแกรมเป็นภาษาเครื่อง ได้ object module (ยังไม่สมบูรณ์ เพราะยังเรียกฟังก์ชันของไฟล์อื่นไม่ได้)
  • Linkage editor / linker = เอา object หลายไฟล์ + library มารวมเป็นไฟล์เดียวที่รันได้ (load module)
  • Loader = โหลด load module จากดิสก์เข้า RAM
  • ช่วงซ้ายเรียก compile time ช่วง linker/loader เรียก load time และตอนรันเรียก execution time (run time)

🔍 3 จังหวะของ binding — ใช้ตัวอย่างเดียวกัน: ตัวแปร x อยู่ห่างจากต้นโปรแกรม 200 ช่อง

Binding ตอนเกิดอะไรขึ้นกับ xได้ address แบบผลที่ตามมา
Compile timecompiler รู้ล่วงหน้าว่าโปรแกรมจะเริ่มที่ 1000 จึงเขียนเลขจริงลงไปเลยว่า x = 1200absolute address (fixed)ต้องโหลดลงที่ 1000 ทุกครั้ง ถ้าที่นั้นไม่ว่าง ต้อง compile ใหม่
Load timecompiler เขียนไว้แค่ "x = ต้นโปรแกรม + 200" พอ loader รู้ว่าวางที่ 5000 ก็แก้เป็น 5200relocatable address (not fixed)วางตรงไหนก็ได้ตอนโหลด แต่โหลดแล้วย้ายไม่ได้
Execution timeในโปรแกรมยังเป็น x = 200 (logical) ทุกครั้งที่ CPU อ้าง ฮาร์ดแวร์จะบวก base register ให้ เช่น base = 5000 ได้ 5200logical ≠ physicalย้ายโปรแกรมได้แม้กำลังรัน (แค่เปลี่ยนค่า base) ต้องมีฮาร์ดแวร์ช่วย (MMU)

ระบบปัจจุบันใช้แบบ execution time เกือบทั้งหมด เพราะยืดหยุ่นที่สุด และเป็นพื้นฐานของ swapping, paging, virtual memory ในหัวข้อต่อๆ ไป

🗣 อาจารย์พูดตอนเปิดหน้านี้อาจารย์บอกว่า อันนี้จะไม่เน้น แล้วไปพูดเรื่อง dynamic linking กับ static linking ต่อ
เกทไหม? ลองตอบ
binding แบบไหนที่ "ย้ายโปรแกรมไปที่อื่นได้แม้กำลังรันอยู่"
execution time เก็บ address แบบ logical ไว้ แล้วให้ฮาร์ดแวร์บวก base ทุกครั้งที่อ้าง ถ้าจะย้ายก็แค่เปลี่ยนค่า base
binding ตอน compile time ได้ address แบบใด
compiler เขียนเลขจริงลงไปเลย จึงเป็น absolute ต้องลงที่เดิมทุกครั้ง

2.2 Logical address vs physical address ★

🤔 คืออะไร

  • Logical address = เลข address ที่โปรแกรม/CPUคิดว่าตัวเองใช้ ทุกโปรแกรมนับจาก 0 ของตัวเอง (เช่น x อยู่ที่ 346)
  • Physical address = ตำแหน่งจริงใน RAM ที่ข้อมูลนั้นอยู่
  • MMU (Memory Management Unit) = ฮาร์ดแวร์ที่แปลง logical → physical ให้อัตโนมัติทุกครั้งที่ CPU อ้าง

❓ ทำไมต้องแยกสองแบบ

  1. ย้ายโปรแกรมได้: โปรแกรมไม่ต้องรู้ว่าตัวเองอยู่ตรงไหนจริง
  2. ป้องกันกัน: แต่ละโปรแกรมเห็นแค่ช่วงของตัวเอง อ้างข้ามไปช่วงของโปรแกรมอื่นไม่ได้

⚙️ ทำงานยังไง (relocation register + limit register, รูป 3.1.E และ 3.1.H)

  1. OS วางโปรแกรมไว้ที่ตำแหน่งเริ่ม เช่น 14000 แล้วตั้ง base (relocation) register = 14000 และตั้ง limit register = ขนาดโปรแกรม
  2. CPU อ้าง logical address เช่น 346
  3. ฮาร์ดแวร์ตรวจ 346 < limit ? ถ้าไม่ใช่ → trap: addressing error (กันอ้างเกินช่วงตัวเอง)
  4. ถ้าผ่าน → physical = base + logical = 14000 + 346 = 14346 แล้วไปอ่าน RAM ช่องนั้น

💡 เทียบให้เห็นภาพ: หอพัก

โปรแกรมพูดว่า "ห้องที่ 346 ของฉัน" (logical) แต่ห้องแรกของมันอยู่ที่ห้องเลข 14000 ของตึก ห้องจริงจึงเป็น 14346 (physical) ส่วน limit คือ "เช่าไว้กี่ห้อง" ถ้าขอห้องเกินที่เช่า จะโดนไล่ (trap)

🗣 อาจารย์พูดสิ่งที่ควรรู้คือสองคำนี้ต้องรู้ logical address กับ physical address ต่างกันอย่างไร อันหนึ่งเป็นเลขที่ CPU อ้างถึง … สมมติ 346 เขาต้องมี relocation register คือ transform ไปเป็นตำแหน่งจริงใน memory
📝 เขียนตอบแบบนี้
Logical address คือ address ที่ CPU/โปรแกรมสร้างขึ้น (มองจากมุมโปรเซส เริ่มที่ 0)
Physical address คือตำแหน่งจริงในหน่วยความจำ
MMU แปลงโดย physical = relocation(base) register + logical
เช่น base = 14000, logical = 346 → physical = 14346
และต้องตรวจ logical < limit ก่อน ไม่งั้นเกิด trap (addressing error)
เกทไหม? ลองตอบ
base = 8000, limit = 500 แล้ว CPU อ้าง logical 100 จะได้ physical เท่าไร
100 < 500 ✓ → 8000 + 100 = 8100
base = 8000, limit = 500 แล้ว CPU อ้าง logical 600 จะเกิดอะไร
600 ไม่น้อยกว่า limit 500 จึงเกิด trap ก่อน (ไม่เอาไปบวก base)

2.3 Dynamic loading / static linking / dynamic linking

🤔 Dynamic loading คืออะไร

แทนที่จะโหลดทั้งโปรแกรมเข้า RAM ตั้งแต่แรก ให้โหลด main program ก่อน ส่วน subroutine (ฟังก์ชันย่อย) ค่อยโหลดตอนที่ถูกเรียกจริง ฟังก์ชันที่ไม่เคยถูกเรียก (เช่น โค้ดจัดการ error ที่นานๆ ใช้ที) ก็ไม่ต้องกินที่ใน RAM
เหมือนเกมที่โหลดด่านเฉพาะตอนเราเข้าไปเล่น ไม่ได้โหลดทุกด่านตั้งแต่เปิดเกม

🤔 Linking คืออะไร

โปรแกรมของเรามักเรียกใช้ฟังก์ชันจาก library (ชุดฟังก์ชันสำเร็จรูป เช่น printf) การ "link" คือการเชื่อมการเรียกนั้นเข้ากับโค้ดจริงของ library ซึ่งทำได้ 2 แบบ:

Static linkingDynamic linking
ทำตอนไหนตอน link (ก่อนรัน)ตอนรัน เมื่อฟังก์ชันถูกเรียกครั้งแรก
ทำงานยังไงคัดลอกโค้ดของ library ใส่ลงในไฟล์โปรแกรมเลยในโปรแกรมมีแค่ stub (ป้ายชี้) ตอนรันจึงไปหา library ที่ติดตั้งไว้ในเครื่อง
ขนาดไฟล์ใหญ่เล็ก
ใช้ร่วมกันแต่ละโปรแกรมมีสำเนาของตัวเองหลายโปรแกรมใช้ library ชุดเดียวกันใน RAM ได้ (shared library เช่น .dll บน Windows, .so บน Linux)
อัปเดต libraryต้อง link โปรแกรมใหม่แค่เปลี่ยนไฟล์ library ทุกโปรแกรมได้ของใหม่

เทียบง่ายๆ: static = ถ่ายเอกสารบทความใส่รายงานของเราเลย ส่วน dynamic = เขียนอ้างอิงว่า "ดูที่ห้องสมุดเล่มนี้"

⚠️ ระวัง: นิยามในชีทกับตำราไม่ตรงกันชีท 3.1.3 เขียนว่า "dynamic linking … ระบบจะนำ subroutine มาลิงก์เข้ากับตัวโปรแกรม ซึ่งโปรแกรมจะมีขนาดใหญ่เพิ่มขึ้น" ซึ่งตรงกับลักษณะของ static linking ในตำรา ตอนสอบให้อธิบายเทียบทั้งสองแบบตามตารางด้านบน จะปลอดภัยที่สุด
🗣 อาจารย์พูด"Dynamic linking and static linking"
เกทไหม? ลองตอบ
ข้อใดเป็นข้อดีของ dynamic linking
dynamic linking เก็บแค่ stub ไว้ในโปรแกรม แล้วไปใช้ shared library ตอนรัน

2.4 Overlay และ swapping ★

🤔 Overlay คืออะไร

สมัยที่ RAM เล็กมาก ถ้าโปรแกรมใหญ่กว่า RAM จะใช้วิธีแบ่งโปรแกรมเป็นส่วนที่ไม่ต้องใช้พร้อมกัน แล้วให้ผลัดกันใช้พื้นที่เดียวกัน โดยมีโปรแกรมเล็กๆ ชื่อ overlay driver คอยโหลดส่วนถัดไปทับส่วนเดิม (ผู้เขียนโปรแกรมต้องออกแบบเองว่าส่วนไหนทับกันได้ จึงยุ่งยาก)

⚙️ ตัวอย่างในชีท: two-pass assembler (รูป 3.1.B)

assembler ทำงาน 2 รอบ คือ Pass 1 (70K) แล้วจึง Pass 2 (80K) ไม่ต้องใช้พร้อมกัน ส่วนที่ต้องอยู่ตลอดคือ symbol table 30K, common routine 20K และ overlay driver 10K

ไม่ใช้ overlay = 70 + 80 + 30 + 20            = 200K
ใช้ overlay    = 30 + 20 + 10 + max(70, 80)    = 140K
ขั้นตอน: โหลด Pass1 ลงพื้นที่ overlay → ทำ pass 1 เสร็จ → overlay driver โหลด Pass2 ทับที่เดิม → ทำ pass 2

🤔 Swapping คืออะไร

ถ้า RAM ใส่ทุก process พร้อมกันไม่ได้ OS จะย้ายบาง process ออกไปพักไว้บนดิสก์ชั่วคราว (swap out) แล้วเอาอีกตัวเข้ามาแทน (swap in) พื้นที่บนดิสก์ที่ใช้พักเรียกว่า backing store

⚙️ ทำงานยังไง (ตัวอย่าง RR)

  1. P1 กำลังรัน แล้ว time quantum หมด
  2. OS save PCB ของ P1 แล้วคัดลอกข้อมูลของ P1 ใน RAM ไปเก็บที่ backing store (swap out)
  3. OS คัดลอก P2 จาก backing store เข้า RAM (swap in) แล้ว load PCB ของ P2
  4. P2 ทำงานต่อจากที่ค้างไว้ และเมื่อถึงคิวของ P1 ก็ swap กลับเข้ามาใหม่

ข้อเสีย: ช้า เพราะต้องคัดลอกข้อมูลทั้ง process ผ่านดิสก์ (โปรเซสยิ่งใหญ่ยิ่งช้า)

🗣 อาจารย์พูดสมมติว่ามันมีขนาด block memory ตรงนี้เหลืออยู่ 80 kilobytes … process สองอันยัดเข้าไปพร้อมกันไม่ได้ ก็ต้องมีการสลับเข้าสลับออก … save process control block, load process control block ก็จะถามนิดหน่อย
เกทไหม? ลองตอบ
process ที่ถูก swap out ไปเก็บอยู่ที่ไหน
swap out = คัดลอกไปเก็บที่ backing store บนดิสก์ แล้วกลับมาทำต่อได้ เพราะสถานะถูก save ไว้ใน PCB

2.5 Single partition: low memory / high memory ★

🤔 คืออะไร

Partition = ช่วงของหน่วยความจำที่แบ่งไว้ใช้งาน ส่วน single-partition allocation = RAM แบ่งเป็นแค่ 2 ส่วน คือส่วนของ OS กับส่วนของ user และมี user process ได้ครั้งละ 1 ตัว (แบบคอมพิวเตอร์ยุคแรก)

⚙️ วาง user ได้ 2 แบบ (รูป 3.1.D, memory 0–512K)

Low memory                 High memory
0    [   OS    ]           0    [   OS    ]
     [  User   ]  ← ต่อจาก OS     [  ว่าง   ]
     [  ว่าง   ]                 [  ว่าง   ]
512K [  ว่าง   ]           512K [  User   ]  ← ไปอยู่ท้ายสุด
  • Low memory: OS อยู่ที่ address 0 แล้ว user ต่อจาก OS ทันที ที่ว่างอยู่ท้าย
  • High memory: OS อยู่ที่ 0 แต่ user ไปอยู่ท้ายสุด (address สูง) ที่ว่างอยู่ตรงกลาง
  • ระบบใช้ base register (execution-time binding) เพื่อย้ายตำแหน่ง user ได้ (รูป 3.1.E)
  • การเว้นที่ว่างไว้ตรงกลางมีประโยชน์ เพราะ stack (โตจากบนลงล่าง) กับ heap (โตจากล่างขึ้นบน) จะได้ขยายเข้าหากันได้
⚠️ อาจารย์เตือนอย่าดูแค่ตำแหน่งบน/ล่างในรูป ให้ดูเลข address เพราะบางรูปเขียน 0 ไว้ด้านล่าง
🗣 อาจารย์พูดคำว่า low และ high address บางทีมันจะเขียน low อยู่ข้างเลข … เราต้องดูเลขนะ บางทีเลขส่วนอยู่ข้างล่าง … low memory OS จะอยู่ที่เลข 0 … หรือบางทีเขาออกแบบมาเป็นแบบให้พื้นที่ว่างตรงกลางก็ได้ ก็จะเกี่ยวข้องกับเรื่อง stack กับ heap
เกทไหม? ลองตอบ
แบบ high memory ส่วนของ user process อยู่ตรงไหน
high memory: OS อยู่ที่ 0 ส่วน user ไปอยู่ท้ายสุด ที่ว่างจึงอยู่ตรงกลาง

2.6 Multiple partition + RR (ตัวอย่าง 3.1.ก)

🤔 คืออะไร

Multiple-partition allocation = ใส่หลาย processใน RAM พร้อมกัน โดยให้แต่ละ process ได้ช่วงที่ติดกันเป็นก้อนเดียว (contiguous) ช่วงที่ยังว่างเรียกว่า hole

⚙️ ทำงานยังไง

  1. OS จดรายการ hole ที่ว่างไว้
  2. process ใหม่มา → หา hole ที่ใหญ่พอ แล้ววางลงไป (เลือก hole ตามวิธี first/best/worst fit ในหัวข้อ 2.7) ส่วนที่เหลือของ hole ยังเป็น hole เล็กลง
  3. ถ้าไม่มี hole ไหนพอ → process นั้นต้องรอใน job queue
  4. process ทำงานจบ → คืนพื้นที่เป็น hole ถ้าติดกับ hole อื่นก็รวมเป็นก้อนเดียว แล้วลองวาง process ที่รออยู่

🔍 ตัวอย่างในชีท

หน่วยความจำ 2560K, OS ใช้ 400K (เหลือ 2160K), low memory, CPU ใช้ RR quantum = 1 ms, job queue: P1 600K/10ms, P2 1000K/5ms, P3 300K/20ms, P4 700K/8ms, P5 500K/15ms

  1. เริ่ม: ใส่ P1 (400–1000K), P2 (1000–2000K), P3 (2000–2300K) เหลือ hole 260K → P4 (700K) ใส่ไม่ได้ ต้องรอ และ P5 ข้ามคิว P4 ไม่ได้ (FCFS)
  2. RR วน P1, P2, P3 ทีละ 1 ms → P2 ได้รันที่เวลา 1, 4, 7, 10, 13 ครบ 5 ms → P2 จบที่ 14
  3. hole 1000–2000K ว่าง → P4 ลง 1000–1700K (เหลือ hole 300K)
  4. ทำต่อแบบเดียวกัน → P1 จบที่ 28 → P5 ลง 400–900K → P4 จบ 38, P3 จบ 53, P5 จบ 58

เลื่อนดูหน่วยความจำเปลี่ยนทีละเหตุการณ์:

เกทไหม? ลองตอบ
ในตัวอย่าง 3.1.ก ทำไม P4 (700K) จึงต้องรอตั้งแต่เริ่ม
2160K − (600 + 1000 + 300) = 260K < 700K จึงต้องรอจน P2 จบที่เวลา 14 แล้วค่อยลง 1000–1700K

2.7 Placement strategy: first / best / worst fit ★

🤔 คืออะไร

เมื่อมีหลาย hole ที่ใหญ่พอ จะเลือก hole ไหน? placement strategy คือกติกาการเลือก

  • First-fit: ไล่จากต้น (address ต่ำ) เจอ hole แรกที่พอ ก็ใส่เลย
  • Best-fit: เลือก hole ที่เล็กที่สุดที่ยังพอ (เหลือเศษน้อยสุด) ต้องดูทุก hole ก่อน
  • Worst-fit: เลือก hole ที่ใหญ่ที่สุด (เพื่อให้เศษที่เหลือใหญ่พอใช้ต่อ) ต้องดูทุก hole ก่อน

⚙️ วิธีทำโจทย์ (hole 100, 500, 200, 300, 600 / process 212, 417, 112, 426)

  1. เขียน hole เรียงตาม address
  2. รับ process ทีละตัวตามลำดับ แล้วเลือก hole ตามกติกา
  3. ลบขนาด process ออกจาก hole นั้น (เศษที่เหลือยังเป็น hole ให้ตัวถัดไปใช้ได้)
  4. ทำจนครบ ถ้าตัวไหนไม่มี hole พอ ให้เขียนว่า "ต้องรอ"

เช่น first-fit: 212 → hole 500 (เหลือ 288) · 417 → hole 600 (เหลือ 183) · 112 → hole 288 (เหลือ 176) · 426 → ไม่มีที่ ต้องรอ

วิธีข้อดีข้อเสีย
First-fitเร็วที่สุด หยุดค้นเมื่อเจอ hole แรกที่พอเศษเล็กๆ กองอยู่ช่วงต้นหน่วยความจำ
Best-fitเหลือเศษน้อยสุด เก็บ hole ใหญ่ไว้ให้โปรเซสใหญ่ต้องค้นทุก hole (ยกเว้นมี list เรียงขนาด) และได้เศษเล็กมากที่ใช้ต่อไม่ได้
Worst-fitเศษที่เหลือใหญ่ ยังพอใช้ต่อได้ต้องค้นทุก hole, hole ใหญ่หมดเร็ว โปรเซสใหญ่ที่มาทีหลังอาจไม่มีที่ลง
🗣 อาจารย์พูดfirst fit … มีข้อดีข้อเสียยังไง ไปทบทวนครับ สามอย่างเนี่ย
Q1hole = 100, 500, 200, 300, 600 และโปรเซส 212, 417, 112, 426 วิธีใดวางได้ครบทุกโปรเซส
Best-fit: 212→300, 417→500, 112→200, 426→600 ครบทุกตัว ส่วน first-fit กับ worst-fit ทำให้ 426K ต้องรอ (ลองกดในเครื่องมือด้านบนดูได้)

2.8 Fragmentation และ compaction ★

🤔 Fragmentation คืออะไร

  • External fragmentation = ที่ว่างรวมกันพอ แต่แตกเป็นชิ้นเล็กๆ ไม่ติดกัน จึงใส่ process ใหญ่ไม่ได้ (อาจารย์เรียกว่า "bubble")
    เช่น ว่าง 100K + 300K + 260K = 660K แต่ process ขนาด 500K ใส่ไม่ได้ เพราะไม่มีช่องไหนติดกันถึง 500K
  • Internal fragmentation = ได้พื้นที่เกินที่ขอ แล้วส่วนเกินถูกทิ้งไว้ในก้อนนั้น (เกิดกับ paging: ขอ 4.5 page ก็ต้องได้ 5 page)

🤔 Compaction คืออะไร และทำงานยังไง

Compaction = ย้าย process ทั้งหลายให้ไปชิดกัน เพื่อให้ hole ทั้งหมดมารวมเป็นก้อนใหญ่ก้อนเดียว (ต้องใช้ execution-time binding จึงย้ายได้)

ก่อน (รูป 3.1.I)                   หลัง compaction
0     OS                           0     OS
400K  P5                           400K  P5
900K  ว่าง 100K                    900K  P4
1000K P4                           1600K P3
1700K ว่าง 300K                    1900K ว่าง 660K  ← รวมเป็นก้อนเดียว
2000K P3
2300K ว่าง 260K
2560K                              2560K

❌ ข้อเสีย

ช้ามาก เพราะต้อง copy ข้อมูลของทุก process ที่ย้าย เวลาโตตามปริมาณที่ต้อง copy (อาจารย์พูดถึง O(N×K)) ระหว่างนั้นระบบทำงานอื่นไม่ได้ จึงควรย้ายให้น้อยที่สุด เช่น รูป 3.1.J มี 3 วิธีที่ย้าย 600K, 400K และ 200K (ดีสุด: ย้ายแค่ P3) สุดท้ายจึงมีแนวคิดใหม่คือ paging ที่ไม่ต้องหาที่ว่างติดกันเลย

🗣 อาจารย์พูดการแก้ปัญหาว่าบับโบล (bubble) หรือ fragment ใน memory จะแก้ยังไง วิธีแรกคือการเลื่อนให้มันมีพื้นที่ใหญ่กองรวมกัน ซึ่งมันก็จะกินเวลา … big O ของ N คูณกับ K เวลาที่ใช้ในการ copy … มันก็เลยต้องมีการแบ่งเป็น page
Contiguous allocation (2.5–2.8)
✅ ง่าย แปลง address แค่บวก base
❌ เกิด external fragmentation รันได้ไม่กี่โปรเซส และต้อง swap หรือ compaction
Paging (2.9)
✅ ลงเฟรมไหนก็ได้ ไม่ต้องติดกัน จึงไม่มี external fragmentation
❌ มี internal fragmentation (เพจสุดท้ายใช้ไม่เต็ม) และต้องมี page table
เกทไหม? ลองตอบ
ที่ว่างรวม 660K แต่แยกเป็น 100K, 300K, 260K ทำให้ใส่ process 500K ไม่ได้ เรียกว่าอะไร
ที่ว่างอยู่นอก process และไม่ติดกัน = external

2.9 Paging ★

🤔 คืออะไร

Paging = ตัด logical memory ของโปรแกรมเป็นชิ้นขนาดเท่ากันเรียกว่า page และตัด RAM เป็นช่องขนาดเท่ากันเรียกว่า frame (page กับ frame ขนาดเท่ากัน) แล้วเอา page ไปใส่ frame ว่างช่องไหนก็ได้ ไม่ต้องติดกัน

❓ ทำไมต้องมี

เพื่อแก้ external fragmentation: ไม่ต้องหาที่ว่างติดกันก้อนใหญ่ ขอแค่มี frame ว่างพอก็ใส่ได้ และไม่ต้องทำ compaction

⚙️ แปลง address ยังไง (รูป 3.1.K)

เพราะ page ไปอยู่ frame ไหนก็ได้ OS จึงต้องจดไว้ใน page table ว่า "page ที่ p อยู่ frame ที่ f"

  1. แยก logical address เป็น (p, d): p = page number = logical div page_size และ d = offset (ตำแหน่งในหน้า) = logical mod page_size
  2. เปิด page table ช่องที่ p → ได้ f (frame number)
  3. physical address = (f, d) = f × page_size + d (d เท่าเดิม เพราะตำแหน่งในหน้าไม่เปลี่ยน)

💡 เทียบให้เห็นภาพ: แยกหนังสือเป็นแผ่น

หนังสือ (โปรแกรม) ถูกแยกเป็นแผ่น (page) แล้วนำไปเก็บในช่องลิ้นชัก (frame) ช่องไหนก็ได้ ส่วนสมุดสารบัญ (page table) บอกว่า "แผ่นที่ 2 อยู่ลิ้นชักที่ 1" ถ้าอยากอ่าน "บรรทัดที่ 2 ของแผ่นที่ 2" ก็ไปที่ลิ้นชัก 1 แล้วอ่านบรรทัดที่ 2 (offset เท่าเดิม)

🔍 ตัวอย่างในชีท (รูป 3.1.M): memory 32 byte, page = 4 byte, page table 0→5, 1→6, 2→1, 3→2

logical 0  (A): p = 0 div 4 = 0, d = 0 mod 4 = 0 → f = 5 → 5×4 + 0 = 20
logical 5  (F): p = 1, d = 1                     → f = 6 → 6×4 + 1 = 25   ← ชีทคำนวณข้อนี้
logical 13 (N): p = 3, d = 1                     → f = 2 → 2×4 + 1 = 9

🔍 ระดับบิต (รูป 3.1.N): address 16 บิต, page = 1 KB

  1. page = 1024 = 210 → d ใช้ 10 บิตขวา และ p ใช้ 16 − 10 = 6 บิตซ้าย → มีได้ 26 = 64 page (p = 0–63, d = 0–1023)
  2. แปลง 1502 เป็นฐาน 2 แล้วตัดเป็น 6 | 10 บิต (ดู บทที่ 0.6)
logical 1502  = 000001 | 0111011110  → p = 1, d = 478
page_table[1] = 000110 = 6
physical      = 000110 | 0111011110  = 6×1024 + 478 = 6622
🗣 อาจารย์พูดaddress ใน logical ถ้าเป็นเรื่อง page จะใช้คำว่า page … frame คือ physical frame ที่ขนาดเท่ากับ page … frame มันจะบอกตำแหน่งเริ่มต้น แล้วก็ CPU อยากอ่านตำแหน่งไหน สมมติว่า page ละ 1K … 0 ถึง 1023
⚠️ จุดที่มักพลาด• เอา p ไปคูณ page size แทน f (ต้องใช้ f ที่ได้จาก page table)
• ลืมว่า d ไม่เปลี่ยน
• นับเพจเริ่มที่ 1 (ต้องเริ่มที่ 0)
Q2page table เดียวกับรูป 3.1.M (0→5, 1→6, 2→1, 3→2) logical address 10 ได้ physical address เท่าไร
p = 10 div 4 = 2, d = 10 mod 4 = 2 → f = 1 → 1×4 + 2 = 6 (ตัวอักษร K อยู่ที่ตำแหน่งจริง 6 ตรงกับรูป)
Q316 บิต, page 1 KB, page table ช่องที่ 2 = 011001₂ แล้ว logical address 3000 = physical เท่าไร
3000 = 2×1024 + 952 → p = 2, d = 952 → f = 011001₂ = 25 → 25×1024 + 952 = 26552
Q4ทำไม paging ถึงแก้ปัญหา external fragmentation ได้
เพราะแบ่งทั้ง logical memory และ physical memory เป็นชิ้นขนาดเท่ากัน (page = frame) เพจลงเฟรมว่างไหนก็ได้ ไม่ต้องติดกัน ขอแค่มีเฟรมว่างพอ จึงไม่ต้องหา hole ใหญ่ติดกันและไม่ต้อง compaction แต่จะยังมี internal fragmentation ในเพจสุดท้าย

ฝึกเพิ่มด้วยโจทย์สุ่ม:

2.10 เสริมนอกชีท: TLB, segmentation ข้ามได้

⏭ ข้ามได้ — ไม่อยู่ในชีทอาจารย์ และอาจารย์ไม่ได้พูดถึง (กดเพื่อเปิดดู)

อยู่ในแผนการสอนสัปดาห์ที่ 10 (Paging, Segmentation) ให้อ่านหลังจากเข้าใจส่วนบนแล้ว

🤔 TLB คืออะไร

page table อยู่ใน RAM ทุกครั้งที่อ่านข้อมูล 1 ครั้ง จึงต้องเข้า RAM 2 รอบ (รอบแรกอ่าน page table รอบสองอ่านข้อมูลจริง) ซึ่งช้า TLB คือหน่วยความจำเล็กๆ ที่เร็วมาก จำผลการแปลง page → frame ที่เพิ่งใช้ไว้ (คล้าย cache)
ถ้าเจอใน TLB (hit) ก็ข้ามการอ่าน page table ได้ ถ้าไม่เจอ (miss) ต้องอ่าน page table ตามปกติ

EAT = hit × (TLB + mem) + miss × (TLB + 2 × mem)
เช่น TLB 20 ns, mem 100 ns, hit 80%: 0.8 × 120 + 0.2 × 220 = 140 ns

🤔 Segmentation คืออะไร

แบ่งโปรแกรมตามความหมาย (main, function, stack, symbol table …) แต่ละส่วน (segment) ขนาดไม่เท่ากัน address เขียนเป็น <s, d> แล้วใช้ segment table ที่เก็บ base (จุดเริ่ม) กับ limit (ขนาด) ถ้า d < limit จะได้ physical = base + d ถ้าไม่ใช่เกิด trap
เช่น segment 2 (limit 400, base 4300): (2, 53) → 4353

Q5TLB = 20 ns, memory = 100 ns, hit ratio = 98% หา EAT (ns)
0.98 × (20 + 100) + 0.02 × (20 + 200) = 117.6 + 4.4 = 122 ns

สรุปบทนี้ใน 1 นาที

  • Binding 3 จังหวะ: compile (absolute) / load (relocatable) / execution (ย้ายได้ ใช้ base register)
  • Logical = ที่โปรแกรมอ้าง, physical = ที่อยู่จริง = base + logical (ต้อง < limit ไม่งั้น trap)
  • Dynamic loading = โหลดเมื่อถูกเรียก · static linking = รวม library เข้าไฟล์ · dynamic linking = ลิงก์ตอนรัน (stub)
  • Overlay = ผลัดกันใช้พื้นที่ (200K → 140K) · swapping = ย้ายไปพักที่ backing store + save/load PCB
  • Low/high memory: ดูเลข address ไม่ใช่ตำแหน่งในรูป
  • First/best/worst fit: hole แรก / เล็กสุดที่พอ / ใหญ่สุด
  • External fragmentation → compaction (ช้า) → paging
  • Paging: p = div, d = mod, f = table[p], physical = f × size + d