מנהל האובייקטים ומרחב השמות¶
נניח שפתחנו event, שכפלנו את ה־handle וסגרנו את המקור. האם האירוע נעלם? אם חושבים על handle כאילו היה האובייקט עצמו, התשובה נראית כן. בפועל סגרנו הפניה אחת, והאובייקט יכול להישאר חי דרך הפניה אחרת.
מנהל האובייקטים מספק דרך משותפת לנהל סוגים שונים של אובייקטי מערכת. לכל סוג יש גוף ומשמעות משלו, ומסביבם קיימים מנגנוני שמות, handles, זכויות וחיים. המשותף אינו אומר שכל הסוגים משתמשים באותה פונקציית סגירה או באותו מרחב שמות.
מייצרים שתי הפניות למצב אחד¶
התוכנית הבאה יוצרת event ללא שם ומשכפלת את ה־handle בתוך אותו תהליך. היא אינה מעבירה בעלות לתהליך אחר.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <cstdio>
#include <cwchar>
int main() {
HANDLE first = CreateEventW(nullptr, TRUE, FALSE, nullptr);
if (!first) return 1;
HANDLE second = nullptr;
if (!DuplicateHandle(GetCurrentProcess(), first,
GetCurrentProcess(), &second,
0, FALSE, DUPLICATE_SAME_ACCESS)) {
const DWORD error = GetLastError();
CloseHandle(first);
std::printf("duplicate error=%lu\n", error);
return 2;
}
CloseHandle(first);
first = nullptr;
if (!SetEvent(second)) {
const DWORD error = GetLastError();
CloseHandle(second);
std::printf("set error=%lu\n", error);
return 3;
}
const DWORD result = WaitForSingleObject(second, 0);
CloseHandle(second);
std::printf("wait=%lu\n", result);
return result == WAIT_OBJECT_0 ? 0 : 4;
}
הסגירה הראשונה אינה מבטלת את ההפניה השנייה. שתי ההפניות מצביעות ל־event אחד, ולכן אין כאן העתק של מצב האירוע. אם היינו יוצרים event חדש במקום לשכפל handle, היינו מקבלים שני מצבים עצמאיים.
טבלת handles שייכת להקשר¶
ערך handle בתהליך אחד אינו בהכרח בעל משמעות בתהליך אחר. המערכת מפרשת אותו דרך ההקשר והטבלה המתאימים. שליחת המספר בהודעה אינה יוצרת אוטומטית הפניה תקפה אצל המקבל.
הפעולה DuplicateHandle יכולה ליצור handle בטבלת תהליך יעד בהתאם לזכויות ולחוזה שלה. המספר שיתקבל ביעד אינו חייב להיות זהה למקור. הסימון bInheritHandle מתאר תכונה נוספת של ההפניה, ואינו זהה לפעולת השכפול עצמה.
לאחר סגירה, הערך המספרי יכול להיעשות שימוש חוזר. לכן בדיקה שהמשתנה אינו null אינה מוכיחה שה־handle עדיין שייך לאובייקט שהתכוונו אליו. המנגנון RAII מונע חלק גדול מהטעויות, אך שיתוף handle גולמי בין threads עדיין דורש חוזה חיים.
שם הוא דרך למצוא אובייקט¶
ניצור event עם שם מקומי ל־session. הקטע הבא מחליף את יצירת האירוע הראשונה, ומשתמש ב־PID כדי להקטין התנגשות בניסוי.
wchar_t name[128]{};
swprintf_s(name, L"Local\\AmitTech.Event.%lu", GetCurrentProcessId());
HANDLE first = CreateEventW(nullptr, TRUE, FALSE, name);
const DWORD creation_status = GetLastError();
if (!first) return 1;
if (creation_status == ERROR_ALREADY_EXISTS) {
CloseHandle(first);
return 5;
}
השם מאפשר לתהליך אחר באותו namespace לפתוח את האובייקט אם יש לו הרשאה. הוא אינו הוכחת זהות של היוצר ואינו תחליף לאבטחה. תהליך אחר יכול להקדים וליצור שם צפוי, ולכן קוד אמיתי צריך להגדיר מדיניות יצירה, פתיחה ואימות מתאימה.
ב־Win32 קיימות מוסכמות כמו Local ו־Global עבור אובייקטים מסוימים. בפנים הן מתחברות למרחבי שמות של Object Manager ול־sessions. אין להסיק שכל prefix עובד לכל API, או שיצירה ב־Global מבטלת בדיקות הרשאה.
ספירת handles אינה כל ספירת החיים¶
קיים הבדל בין handles של תהליכים לבין הפניות פנימיות שהקרנל מחזיק. פעולה במערכת יכולה להחזיק reference לאובייקט גם כאשר היישום סגר handle מסוים. לכן מספר handles אינו תמיד כל הסיפור של lifetime.
בחקירת קרנל אפשר לראות כותרות אובייקטים ומונים באמצעות symbols וכלי debugger מתאימים. ה־offsets והייצוג משתנים בין builds, ואינם מיועדים לכך שאפליקציה תחשב מצביע קרנל מתוך handle.
גם סוג האובייקט חשוב. מנהל האובייקטים יכול לבדוק שההפניה מתאימה לפעולה, והזכויות שניתנו ל־handle יכולות להגביל מה מותר לעשות דרכו. פתיחת אותו אובייקט פעמיים עם זכויות שונות יוצרת שתי יכולות שימוש שונות.
סגירה אינה תמיד ביטול עבודה¶
סגירת handle מורידה בעלות על הפניה. היא אינה בהכרח מבטלת כל פעולה אסינכרונית שכבר יצאה לדרך, ואינה הופכת buffers שעדיין בשימוש לחופשיים. בפרק I/O נצטרך להפריד סגירה, ביטול והשלמה.
גם סגירת handle בזמן ש־thread אחר ממתין עליו היא הפרת חוזה במקרים שבהם התיעוד אינו מתיר זאת. הפתרון הוא פרוטוקול shutdown שמסמן סיום וממתין למשתמשים, ולא סגירה בתקווה להעיר את כולם.
מאבחנים דליפה לפי אחריות¶
אם מספר handles גדל בכל בקשה, מחפשים את הפעולה שרוכשת handle ואת כל מסלולי היציאה. אם המספר נשאר יציב אך האובייקט חי, בודקים גם הפניות אחרות ועבודה פעילה. כלי כמו Process Explorer מסייע למצוא סוגים ושמות, אך אינו מחליף הבנת בעלות.
המודל שימושי לכל המשך הקורס: שם מוצא אובייקט, handle נותן גישה בהקשר מסוים, והרשאות וחיי references קובעים מה אפשר לעשות ועד מתי.
לקריאה נוספת: מנהל האובייקטים של Windows, שכפול handles ויצירה ופתיחה של event.