הinterrupt Page Fault – וallocation Page-ים דינמית¶
1. למה צריך בכלל Page Fault?¶
עד עכשיו למדנו שה־paging מאפשר לכל תוכנה במערכת לרוץ כאילו יש לה RAM משלה, מבלי לראות את שאר התוכנות.
אבל בפועל, אנחנו לא באמת מקצים את כל הזיכרון הזה מראש – זה לא יעיל, וזה לא תמיד אפשרי.
אז מה עושים?¶
אנחנו מקצים דפים (Pages) רק כשצריך.
אבל... המעבד לא יודע לחכות בסבלנות.
אם תוכנה מנסה לגשת לכתובת שלא קיימת לה – המעבד פשוט עוצר הכל ומקפיץ שגיאה.
וזה בדיוק מה שנקרא:
2. Page Fault¶
מה זה?¶
כאשר תוכנה מנסה לגשת לכתובת שאין לה page מוגדר – או שאין לה הרשאות מתאימות – המעבד לא נותן לה לעשות זאת.
במקום זה, הוא קורא לinterrupt מיוחדת: int 14h – זו interrupt פנימית של החומרה.
המעבד לא פשוט קורס – הוא עוצר את הprocess ועובר להריץ handler שהקרנל שלך מגדיר.
3. מתי קורה Page Fault?¶
| מצב | מה הסיבה לשגיאה? |
|---|---|
| Page לא קיים בטבלאות | ה־bit של Present = 0 |
| ניסיון כתיבה ל־Page שהוא לקריאה בלבד | bit של RW = 0 |
| ניסיון גישה מיוזר ל־Page קרנלי | bit של User/Supervisor = 0, ו־CPL=3 (Ring 3) |
4. איך המעבד מתאר את השגיאה?¶
המעבד שומר מידע על השגיאה כדי שתוכל להבין מה קרה בדיוק.
הוא דוחף ל־Stack:¶
-
כתובת ה־EIP שממנה באנו
-
קוד השגיאה –
error code -
כתובת ה־CR2 – הכתובת הווירטואלית שגרמה ל־Page Fault!
CR2 = כתובת שהמעבד ניסה לגשת אליה ונכשל.
5. איך אנחנו מטפלים בזה?¶
אנחנו מגדירים ב־IDT שלנו את handler של הinterrupt int 14h – כלומר את המקום שבו ירוץ הקוד כשיש Page Fault.
idt_14:
dw page_fault_handler
dw 0x08 ; Kernel selector
db 0
db 10001110b ; Present, Ring 0
dw page_fault_handler >> 16
6. כתיבת ה־Handler¶
page_fault_handler:
pusha ; save the registers
push ds
push es
; Step 1 - get the address that caused the error
mov eax, cr2 ; the address that led to the page fault
mov [fault_address], eax ; save it in a variable for later reading
; Step 2 - get the error code
mov ebx, [esp + 32] ; error code is located after pusha + 2 pushes
; (optional) we could print what happened, or check the cause of the error
; Step 3 - try to allocate a new page for this address
call pagefault_resolve
; Step 4 - restore all registers and continue
pop es
pop ds
popa
add esp, 4 ; get rid of the error code
iret
7. מה עושה pagefault_resolve?¶
המטרה: להוסיף Page חדש ל־Page Table, שמכסה את הכתובת שהובילה ל־Page Fault.
שלבים:¶
-
נקבל את הכתובת שגרמה לשגיאה (
cr2) -
נחשב את ה־Directory Index וה־Table Index
-
נבדוק האם Page Table כבר קיימת (אם לא – ניצור אותה)
-
ניצור Page חדש עם הרשאות מתאימות
-
נעדכן את הטבלה
-
נסיים – הפונקציה חוזרת וה־IRET משחזר את המצב
8. דוגמה לפונקציה pagefault_resolve¶
pagefault_resolve:
mov eax, [fault_address] ; get the address
shr eax, 12 ; take the page number (Page Number)
mov ebx, eax
and ebx, 0x3FF ; Table Index (10 bits)
shr eax, 10
and eax, 0x3FF ; Directory Index (10 bits)
; find the table's address
mov ecx, [cr3] ; cr3 = page directory address
mov edx, [ecx + eax*4] ; the page table
; if the table doesn't exist - create a new one
test edx, 1 ; Present?
jnz .has_table
; no table? create one
call allocate_frame ; returns an address for a new 4KB frame
or eax, 0x7 ; Present, RW, User
mov [ecx + eax*4], eax ; place it in the table
call flush_tlb ; refresh the page table (discussed later)
mov edx, eax
.has_table:
and edx, 0xFFFFF000 ; clear the low bits (address only)
; now create a new page
call allocate_frame
or eax, 0x7 ; Present, RW, User
mov [edx + ebx*4], eax ; insert into the page table
ret
9. שימושים מתקדמים¶
ברגע שיש לנו Page Fault:
-
נוכל להקצות זיכרון On Demand – רק כשצריך!
-
נוכל לממש
mallocפשוט ביוזר מוד -
נוכל לממש
mmap– טעינה של קובץ לזיכרון לפי דרישה -
נוכל לאכוף הרשאות זיכרון – pages לקריאה בלבד / קרנל בלבד
שאלות למחשבה¶
מה יקרה אם הקרנל עצמו יקבל Page Fault?¶
אם הקרנל מקבל Page Fault – זה בדרך כלל באג חמור מאוד.
בניגוד ליוזר מוד, שם אנחנו מצפים ל־page faults כחלק מניהול זיכרון רגיל, הקרנל אמור לגשת רק לpages שהוא הגדיר מראש.
אם בכל זאת מתקבל Page Fault בתוך הקרנל (למשל גישה ל־NULL, או גישה לpage שלא הוקצה), יש שתי אפשרויות:
-
הקרנל מקריס את עצמו (
kernel panic) ומפסיק את כל המערכת. -
או, אם יש לו handler מתוחכם – הוא יכול לנסות לשחזר את המצב (בדרך כלל בלינוקס זה ייגמר בקריסת process או reboot).
האם אפשר לשתף pages בין תוכנות שונות?¶
כן! וזה אפילו כלי חשוב במערכות הפעלה מתקדמות.
מערכת ההפעלה יכולה להגדיר page מסוים כך שהוא יופיע בטבלאות של כמה תוכנות שונות – ואז הן כולן רואות את אותו זיכרון.
דוגמאות לשימושים:
- שיתוף זכרון בין תוכנות לצורך תקשורת פנימית בניהם
- ספריות משותפות (ספריות שנטענות להרבה תוכנות)
- מיפוי קבצים משותפים, כאשר תוכנות ניגשות לאותן קבצים
כמובן, צריך לשים לב להרשאות (קריאה בלבד, כתיבה וכו') כדי למנוע בעיות אבטחה.