ניהול זיכרון

ניהול זיכרון הוא סוג של ניהול משאבים המיוחס לזיכרון המחשב (RAM). הדרישה המרכזית מניהול זיכרון היא לאפשר הקצאה דינאמית של חלקים מהזיכרון עבור תוכנות, בהתאם לדרישתן, ולשחרר לשימוש מחדש כשאין בו צורך יותר. ניהול משאבי הזיכרון הוא קריטי לכל מערכת מחשוב מתקדמת, המשרתת מספר תהליכים. ניהול זיכרון באופן יעיל יכול להשפיע באופן ניכר על ביצועי המערכת.

קיימות מספר שיטות לניהול זיכרון יעיל. שיטה נפוצה היא זיכרון וירטואלי, המפרידה את כתובות הזיכרון החשופות לתהליך (כתובות וירטואליות) מכתובות זיכרון שחושף המעבד (כתובות פיזיות). הפרדה זו מאפשרת בידוד והגנה בין תהליכים שונים ומגדילה את מרחב הזיכרון החשוף לתהליך מעבר לגודל הזיכרון הזמין. זאת באמצעות דפדוף או החלפה עם זיכרון משני, אשר נגיש פחות למעבד.

בכמה מערכות הפעלה, כמו OS/360 לדוגמה, זיכרון מנוהל על ידי מערכת ההפעלה. במערכות הפעלה אחרות, לדוגמה דמויות יוניקס, הזיכרון מנוהל ברמת האפליקציה.

 ניהול זיכרון ידני 
בקשה להקצאת זיכרון דורשת איתור של אזור זיכרון רציף (לעיתים נקרא בלוק) פנוי בגודל המבוקש. בקשות זיכרון מסופקות על ידי הקצאת כמות מתוך מקטע הזיכרון של הערימה (חלק ממקטעי הזיכרון). בכל זמן נתון, חלק מזיכרון הערימה נמצא בשימוש וחלק אינו בשימוש, כלומר בעזרת החלק הפנוי (שאינו נמצא בשימוש) ניתן לספק בקשות הקצאת זיכרון עתידיות.
ממוזער|אילוסטרציה לבעיית הקיטוע חיצוני
ישנם מספר אתגרים בניהול זיכרון הערימה, לדוגמה קיטוע חיצוני, אשר מתעוררת כאשר ישנם מרווחים רבים בין גושי זיכרון שהוקצו. בקשות להקצאת זיכרון בגודל מסוים לא יכולות להשתמש במרווחים קטנים מגודל הבקשה כי ההקצאה חייבת להיות רציפה. בנוסף המטא דאטה של הקצאה יכול להגדיל את כמות הזיכרון הנדרשת עבור דרישת זיכרון קטנה יחסית. בעיה זו יכולה להיות מטופלת באמצעות chunking.

ניהול הזיכרון חייב לכלול ולעקוב אחר בקשות הקצאה, כדי להבטיח שהם לא יחפפו ושלא יאבד זיכרון לאף תהליך/אפליקציה (בעצם, שלא יהיו דליפות זיכרון).

 יעילות 
אלגוריתם הקצאת הזיכרון הדינאמי יכול להשפיע משמעותית על ביצועי המערכת. מחקר שנערך ב-1994 על ידי DEC מדגים את התקורה הכרוכה במגוון אלגוריתמי הקצאה. הממוצע הנמוך ביותר של מספר פקודות המכונה הדרוש (Instruction path length) כדי להקצות משבצת זיכרון בודדת הוא 52, כפי שנמדד בעזרת profiler (מנגנון אנליזת תוכנה דינמי), אשר בחן מגוון תוכנות.

 מימושים 
בגלל חוסר היכולת לדעת מראש את המיקום המדויק של הזיכרון שיוקצה, הגישה לזיכרון היא עקיפה, לרוב בעזרת מצביע או רפרנס. האלגוריתם הספציפי המקצה או משחרר בלוקים הוא לרוב חלק מליבת מערכת ההפעלה, אלגוריתמים לדוגמה:

 הקצאת בלוקים בגודל קבוע 
