לדלג לתוכן

6.4 ניהול זכרון תרגול

תרגול - ניהול זכרון

תרגיל 1 - חקירת buddy allocator

הריצו את הפקודה הבאה ובחנו את הפלט:

cat /proc/buddyinfo

א. כמה zones יש במערכת שלכם? מהם השמות שלהם?

ב. מה המספר הראשון בכל שורה מייצג? ומה המספר האחרון?

ג. חשבו: אם המספרים בעמודות הגבוהות (8, 9, 10) הם כולם 0, מה זה אומר על מצב הזכרון הפיזי?


תרגיל 2 - חקירת /proc/meminfo

הריצו:

cat /proc/meminfo

א. מצאו את השדות הבאים ורשמו את הערכים שלהם: MemTotal, MemFree, MemAvailable, Cached, SwapTotal, SwapFree.

ב. הסבירו: למה MemAvailable גדול מ-MemFree? מה ההבדל ביניהם?

ג. אם Cached מראה ערך גבוה (למשל כמה GB), האם זה אומר שיש בעיית זכרון? הסבירו.


תרגיל 3 - ניתוח /proc/[pid]/maps

כתבו תוכנית C פשוטה שמקצה זכרון עם malloc, ממפה קובץ עם mmap, ואז קוראת ל-getchar() כדי להמתין (שנוכל לבדוק את ה-maps בזמן שהתוכנית רצה):

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    // הקצאה עם malloc
    char *heap_mem = malloc(1024 * 1024);  // 1MB

    // מיפוי אנונימי עם mmap
    char *anon_mem = mmap(NULL, 4096 * 10, PROT_READ | PROT_WRITE,
                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    printf("PID: %d\n", getpid());
    printf("heap_mem: %p\n", heap_mem);
    printf("anon_mem: %p\n", anon_mem);
    printf("press enter to exit...\n");
    getchar();

    free(heap_mem);
    munmap(anon_mem, 4096 * 10);
    return 0;
}

א. קמפלו והריצו את התוכנית. בטרמינל אחר, הריצו:

cat /proc/<PID>/maps

מצאו את האזורים שמתאימים ל-heap_mem ול-anon_mem. איך זיהיתם אותם?

ב. מצאו את אזור ה-stack. מה ההרשאות שלו?

ג. מצאו את libc.so ב-maps. כמה VMAs יש לה? מה ההרשאות של כל אחד ולמה?


תרגיל 4 - צפייה ב-Copy-on-Write

כתבו תוכנית שמקצה buffer גדול (למשל 100MB), ממלאת אותו בתו כלשהו, ואז קוראת ל-fork.

לפני ואחרי ה-fork, הדפיסו את ה-PID ועצרו עם getchar. בזמן ההמתנה, השוו את /proc/<PID>/maps של האב והבן.

אחר כך, בתהליך הבן, שנו בית אחד ב-buffer ושוב עצרו. בדקו שוב את ה-maps.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    size_t size = 100 * 1024 * 1024;  // 100MB
    char *buf = malloc(size);
    memset(buf, 'A', size);

    printf("parent PID: %d, press enter to fork...\n", getpid());
    getchar();

    pid_t pid = fork();

    if (pid == 0) {
        // תהליך הבן
        printf("child PID: %d, press enter to modify memory...\n", getpid());
        getchar();

        // שינוי בית אחד - מפעיל COW
        buf[0] = 'B';

        printf("child modified memory, press enter to exit...\n");
        getchar();
        _exit(0);
    }

    // תהליך האב
    printf("parent waiting for child...\n");
    wait(NULL);
    free(buf);
    return 0;
}

בכל שלב, בדקו את /proc/<PID>/smaps או /proc/<PID>/status ומצאו את השדה RssAnon. מה משתנה אחרי שהבן כותב לזכרון?


תרגיל 5 - שאלה תיאורטית

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

ציינו את כל השלבים: מהרגע שהCPU מנסה לגשת לכתובת, דרך ה-page fault, חיפוש VMA, הקצאת דף פיזי, עדכון page table, ועד שהפקודה רצה שוב בהצלחה.

מה ההבדל אם הכתובת הייתה של קובץ ממופה (file-backed mmap) במקום מיפוי אנונימי?