การจัดการหน่วยความจำ

1. ลำดับชั้นของหน่วยความจำ (Memory Hierarchy)
แนวคิดหลัก: คอมพิวเตอร์ต้องบาลานซ์ “ความเร็ว” กับ “ต้นทุน” จึงมีหน่วยความจำหลายชั้น ไล่จากเร็ว–แพง–ความจุน้อย ไปช้า–ถูก–ความจุสูง
ระดับต่าง ๆ
Register
อยู่ภายใน CPU ใกล้ตัวหน่วยคำนวณที่สุด
ขนาดเล็กมาก (หลักสิบ–หลักร้อยไบต์) แต่เร็วที่สุด ใช้เก็บค่าตัวแปร/ผลลัพธ์ชั่วคราวของคำสั่งที่กำลังทำงาน
Cache (L1, L2, L3)
อยู่บน chip CPU หรือใกล้ CPU มาก
แบ่งเป็นหลายระดับ: L1 เร็วที่สุดแต่เล็กสุด, L2/L3 ช้าลงแต่ใหญ่ขึ้น
หน้าที่คือเก็บสำเนาข้อมูล/คำสั่งที่ “มีแนวโน้มใช้บ่อยในระยะสั้น” เพื่อลดการเข้าถึง RAM
Main Memory (RAM)
เป็นที่อยู่ของโปรเซสและข้อมูลที่กำลังใช้งาน
OS จัดสรรพื้นที่ใน RAM ให้แต่ละโปรเซส เช่น code, data, stack, heap
เข้าถึงช้ากว่า cache แต่ยังเร็วกว่า disk มาก
Secondary Storage (SSD/HDD)
ใช้เก็บไฟล์ ถาวร เช่น ระบบปฏิบัติการ โปรแกรม เอกสาร ฯลฯ
ความจุสูงมากแต่ช้ากว่า RAM หลายเท่า
OS ใช้เป็นแหล่งโหลดโปรแกรมขึ้น RAM และใช้ทำ swap/page file เมื่อต้องจำลอง RAM เพิ่ม
Tertiary Storage (Tape, Cloud, Backup Storage)
ใช้เก็บสำรองข้อมูลระยะยาว เช่น backup, archive
เข้าถึงช้ามาก แต่ต้นทุนต่อ GB ถูกที่สุด
หลักการสำคัญ
Locality of Reference
Temporal locality: ถ้าเพิ่งใช้ข้อมูลชุดหนึ่ง มีโอกาสสูงว่าจะใช้ซ้ำอีก
Spatial locality: ถ้าใช้ตำแหน่งหนึ่ง มีโอกาสใช้ตำแหน่งใกล้เคียง
OS และ hardware (cache controller, MMU) ทำงานร่วมกันเพื่อใช้ประโยชน์จาก locality เอาข้อมูลที่ “มีแนวโน้มใช้บ่อย” ขึ้นมาอยู่ชั้นที่เร็วกว่าโดยอัตโนมัติ
2. Address Binding และ MMU
แนวคิดหลัก: โปรแกรมที่เขียน/คอมไพล์ไม่ได้รู้ตำแหน่งจริงใน RAM ล่วงหน้า จึงใช้ “logical address” แล้วให้ระบบแปลงเป็น “physical address”
Logical Address vs Physical Address
Logical Address
ที่อยู่ที่ CPU/โปรแกรมเห็น เช่น 0x0000–0xFFFF สำหรับโปรเซสหนึ่ง
เป็นมุมมองเชิงตรรกะ ทำให้แต่ละโปรเซสเหมือนมีหน่วยความจำของตนเอง
Physical Address
ที่อยู่จริงบน RAM เช่น ช่องที่ 2 GB ของแถวแรมจริง
ถ้าดูจากมุมมอง hardware จะเห็นเป็นตัวเลขตำแหน่งจริงในโมดูล RAM
Memory Management Unit (MMU)
ฮาร์ดแวร์ระหว่าง CPU กับ RAM ทำหน้าที่แปลง logical → physical ทุกครั้งที่มีการอ้างอิงหน่วยความจำ
ใช้ข้อมูลจาก structures ต่าง ๆ เช่น segment table, page table
ประเภทของ Address Binding
Compile-time binding
ตำแหน่งหน่วยความจำถูกกำหนดตอนคอมไพล์
โปรแกรมต้องโหลดที่ตำแหน่งนั้นเท่านั้น (ไม่ยืดหยุ่น)
ปัจจุบันใช้ในบางระบบฝังตัว (embedded) ที่เรียบง่าย
Load-time binding
ตำแหน่งถูกตัดสินตอนโหลดโปรแกรมเข้า RAM
ถ้าต้องย้ายตำแหน่งใหม่ ต้องโหลดใหม่เพราะ address เปลี่ยน
Execution-time binding
ใช้ MMU แปลงที่อยู่แบบ dynamic ขณะโปรแกรมกำลังทำงาน
ทำให้โปรเซสย้ายที่อยู่ใน RAM ได้ (relocation) หรือแม้แต่แบ่งกระจายเป็นหลายเฟรมได้ (paging)
เป็นรูปแบบที่ใช้ใน OS ทั่วไปในปัจจุบัน
3. การจัดสรรหน่วยความจำแบบต่อเนื่อง (Contiguous Allocation)
ใช้ในโมเดลที่ให้โปรเซสหนึ่งใช้พื้นที่ใน RAM ติดกันเป็นบล็อกเดียว (ไม่มีการแบ่งเป็น page/segment ย่อยในระดับ OS)
แนวคิด
RAM ถูกแบ่งเป็น “ช่องว่าง (hole)” และ “ส่วนที่ถูกใช้งาน”
เมื่อมีโปรเซสใหม่ ต้องหาช่องว่างที่มีขนาด >= ขนาดที่โปรเซสนั้นต้องการ
เมื่อโปรเซสจบ จะปล่อยพื้นที่กลับเป็นช่องว่าง ทำให้เกิดช่องว่างเล็ก ๆ กระจายอยู่ (external fragmentation)
วิธีเลือกช่องว่าง
First-fit
เลือก “ช่องแรก” จากต้นหน่วยความจำที่ใหญ่พอ
เร็วเพราะไม่ต้องไล่ดูทั้งหมด
แต่ช่องว่างเล็ก ๆ อาจกระจายเต็มด้านหน้า ทำให้ fragmentation สูง
Best-fit
ไล่ดูทุกช่อง แล้วเลือกช่องที่ “พอดีที่สุด” หรือ เล็กที่สุดที่ยังพอได้
ใช้หน่วยความจำคุ้มเพราะไม่เหลือเศษมาก
แต่ต้องค้นหานานกว่า และก็ยังเกิดช่องว่างเล็ก ๆ จำนวนมากอยู่ดี
Worst-fit
เลือกช่องที่ “ใหญ่ที่สุด” แล้วจองบางส่วนให้โปรเซส
หวังว่าจะเหลือช่องว่างที่ยังใหญ่พอสำหรับโปรเซสอื่นต่อไป
โดยทั่วไปใช้ไม่บ่อยและประสิทธิภาพไม่ค่อยดีกว่าวิธีอื่น
External Fragmentation และ Compaction
External Fragmentation
พื้นที่ว่างรวมกันอาจเยอะพอ แต่กระจายเป็นชิ้นเล็ก ๆ ทำให้เอามารวมเป็นบล็อกใหญ่ต่อเนื่องไม่ได้
Compaction
OS ย้ายโปรเซสใน RAM มาต่อกันใหม่ เพื่อละลายช่องว่างเล็ก ๆ ให้กลายเป็นบล็อกว่างใหญ่ก้อนเดียว
ทำได้เฉพาะเมื่อใช้ execution-time binding (สามารถย้ายได้โดยไม่ต้องแก้โค้ด)
มีค่าใช้จ่ายสูงเพราะต้องคัดลอกข้อมูลจำนวนมาก
4. Paging และ Segmentation
สองเทคนิคนี้ถูกใช้เพื่อลดปัญหา fragmentation และเพิ่มความยืดหยุ่นในการจัดการหน่วยความจำ
4.1 Paging
แนวคิด
OS แบ่ง RAM เป็น “เฟรม (frame)” ขนาดเท่ากัน เช่น 4 KB ต่อเฟรม
แบ่ง logical address space ของโปรเซสเป็น “เพจ (page)” ขนาดเท่ากันกับ frame
หนึ่งเพจของโปรเซสสามารถไปอยู่ใน frame ใดก็ได้ใน RAM (ไม่ต้องติดกัน)
ใช้ “Page Table” ต่อโปรเซสเพื่อบอกว่าหน้า (page) ที่ n อยู่ใน frame หมายเลขใด
การแปลงที่อยู่ (Address Translation) แบบ paging
Logical address แบ่งเป็น 2 ส่วน:
page number (p)
offset ภายในเพจ (d)
MMU ใช้ page number ไป index ใน page table ได้ frame number (f)
physical address = frame number (f) + offset (d)
ทำให้ลด external fragmentation เพราะไม่ต้องให้โปรเซสใช้พื้นที่ติดกันทั้งก้อน มีเพียง internal fragmentation ที่เกิดจากพื้นที่ว่างในเฟรมสุดท้ายของโปรเซส
จุดเด่นของ paging
ทำให้มี “virtual memory” ได้ เมื่อใช้ร่วมกับ secondary storage (page file / swap)
ง่ายต่อการจัดสรรและย้ายเพราะทุกเฟรมมีขนาดเท่ากัน
4.2 Segmentation
แนวคิด
แบ่งโปรแกรมตามโครงสร้างตรรกะ เช่น
segment 0: code
segment 1: global data
segment 2: stack
segment 3: heap ฯลฯ
แต่ละ segment มีขนาดไม่เท่ากัน และเติบโต/หดตัวได้ตามลักษณะของ segment นั้น
การแปลงที่อยู่แบบ segmentation
Logical address แบ่งเป็น
segment number (s)
offset ภายใน segment (d)
มี Segment Table สำหรับแต่ละโปรเซส เก็บ base address (ตำแหน่งเริ่มต้นใน RAM) และ limit (ความยาว) ของแต่ละ segment
เมื่อจะเข้าถึงที่อยู่
ตรวจว่า offset < limit เพื่อเช็คว่าไม่เกินขอบเขต (ป้องกันการล้ำ segment)
physical address = base address ของ segment + offset
จุดเด่น/ข้อจำกัด
ใกล้เคียงมุมมองของนักโปรแกรมมากกว่า (logical grouping)
ทำการป้องกัน (protection) และการแชร์โค้ดระหว่างโปรเซสได้ง่าย
ยังมี external fragmentation เพราะ segment มีขนาดต่างกันและต้องวางติดกัน
การผสม (Segmentation with Paging)
บางสถาปัตยกรรมใช้ segmentation ระดับตรรกะ แต่ภายใน segment ใช้ paging อีกชั้นหนึ่ง
ช่วยลด fragmentation ในขณะที่ยังรักษามุมมองแบบ segment สำหรับโปรแกรมเมอร์
5. การตรวจสอบการใช้หน่วยความจำ (Monitoring Memory Usage)
หัวข้อนี้เชื่อมทฤษฎีกับการปฏิบัติจริงบน Windows / Linux
5.1 บน Windows
Task Manager
แท็บ Performance > Memory แสดง
ปริมาณ RAM ทั้งหมด, ใช้งานอยู่, เหลือว่าง
Cache, Committed, Paged/Non-paged pool
แท็บ Processes แสดง RAM ที่แต่ละโปรเซสใช้ (Working Set)
Resource Monitor
รายละเอียดลึกกว่า Task Manager
แสดง Hard faults/sec (เกี่ยวข้องกับ page fault), Standby, Modified, Free ฯลฯ
เหมาะสำหรับให้ผู้เรียนสังเกตพฤติกรรมเมื่อเปิด/ปิดโปรแกรมหนัก ๆ
Performance Monitor (perfmon)
เลือก counter เช่น
Memory\Available MBytes
Memory\Pages/sec
ใช้เก็บสถิติระยะยาว เช่น ดูแนวโน้มการใช้ RAM ในช่วงเวลาเรียน/ทดลอง
5.2 บน Linux
คำสั่งที่ใช้บ่อย:
free -hแสดง RAM รวม, ใช้แล้ว, ว่าง, buffers, cached, swap
ควรอธิบายว่า “used” ใน Linux รวม cache/buffer ด้วย ซึ่งไม่ได้แปลว่า RAM ขาด
vmstatแสดงข้อมูล virtual memory เช่น processes, memory, swap, IO, system, CPU
ใช้ดูแนวโน้ม page in/out, swap in/out
top/htopแสดง process แบบ real-time
ดูได้ว่า process ไหนกิน RAM/CPU มากสุด
/proc/meminfoไฟล์ pseudo ที่เก็บรายละเอียดสถานะหน่วยความจำ
ใช้กับ script หรือให้ นศ. อ่านค่าเฉพาะที่สนใจ เช่น MemTotal, MemFree, Buffers, Cached, SwapTotal, SwapFree
กิจกรรมที่ดีในแล็บ:
ให้ นศ. เปิดโปรแกรม “กิน RAM” เช่น browser หลายแท็บ หรือ VM แล้วสังเกตค่าใน Task Manager / free -h ก่อน–หลัง
ให้ทดลอง kill process หนัก ๆ แล้วสังเกตว่าค่า cached/buffers เปลี่ยนอย่างไร เพื่อเชื่อมกับแนวคิดว่าระบบชอบใช้ RAM ว่างให้เป็น cache แทนที่จะปล่อย idle