הקצאה מסוג זה, הנקרא גם הקצאת בריכת זיכרון, משתמשת ברשימת בלוקים פנויים (אשר לא הוקצו), בעלי גודל קבוע (לרוב כולם באותו גודל). שיטה זו עובדת היטב עבור מערכות משובצות מחשב פשוטות, בהן אין צורך בהקצאת אובייקטי זיכרון גדולים. אבל שיטה זו סובלת מקיטוע, כלומר בזבוז זיכרון בתוך הבלוק המוקצה לאובייקטים קטנים יותר. למרות זאת, בעקבות התקורה הנמוכה יחסית, שיטה זו יכולה לשפר משמעותית את הביצועים עבור אובייקטים שזקוקים להקצאה/שחרור באופן תכוף ונמצאת בשימוש נפוץ במשחקי וידאו.

 בלוק חבר 
בשיטה זו, הקצאת הזיכרון מתבצעת ממספר בריכות זיכרון (במקום רק אחת), כאשר כל בריכה מייצגת בלוקי זיכרון בגודל מסוים (ושונה) שהוא חזקה של שתיים. ייתכנו גם בריכות שמאופיינות בגודלי בלוקים נוחים אחרים.

כל הבלוקים מגודל ספציפי, נשמרים ברשימה מקושרת ממויינת או עץ וכל הבלוקים החדשים שנוצרים בזמן הקצאות הזיכרון נוספים לבריכת הזיכרון המתאימה (לפי גודלם) לשימוש עתידי. אם מבוקשת הקצאת זיכרון בעלת גודל קטן מהגודל המינימלי הזמין, נבחר הגודל המינימלי והבלוק הספציפי מפוצל. אחד מהחלקים שפוצלו נבחר והתהליך ממשיך עד שבקשת ההקצאה נענית עד תום.

כאשר בלוק מוקצה, האלגוריתם יתחיל עם גודל הבלוק הקטן ביותר שגדול דיו כדי להימנע מפירוק בלוקים מיותר. כאשר בלוק משוחרר, משווים אותו לבלוק החבר. אם שניהם משוחררים, הם מאוחדים יחדיו וימוקמו במיקום רשימת הבלוקים (הגדולה יותר) אשר מתאימה לגודלם החדש.

 הקצאת אריח (מטמון) 
כאשר טיפוס וגודל האובייקטים שאותם רוצים להקצות ידועים מראש, ניתן להשתמש במנגנון ה-slab (אריחים). בשיטה זו מקצים מראש "מטמונים", המכילים מספר רב של אריחים פנויים, כאשר כל אריח הוא בדיוק בגודל האובייקטים המבוקשים. בניית אובייקט כלשהו תקצה לעצמה אריח פנוי כלשהו מתוך המטמון ואילו מחיקה של אובייקט תוסיף את האריח שהיה בשימוש בחזרה למטמון. אלגוריתם ניהול הזיכרון צריך לבצע מעקב אחר רשימת המשבצות/החריצים הפנויים במטמון. שיטת ניהול זיכרון זו פותרת את בעיית הפרגמנטציה מכיוון שכל אריח ברשימה הוא בדיוק בגודל המתאים.

 הקצאה על המחסנית 
בהרבה מערכות דמויות יוניקס וגם בחלונות של Microsoft מממשים פונקציה הנקראת alloca, עבור הקצאת זיכרון מחסנית דינאמי, בצורה הדומה מאוד לפונקציה malloc של שפת C המיועדת להקצאה על הערימה (heap). המהדר, באופן טיפוסי, יתרגם זאת לפקודות אינליין אשר מבצעות מניפולציות במצביע המחסנית.

על אף שאין צורך בשחרור ידני של זיכרון המוקצה באופן זה, מכיוון שהוא משוחרר באופן אוטומטי כאשר הפונקציה שקראה לalloca "חוזרת", קיים סיכון של גלישת חוצץ (Buffer overflow). מכיוון שalloca היא פונקציית הרחבה נפוצה יחסית לניהול הזיכרון במחסנית וקיימת במערכות רבות (אך לא ב-POSIX או בשפת C), ההתנהגות שלה במקרה של גלישת חוצץ אינה מוגדרת.

