4.2 מבנה מערכת ההפעלה הרצאה
מערכת ההפעלה¶
- מערכת ההפעלה מורכבת משני חלקים הקרנל והuserland:
- הקרנל זה הלב של מערכת ההפעלה, המנוע שלה - שאחראי על כל ההתקנים במערכת (העכבר, המקלדת), אחראי על הקבצים, האחסון ואחראי על לטעון תוכנות לראם.
- הuserland הוא כל התוכנות שאנחנו מכירים במערכת ההפעלה, בפועל כאשר תוכנה במערכת ההפעלה (למשל הדפדפן שלכם) צריכה להשתמש במשאב במערכת ההפעלה כמו קובץ, היא מבקשת מהקרנל לעשות את הפעולה.
- דמיינו שהקרנל זוהי תוכנה שרצה ישירות על החומרה שלכם במחשב, ויש לה גישה לראם, לאחסון ולמעבד - הקרנל מריץ בעצמו את התוכנות הרגילות (הuserland) ובכל פעם שהתוכנות האלו צריכות משהו, כמו לפתוח קבצים, להטען לזכרון (להריץ קבצי הרצה), או אפילו לתקשר עם המקלדת והעכבר התוכנות בuserland חייבים לעשות זאת דרך הקרנל, ולבקש מהקרנל לעשות בשבילם את הפעולות האלה
- דמיינו שהתוכנות בuserland הן לקוחות במסעדה, הקרנל הוא המלצר והטבחים הם החומרה, וכאשר הלקוחות רוצים משאבים (אוכל מהטבחים) הם מדברים אל הקרנל.
- אז איזה דברים תוכנות הuserland יכולות לבקש מהקרנל?
- גישה לאינטרנט (הקרנל מנהל את הכרטיס רשת של המחשב)
- גישה לקבצים (הקרנל מנהל את האחסון של המחשב)
- הרצת תוכנות (הקרנל מנהל את הזכרון של המחשב)
- גישה לקלט - עכבר ומקלדת (הקרנל מנהל את ההתקנים של המחשב)
- גישה לגרפיקה (הקרנל מנהל את המסך)
- ועוד!
למה?¶
- מדוע שנרצה להפריד בין הuserland לקרנל? הסיבה העיקרית היא סיבה אבטחתית, אם ניתן גישה לתוכנות שרצות בuserland לכל המשאבים ולחומרה, הם יכולו בקלות מאוד להרוס את המחשב ואת כל מערכת ההפעלה. ותוכנות שרצות בuserland הן תוכנות שבדרך כלל אנחנו מורידים מהאינטרנט, כמו דפדפן, ספוטיפי, משחקים במחשב, ועוד.
- אז מדוע קוד שרץ בuserland לא יכול לעשות את כל הדברים שקוד שרץ בקרנל יכול? הרי קוד זה קוד! - אז לא בדיוק,
במעבד שלנו יש מצבים, נקח לדוגמה את מעבדי intel (X86) - כאשר קוד שרץ בקרנל רץ, הוא רץ במצב realmode, שזה מצב שהמעבד מאפשר לקוד שהוא מריץ לעשות כל מה שהוא רוצה ולגשת לכל משאב שהוא רוצה, ולעומת זאת כאשר המעבד מריץ קוד שנמצא בuserland הוא מריץ אותו במצב שנקרא protected mode שהמעבד ממש חוסם להריץ פעולות מסויימות כגון גישה לא מבוקרת למשאבים במחשב. - אז איך תוכנות שרצות בuserland מבקשות דברים מהקרנל?
קונספט: syscall¶
- קריאות syscall הן הדרך שבה תוכנות בuserland מדברים עם הקרנל, חשבו על syscall כממש פונקציה שאפשר לקרוא ולהריץ אותה מקוד שאנחנו כותבים שמבצעת פעולה במערכת הפעלה על ידי הקרנל, למשל כמו פונקציה שכותבת מידע לקובץ, בסוף זה מתבצע על ידי הקרנל.
- לדוגמה כאשר בפייתון אנחנו כותבים מידע לקובץ, מאחורי הקלעים הפונקציה הזו קוראת לsyscall שנקרא נגיד WriteFile שמבקש מהקרנל לכתוב מידע לקובץ.
- רשימת כל הsyscall-ים בווינדוס: https://j00ru.vexillium.org/syscalls/nt/64/
- על כולם יש תיעוד באינטרנט בmsdn (דוקמנטציה רשמית של מיקרוסופט
ממשקים לsyscall-ים¶
- לרוב כשאנחנו כותבים תוכנה או קוד שרץ בusermode אז אנחנו לא קוראים ישירות לsyscall, בדרך כלל אנחנו משתמשים בספריות שאנשים כתבו שקוראות מאחורי הקלעים לאיזשהו syscall. למעשה בהמון טכנולוגיות פיתוח קוד שונות שאנחנו משתמשים בהם, מפרש של פייתון, מנוע javascript או ספרייה ב.net לרוב הן קוראות לsyscall-ים- ולא אנחנו בקוד שלנו.
- בווינדוס, כדי לתקשר עם הקרנל קיימת ספרייה של מיקרוסופט שנקראת winAPI, שמאפשרת לתוכנות בווינדוס לתקשר ישירות עם הספרייה, ומשם היא מאחורי הקלעים מתקשרת עם הקרנל בשבילנו.
- כאן יש תיעוד של מיקרוסופט על כל הפונקציונליות של הספרייה: https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-api-list
דרייברים- כתיבת קוד לקרנל¶
- דרייבר הוא קוד מיוחד שרץ בקרנל, לרוב מטרתו זה לנהל איזשהו רכיב חומרתי.
- למשל מצלמה שאתם קונים למחשב שלכם, כנראה מגיעה עם דרייבר- קוד שרץ בקרנל שיודע להתממשק ולנהל את המצלמה. וכך תוכנות כמו זום, סקייפ או דיסקורד בusermode יוכלו להשתמש בצורה מבוקרת במצלמה.
- כל חברה יכולה לכתוב דרייברים למוצרים שלה, ובכך המוצרים יוכלו לעבוד עם ווידנוס.
- ומכאן מגיעה השאלה: כל בן אדם יכול פשוט לכתוב דרייבר שירוץ בקרנל ולהעלות אותו בקלות? אז התשובה היא לא, לכתוב קוד שרץ בקרנל זה מאוד מסוכן, באג קטן ואתם יכולים לשבור את כל המערכת הפעלה.
- אז מי כן יכול לכתוב דרייברים לקרנל? רק חברות שמיקרוסופט (החברה שפיתחה את ווינדוס) מאשרת, למעשה כל דרייבר שחברה מסויימת רוצה לשחרר לווינדוס, מיקרוסופט צריכה לאשר, ולחתום חתימה קריפטוגרפית על הדרייבר, ובכך לאשר ריצה שלו בווינדוס.
- מה היא חתימה קריפטוגרפית? קריפטוגרפיה היא ענף במתמטיקה ובמדעי המחשב העוסק במחקר ופיתוח שיטות אבטחה למידע וצפנה של תקשורות. במילים פשוטות- מדובר על אלגוריתמים מתמטיים שאנשים חכמים מפתחים כדי להגן על נתונים, ולהבטיח איי זיופים של דברים מסויימים. אז איך זה מתבטא? ברגע שוונדוס מריצים דרייבר, הם בודקים עם אלגוריתמים קריפטוגרפים שבאמת הדרייבר הזה חתום על ידי ווינדוס. ואם כן אז ווידנוס מריץ על הדרייבר בהצלחה.
קבצי הרצה¶
- במערכת הפעלה יש המון סוגים של קבצים, ולרוב הקבצים בדרך כלל יש סיומת מסויימת שמציינת את סוג הקובץ
- קובץ הרצה- סיומת .exe: סוגי קבצים שמערכת ההפעלה יודעת להריץ כתוכנה.
- קובץ ספרייה דינמית- סיומת .dll: קובץ שמשמש כספרייה, שנטענת ביחד עם תוכנה מסויימת כשהתוכנה רצה. כל תוכנה יכולה להשתמש בdll-ים ולטעון אותם ביחד איתם ולהשתמש בהם. לרוב קבצים כאלו פשוט יכילו המון פונקציות שאפשר לקרוא להם. והמון פעמים כמה תוכנות במקביל יכולות לטעון את אותה ספרייה ולהשתמש בה ביחד.
- קובץ דרייבר (התקן)- סיומץ .sys: קובץ דרייבר, התקן שרץ בקרנל ולרוב אחראי לנהל משהו במערכת ההפעלה. קוד קרנלי.
- קבצי exe ו- dll-ים של ווינדוס יכולים לרוץ גם בקרנל. נקודה חשובה: לא מדובר על exe ו- dll-ים usermode-ים, מדובר ספציפית על exe-ים של הקרנל שווינדוס פיתחו.
אז איך ווינדוס יודע בכלל להריץ קבצי exe ו- dll? הרי הם מכילים שפת מכונה? כיצד הוא יודע איך לטעון את הקוד לזכרון? - הרכיב שאחראי לעשות זאת נקרא הloader, קוד בקרנל שאחראי להריץ את הקבצים האלו, ולטעון אותם לזכרון כתוכנה חדשה. למעשה בווינדוס קבצי dll וקבצי exe כתובים בפורמט מיוחד שנקרא PE, שהloader יודע לטעון ולקרוא.
הקרנל על רגל אחת¶
אז מה קורה שאנחנו מנסים לפתוח קובץ בפייתון?
1. את הקוד פייתון שלנו המפרש מריץ, שהיא התוכנה python.exe והוא בסוף אחראי להריץ את הקוד פייתון.
2. מאחורי הקלעים יכול להיות שהמפרש פייתון שלנו משתמש בספרייות דינמיות (dll-ים.) למשל python39.dll, ספרייה דינמית של פייתון 3.9. בספרייה הזו למשל קיים המודול המובנה בפייתון io שאחראי לטפל בכל הקריאות שקשורות לפלט וקלט. הdll הזה נטען לתוכנת פייתון כשאנחנו מריצים אותה.
3. כאשר הdll הזה עושה פעולה כלשהי על קבצים, הוא משתמש בwinapi כדי לעשות את הפעולה הזו, winapi היא ספרייה של ווינדוס שנועדה כדי לתקשר עם הקרנל. כאשר הdll מבצע את פעולת פתיחת הקובץ, הוא קורא לפונקצית winapi שפותחת קובץ חדש: CreateFileW שמומשת ב Kernel32.dll. לwinapi יש עוד המון dll-ים, שאחראים לכל הקריאות winapi הקיימות. (יש כמעט 1000 סוגים שונים של פונקציות בwinapi) חלק מהdll-ים של winapi הם למשל: user32.dll, advapi32.dll ועוד. וכולם בסוף קוראים לntdll.dll שבסוף מבצע את הsyscall.
4. אחרי שקראנו לwinapi כדי לבצע פעולה כלשהי עם הקרנל, הwinapi עושה את הsystem call (הsyscall). ובעצם גורם למעבד שלנו לשנות את ה"מוד" שלו ממצב protected mode למצב real mode. שמאפשר להריץ קוד קרנלי, מהחלק הזה- כל מה שירוץ יהיה קוד קרנלי. ליבת הקרנל, שנמצאת בקובץ ntsokrnl.exe מקבלת את הsyscall, ומתחילה להריץ אותו. הקרנל משתמש בדרייברים של הדיסק, האחסון והמערכת קבצים (נדבר בעתיד על מה היא מערכת קבצים), הם נקראים- ntfs.sys ו- fat32.sys ובסוף כל הקוד הקרנלי הזה יפתח לנו קובץ חדש במערכת ההפעלה, ואחרי שהקוד יסיים לרוץ הוא יחזור אלינו, ישירות לwinapi, ומשם לdll של פייתון, ומשם עד לקוד פייתון שלנו בחזרה.
