מפת NT ומעבר לקרנל¶
נניח שאנחנו קוראים ל־SetEvent. מבחינת C++ זו פונקציה שמקבלת handle ומחזירה BOOL. מבחינת מערכת ההפעלה, אנחנו מבקשים לשנות אובייקט משותף שמערכת התזמון יכולה להמתין עליו. כדי להבין את הפעולה צריך לעקוב אחר המעבר מהקוד שלנו אל רכיבי Windows.
השם Windows מתאר מערכת שלמה, ולא פונקציה אחת או שכבה אחת. הממשק Win32, ספריות משתמש, שירותי NT, מנהלי ה־executive, מנגנוני kernel ודרייברים משתתפים במסלולים שונים. המפה עוזרת לשאול מי אחראי לכל החלטה.
מתחילים מפעולה קטנה¶
התוכנית הבאה יוצרת event לא מסומן, מסמנת אותו וממתינה לו עם timeout אפס. שומרים אותה כ־event_path.cpp.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <cstdio>
int main() {
HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
if (!event) {
std::printf("create error=%lu\n", GetLastError());
return 1;
}
if (!SetEvent(event)) {
const DWORD error = GetLastError();
CloseHandle(event);
std::printf("set error=%lu\n", error);
return 2;
}
const DWORD result = WaitForSingleObject(event, 0);
const DWORD error = result == WAIT_FAILED ? GetLastError() : 0;
CloseHandle(event);
std::printf("wait=%lu error=%lu\n", result, error);
return result == WAIT_OBJECT_0 ? 0 : 3;
}
ה־event נשאר מסומן כי בחרנו manual reset. ההמתנה אינה צריכה לחכות לתהליך אחר, אך היא עדיין משתמשת בחוזה ההמתנה של מערכת ההפעלה. בפרק הזה לא נשתמש בתוצאה כדי לטעון שלא היה context switch; בשביל טענה כזו צריך trace של תזמון.
הממשק הציבורי אינו כל המימוש¶
הקריאה מ־Win32 יכולה לעבור דרך Kernel32, KernelBase ו־ntdll לפי ה־build וה־API. חלק מהשמות הם forwarders או עטיפות, וחלק מהעבודה מתבצעת במרחב המשתמש. אין יחס קבוע של קריאת Win32 אחת לקריאת מערכת אחת.
ב־WinDbg הגדירו symbols מתאימים, עצרו ב־main והציבו breakpoint בפונקציה ntdll!NtSetEvent אם היא קיימת ב־build שנחקר. הפעולות הבאות הן פקודות דיבאגר, לא קוד יישום.
המחסנית מראה את הדרך שהביאה אותנו אל ה־stub. ב־x64 ניתן לצפות בקוד שמכין מספר שירות ומפעיל מעבר מערכת. הרצף והמספר הם פרטי מימוש של ה־build. איננו מעתיקים אותם לתוכנית ואיננו הופכים אותם לממשק חלופי ל־WinAPI.
מצב הרשאה וזהות תהליכון הם צירים שונים¶
הוראת מעבר למערכת משנה את אופן הביצוע ואת הכללים שהמעבד אוכף. קוד הקרנל יכול לגשת למשאבים ולנתונים שקוד המשתמש אינו רשאי לנהל ישירות. המערכת שומרת מידע שנדרש כדי לחזור להקשר המשתמש המתאים.
המעבר אינו מחייב החלפת thread. אותו thread יכול להיכנס לקרנל, לבצע פעולה קצרה ולחזור. אם הפעולה מחייבת המתנה, או אם התזמון בוחר להפעיל thread אחר, ייתכן גם context switch. אלה אירועים שונים שיכולים להתרחש באותו מסלול.
בדומה לכך, שני threads של תוכנית משתמש יכולים לרוץ באמת במקביל על שתי ליבות. אין צורך שכל אחד מהם יהיה kernel thread שכתבנו בעצמנו. מערכת ההפעלה מתזמנת את ישויות הביצוע, וקוד המשתמש הוא חלק מהעבודה שלהן.
ה־executive מחלק אחריות¶
מנהל האובייקטים מטפל בזהות ובחיים של אובייקטים ובהפניות אליהם. מנהל הזיכרון מטפל במרחבי כתובות ובדפים. מנהל ה־I/O מתאם בקשות עם דרייברים, ומנגנוני האבטחה משתתפים בבדיקות גישה.
המונח kernel משמש לעיתים לכל הקוד שרץ ב־kernel mode, ולעיתים לשכבה צרה יותר שעוסקת בתזמון, dispatching, interrupts ומנגנונים נמוכים. צריך להבין באיזה מובן משתמשים בו בכל תיעוד. העובדה שרכיבים רבים נמצאים ב־ntoskrnl אינה מוחקת את ההפרדה הלוגית ביניהם.
ה־HAL מספק שכבת התאמה לחלק מפרטי החומרה. דרייברים נוספים מנהלים התקנים ומסלולי I/O. אין לצייר שכל פעולה עוברת דרך כל הרכיבים בסדר אחד; המפה היא רשת אחריות, והמסלול נקבע לפי הבקשה.
הפניה אינה הרשאה בלתי מוגבלת¶
בכניסה לקרנל המערכת אינה אמורה להאמין לכל מספר שהמשתמש העביר. צריך לפרש handle בהקשר המתאים, לבדוק סוג אובייקט וזכויות ולוודא שפרמטרים אחרים חוקיים. המעבר ל־kernel mode אינו הופך את בקשת היישום לבקשה מורשית.
בשלב הזה אפשר לחשוב על handle כעל הפניה שבאמצעותה מערכת ההפעלה כבר קישרה אובייקט וזכויות מסוימות. בשיעור מנהל האובייקטים נפריד בין הערך המקומי, טבלת ה־handles והאובייקט עצמו.
תצפית מוגבלת עדיפה על הסבר מומצא¶
דיבאגר user mode יכול להראות את ה־stub ואת מחסנית המשתמש, אך אינו נותן תמונה מלאה של מצב הקרנל. כדי לחקור trap frame או תזמון פנימי צריך כלי מתאים, trace או חיבור kernel debugging במעבדה נפרדת.
הדוח של הניסוי מפריד בין מה שנראה באסמבלי, מה שמובטח ב־API ומה שמשוער לגבי המימוש הפנימי. כך אפשר להשוות שני builds בלי לבנות תוכנה שתלויה במספר שירות או ב־offset שהתגלו במקרה.
לקריאה נוספת: סקירת רכיבי Windows.