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

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

כיצד מתבצעת הקצאת זיכרון?

רוב מהנדסי התוכנה אינם יודעים את הפרטים של תהליך זה. אבל אם אתה מועמד למתכנת מערכת, אתה צריך לדעת יותר על זה. כאשר מסתכלים על תהליך ההקצאה, יש צורך להיכנס לפרטים קטנים על לינוקס וה- glibc סִפְרִיָה.

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

ה malloc() משפחת הפונקציות אחראית על הקצאת זיכרון בשפת C. השאלה שיש לשאול כאן היא האם malloc(), כפונקציית glibc, מבצעת קריאה ישירה למערכת.

אין קריאת מערכת בשם malloc בליבת לינוקס. עם זאת, ישנן שתי קריאות מערכת לדרישות זיכרון של יישומים, שהן brk ו mmap.

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

instagram viewer

קריאת המערכת הראשונה: brk

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

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

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

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

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

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

להבנה טובה יותר, נסה להרכיב ולהפעיל את היישום לדוגמה הבא:

#לִכלוֹל <stdio.h>
#לִכלוֹל <stdlib.h>
#לִכלוֹל <unistd.h>
intרָאשִׁי(int argc, לְהַשְׁחִיר* argv[])
{
לְהַשְׁחִיר *ptr[7];
int n;
printf("Pid של %s: %d", argv[0], getpid());
printf("הפסקת תוכנית ראשונית: %p", sbrk (0));
עבור (n=0; נ<5; n++) ptr[n] = malloc (16 * 1024);
printf("לאחר 5 x 16kB malloc: %p", sbrk (0));
חינם(ptr[1]);
printf("לאחר חינם של 16kB שניות: %p", sbrk (0));
ptr[5] = malloc (16 * 1024);
printf("לאחר הקצאת ה-6 מתוך 16kB: %p", sbrk (0));
חינם(ptr[5]);
printf("לאחר שחרור הבלוק האחרון: %p", sbrk (0));
ptr[6] = malloc (18 * 1024);
printf("לאחר הקצאת 18kB חדש: %p", sbrk (0));
getchar();
לַחֲזוֹר0;
}

כאשר אתה מפעיל את היישום תקבל תוצאה דומה לפלט הבא:

Pid of ./a.out: 31990
תוכנית ראשונית לשבור: 0x55ebcadf4000
לאחר 5 x 16kB malloc: 0x55ebcadf4000
לאחר חינם של 16kB השני: 0x55ebcadf4000
לאחר הקצאת ה-6 מתוך 16kB: 0x55ebcadf4000
לאחר שחרור הבלוק האחרון: 0x55ebcadf4000
לאחר הקצאת א חָדָשׁ18kB: 0x55ebcadf4000

הפלט עבור brk עם strace יהיה כדלקמן:

brk(ריק) = 0x5608595b6000
brk (0x5608595d7000) = 0x5608595d7000

כפי שאתה יכול לראות, 0x21000 נוספה לכתובת הסיום של שדה הנתונים. אתה יכול להבין את זה מהערך 0x5608595d7000. אז בערך 0x21000, או 132KB של זיכרון הוקצה.

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

פריסת מרחב כתובת אקראית: ASLR

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

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

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

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

על ידי השבתה זמנית של תכונת ASLR עם הפקודה הבאה, נראה שיישום הבדיקה הקודם מחזיר את אותם ערכי כתובת בכל פעם שהוא מופעל:

הֵד0 | sudo tee /proc/sys/kernel/randomize_va_space

כדי לשחזר אותו למצבו הקודם, יספיק לכתוב 2 במקום 0 באותו קובץ.

קריאת המערכת השנייה: mmap

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

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

מכיוון שהקצאות זיכרון עם mmap איטיות מאוד בהשוואה לאלו עם brk, יש צורך בהקצאת brk.

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

החשיבות של הקצאת זיכרון בלינוקס

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

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

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

מבוא קצר לשפת התכנות C

קרא הבא

לַחֲלוֹקצִיוּץלַחֲלוֹקאימייל

נושאים קשורים

  • לינוקס
  • זיכרון מחשב
  • ליבת לינוקס

על הסופר

Fatih Küçükkarakurt (7 מאמרים שפורסמו)

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

עוד מאת Fatih Küçükkarakurt

הירשם לניוזלטר שלנו

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

לחץ כאן כדי להירשם