גרסה בטוחה יותר של alloca הנקראת malloca_, אשר מדווחת על שגיאות, קיימת ב-Microsoft Windows. היא דורשת את השימוש בfreea_. גם gnulib מאפשרת ממשק שקול ובמקום לזרוק חריגה (פסיקת) SEH לגבי גלישת חוצץ, הוא מפקיד/מאציל את הקצאת הזיכרון לפונקציית הערימה malloc כאשר הספרייה נתקלת בגודל הקצאה גדול מהיכולת.

אפשר להתחקות אחר תכונה זו באופן דומה על ידי ביצוע חשבון ידני ובדיקת גודל, כמו שימושים בalloca_account ב-glibc.

 ניהול זיכרון אוטומטי 
במספר מימושים של שפות תכנות, לדוגמה שפת ג'אווה, בעת קריאה לשגרה, סביבת זמן הריצה של התוכנית מקצה באופן אוטומטי זיכרון במחסנית הקריאות עבור משתנים מקומיים שאינם סטטיים בשגרה/פונקציה, אלו קרויים גם משתנים אוטמטיים. באופן דומה, משתנים אלו משחררים את מקומם בזיכרון באופן אוטומטי כאשר השגרה חזרה/יצאה.

הצהרות משתנים מיוחדות, יכולות לאפשר למשתנים מקומיים לשמור את ערכם בין קריאות לשגרה, או לאפשר למשתנים מקומיים להיות נגישים על ידי שגרות/פונקציות אחרות. הקצאת הזיכרון האוטומטית של משתנים מקומיים מאפשרת לרקורסיה להיות אפשרית עד לעומק המוגבל על ידי הזיכרון הזמין/פנוי לשימוש.

 שיטת איסוף זבל 

שיטת איסוף זבל היא אסטרטגיה לזיהוי זיכרון שהוקצה (באופן אוטומטי), עבור אובייקטים שאינם שמישים יותר בתוכנית והחזרת הזיכרון הזה, שבפועל כבר לא יכול להימצא בשימוש על ידי התוכנית, לבריכת הזיכרון הפנוי. השיטה הזו, בניגוד לניהול זיכרון "ידני", מקלה על המתכנתים והם אינם נדרשים להקצות ולשחרר זיכרון באופן מפורש במהלך התוכנית, וכך יכולה למנוע באגים בניהול הזיכרון. עם זאת, שיטה זו דורשת זיכרון משל עצמה ומעצם כך לוקחת משאבי זיכרון מלכתחילה (שאותם היא מנסה לחסוך). בנוסף לכך היא גם עלולה להתחרות עם התוכנה, לה היא מנסה לעזור בניהול הזיכרון, על זמן ריצה על המעבד.

 מערכות עם זיכרון וירטואלי 
זיכרון וירטואלי היא שיטה להפרדה בין ארגון הזיכרון, לחומרה פיזית (זיכרון פיזי). אפליקציות ותוכנות פועלות ומשתמשות בזיכרון בעזרת כתובות זיכרון וירטואליות. כל ניסיון של האפליקציה לגשת לכתובת זיכרון וירטואלית ספציפית גורמת למעבד לתרגם את כתובת הזיכרון הווירטואלית לכתובת זיכרון פיזית ממשית. בצורה זו מערכת ההפעלה מגבילה את האופן שבו תהליך כלשהו יכול לגשת לזיכרון. תכונה זו, הנקראת הגנת זיכרון, מונעת מתהליך לקרוא או לכתוב לזיכרון אשר אינה הוקצה עבורו, ובכך למנוע מקוד זדוני או לא תקין בתוכנית אחת להפריע לפעולתה התקינה של תוכנית אחרת.

על אף שבאופן רגיל הזיכרון אשר מוקצה לתהליכים ספציפיים בדרך כלל מבודד, תהליכים לעיתים זקוקים ליכולת לשתף מידע ביניהם. זיכרון משותף הוא אחת הטכניקות המהירות יותר עבור תקשורת בין תהליכים.

זיכרון לרוב מסווג על ידי קצב הגישה אליו, לשתי קטגוריות - אחסון ראשי ואחסון המשני. מערכות ניהול זיכרון, בנוסף לפעולות אחרות, גם מטפלות בהעברת מידע בין שתי רמות זיכרון אלו.

 ניהול זיכרון במערכות OS/360 ובמערכות מבוססות בהמשך 
מערכת IBM/360 לא תומכת בזיכרון וירטואלי. בידוד זיכרון בין תהליכים מושג לעיתים בעזרת שימוש במפתחות הגנה, המקצים מקום אחסון ומפתח שונה עבור כל תהליך, כאשר המקום 0 מוקצה לתהליך המשגיח על המערכת ולשאר התהליכים נשארים המפתחות 1–15. התהליך המשגיח הוא האחראי על ניהול הזיכרון של כל שאר התהליכים במערכת. תהליך יכול לבקש זיכרון בעזרת שימוש במאקרו GETMAIN, ולשחרר אותו בעזרת שימוש במאקרו FREEMAIN. אלו מביאים לקריאה לתהליך המשגיח (SVC) אשר מבצע את הפעולה. פרטי המימוש המדויקים משתנים כתלות באופן יצירת המערכת, למשל PCP, MFT, MVT (כולן מערכות מבוססות OS/360).

במערכת OS/360 MVT, הקצאת משנה בתוך אזור תהליך או אזור תור המערכת (SQA) מבוססת על בריכות משניות, אזורים שהם מכפלות של 2KB בגודלן – גודל אזור בריכת משנה המוגן על ידי מפתח הגנה. בריכות משנה ממוספרות מ-0 עד 255. בתוך אזור, לבריכות משנה מוקצת הגנת האחסון של התהליך או המפתח של המשגיח, מפתח 0. בריכות משנה 0–127 מקבלות את מפתח ההגנה של התהליך. בתחילה, רק בריכת משנה אפס נוצרת, וכל בקשות אחסון על ידי המשתמש נענות על ידי בריכת משנה 0, אלא אם כן מצוין מספר בריכת משנה אחרת בבקשת הזיכרון. בריכות משנה 250–255 נוצרות בעזרת בקשות זיכרון על ידי המשגיח אך מטעם התהליך. לרוב הבקשות הללו מוקצה מפתח 0, על אף שחלק מקבלות את המפתח של התהליך. מספרי בריכות המשנה רלוונטיות גם בMFT, אך שם הפרטים הרבה יותר פשוטים. MFT משתמש במחיצות קבועות אשר ניתנות להגדרה מחדש על ידי המפעיל, במקום אזורים דינמיים, ולPCP יש בכלל רק מחיצה אחת.

כל בריכת משנה ממופה על ידי רשימה של בלוקי שליטה המזהים בלוקי זיכרון מוקצים ופנויים בתוך בריכת המשנה. זיכרון מוקצה על ידי מציאת אזור פנוי בגודל מספק, או על ידי הקצאת בלוקים נוספים בתוך בריכת המשנה, לכל היותר עד לגודל אזור התהליך הכולל. אפשרי לפנות את כל או חלק מאזור זיכרון מוקצה.

הפרטים במימוש של OS/SV1 דומים לאלו של MFT ושל MVT; הפרטים עבור OS/VS2 זהים לאלו של MFT, מלבד העובדה שגודל דף זיכרון הוא 4KB. עבור OS/VS1 וגם עבור OS/VS2 אזור תור המערכת המשותף (SQA) לא ניתן לחלוקה לדפי זיכרון.

ב-MVS, מרחב הכתובות כולל אזור משותף נוסף אשר ניתן חלוקה לדפי זיכרון, אשר נקרא אזור האחסון המשותף (CSA), וכן אזור פרטי נוסף, אזור התהליך של המערכת (SWA). כמו כן, מפתחות הזיכרון 0–7 כולם שמורים לשימוש של קוד בעל הרשאות גבוהות.

 קישורים חיצוניים 

 הערות שוליים 

2023 03
קטגוריה:זיכרון מחשב
קטגוריה:מדעי המחשב
קטגוריה:מערכת הפעלה