Как происходит распределение памяти на самом низком уровне в операционной системе? - PullRequest
1 голос
/ 01 мая 2019

Я пытаюсь выяснить, как выделяется память на самом низком уровне в операционной системе.Из того, что я могу понять, является то, что операционная система просто ведет учет памяти, которая доступна и недоступна, и это язык программирования C, который будет выполнять распределение на самом низком уровне.

Итак, первый пример - это то, что я придумал как простая система выделения памяти, а затем я взял пример из следующего ресурса: https://github.com/levex/osdev.

Example-1:

    struct heap_elements {
        int start_address;
        int end_address;
        int size;
        int reservation;
    };

    struct heap_elements heap[25];

    // Write len copies of val into dest.
    void memset(int *dest, int val, int len)
    {
        int *temp = (int *)dest;
        for ( ; len != 0; len--) *temp++ = val;
    }

    /*
    * This function will take a source and destination and copy n amount
    * - of bytes from the source to the destination address. 
    */ 
    void memory_copy(unsigned char *source, unsigned char *destination, int bytes) {
        for (int i = 0; i < bytes; i++) {
            *(destination + i) = *(source + i);
        }
    }

    int find_memory_hole(int size) {

        for (int i = 0; i < total_elements; i++) {
            if (heap[i].reservation == 0) {
                if (heap[i].size >= size || heap[i].size == 0) {
                return i;
                }
            }
        }
        return -1;
    }

    int * malloc(int size) {   
        int hole = find_memory_hole(size);
        if (hole != -1) {
            if (heap[hole].start_address == 0) {
                heap[hole].start_address = ending_address;
                ending_address += size;
                heap[hole].end_address = ending_address;
                heap[hole].size = size;
                heap[hole].reservation = 1;
                kprintf("Starting address: %d\n", heap[hole].start_address);
                kprintf("Ending address: %d\n", heap[hole].end_address);
            } else {
                heap[hole].size = size;
                heap[hole].reservation = 1;
            }
            memset((int*)heap[hole].start_address, 0, size);
            return (int*)heap[hole].start_address;
        } else {
            kprintf("FREE SOME MEMORY~!\n");
            kprintf("WE NEED ROOM IN HERE~!\n");
            return 0;
        }
    }

    void heap_install() {
        total_elements = 25;
        starting_address = 0x100000;  // 1 - MB
        ending_address = 0x100000;    // 1 - MB
        max_memory_address = 0xEEE00000;  // 4 - GB

        for (int i = 0; i < total_elements; i++) {
            heap[i].start_address = 0;
            heap[i].end_address = 0;
            heap[i].size = 0;
            heap[i].reservation = 0;
        }

        return;
    }

    void free(void * pointer) {

        int memory_found = 0;
        kprintf("Address %d\n", &pointer);
        int memory_address = &pointer;

        for (int i = 0; i < total_elements; i++) {
            if (heap[i].start_address == memory_address) {
                heap[i].size = 0;
                heap[i].reservation = 0;
                memory_found = 1;
                break;
            }
        }

        if (memory_found == 0)
            kprintf("Memory could not bee free'd (NOT FOUND).\n");

        return;
    }

Пример-2:

    void mm_init(unsigned kernel_end)
    {
        kprintf("The kernel end is: %d\n", kernel_end);
        last_alloc = kernel_end + 0x1000;   // Set our starting point.
        heap_begin = last_alloc;
        heap_end = 0x5B8D80;                // Set the bar to 6 MB
        memset((char *)heap_begin, 0, heap_end - heap_begin);
    }

    void mm_print_out()
    {
        kprintf("Memory used: %d bytes\n", memory_used);
        kprintf("Memory free: %d bytes\n", heap_end - heap_begin - memory_used);
        kprintf("Heap size: %d bytes\n", heap_end - heap_begin);
        kprintf("Heap start: 0x%x\n", heap_begin);
        kprintf("Heap end: 0x%x\n", heap_end);
    }

    void free(void *mem)
    {
        alloc_t *alloc = (mem - sizeof(alloc_t));
        memory_used -= alloc->size + sizeof(alloc_t);
        alloc->status = 0;
    }

    char* malloc(unsigned size)
    {
        if(!size) return 0;

        /* Loop through blocks and find a block sized the same or bigger */
        unsigned char *mem = (unsigned char *)heap_begin;
        while((unsigned)mem < last_alloc)
        {
            alloc_t *a = (alloc_t *)mem;
            /* If the alloc has no size, we have reaced the end of allocation */

            if(!a->size)
                goto nalloc;
            /* If the alloc has a status of 1 (allocated), then add its size
            * and the sizeof alloc_t to the memory and continue looking.
            */
            if(a->status) {
                mem += a->size;
                mem += sizeof(alloc_t);
                mem += 4;
                continue;
            }
            /* If the is not allocated, and its size is bigger or equal to the
            * requested size, then adjust its size, set status and return the location.
            */
            if(a->size >= size)
            {
                /* Set to allocated */
                a->status = 1;
                memset(mem + sizeof(alloc_t), 0, size);
                memory_used += size + sizeof(alloc_t);
                return (char *)(mem + sizeof(alloc_t));
            }
            /* If it isn't allocated, but the size is not good, then
            * add its size and the sizeof alloc_t to the pointer and
            * continue;
            */
            mem += a->size;
            mem += sizeof(alloc_t);
            mem += 4;
        }

        nalloc:;
        if(last_alloc+size+sizeof(alloc_t) >= heap_end)
        {
            panic("From Memory.c", "Something", 0);
        }
        alloc_t *alloc = (alloc_t *)last_alloc;
        alloc->status = 1;
        alloc->size = size;

        last_alloc += size;
        last_alloc += sizeof(alloc_t);
        last_alloc += 4;

        memory_used += size + 4 + sizeof(alloc_t);
        memset((char *)((unsigned)alloc + sizeof(alloc_t)), 0, size);
        return (char *)((unsigned)alloc + sizeof(alloc_t));

    }

В обоих примерах я ожидал, что память, которую я выделил из malloc (), будет иметь тот же начальный адрес, что и для того, где я его выделил,если это имеет смысл?Если я знаю, что конец моего ядра находится на отметке 0x9000, и я хочу начать выделять память с отметки 1 МБ.Да, я знаю, где находится мое ядро ​​в памяти, странно и не совсем обычно, но я знаю, что память свободна после отметки в 1 МБ.

Итак, если я знаю следующее:

kernel_end = 0x9000;
heap_starts = 0x100000;
heap_ends = 0x5B8D80;

Я бы ожидал, что это:

char * ptr = malloc(5)

printf("The memory address for this pointer is at: %d\n", &ptr);

Будет около адреса памяти 0x100000, но это не так.Это кое-где совершенно иное, и поэтому я думаю, что я физически не говорю, где указатель на символ должен находиться в памяти, и что это язык программирования C, где-то иначе.Я не могу понять, что я делаю неправильно, и это не должно быть так сложно понять.Также я заглянул в OSDev Wiki и ничего не нашел.

Ответы [ 2 ]

5 голосов
/ 01 мая 2019

Я пытаюсь выяснить, как выделяется память на самом низком уровне в операционной системе. Из того, что я могу собрать, является то, что операционная Система просто делает учет доступной памяти и не доступны, и это язык программирования C, который будет делать распределение на самом низком уровне.

Операционная система, безусловно, ведет учет того, какая память доступна, а какая нет, но, говоря об этих терминах, она значительно упрощает, и я подозреваю, что у вас есть другое представление о том, что означает здесь «память», чем лучше всего подходит.

Подсистема управления виртуальной памятью ОС управляет тем, как физическая память и другие ресурсы хранения, такие как дисковое пространство подкачки, отображаются в виртуальное адресное пространство каждого процесса, включая , сколько и какие части процесса виртуальное адресное пространство отображается в используемую память. Он обслуживает запросы на увеличение и уменьшение доступной виртуальной памяти процесса, а также явные запросы на создание отображений памяти, например, основанных на обычных файлах.

Что касается обслуживания вызовов malloc() в пользовательской программе, вы более или менее правы. Программы обычно получают память от ОС в виде значительных блоков, которые malloc(), free() и друзья разделяют и управляют ими. Как правило, эти детали затрагивают ядро ​​только тогда, когда процесс заполняет память, уже доступную ему, и должен запросить у ядра больше.

Но самый низкий уровень определенно в ядре. Функции управления памятью библиотеки C могут работать только с памятью, выделенной процессу ОС.

Из обоих примеров я ожидал, что память, выделенная из malloc () будет иметь тот же начальный адрес, что и в том месте, где я его выделил, если в этом есть смысл? Если я знаю, что конец моего ядра на 0x9000 и я хочу начать выделять память с отметки 1 МБ. Да я знаю, где мое ядро ​​в памяти странно и не обычное, но я знать, что память свободна после отметки 1 МБ.

Взгляд ядра на память отличается от процессов в пользовательском пространстве '. Каждый процесс выполняется в своем собственном виртуальном адресном пространстве, без какой-либо информации о том, какие физические адреса он использует.

1 голос
/ 02 мая 2019

Я пытаюсь выяснить, как выделяется память на самом низком уровне в операционной системе.

Ваша проблема в том, что вы не смотрите, как операционная система распределяет память. Вы просматриваете распределение памяти на уровне приложения.

Для процессов операционная система выделяет память только в PAGES. Процесс получает больше памяти, вызывая системную службу, которая отображает больше страниц в адрес процесса (т.е. делает больше страниц действительными).

Поскольку приложениям обычно требуется выделение памяти меньше, чем страница (для таких вещей, как строки), часто используются функции управления кучей. malloc и free редко (если вообще когда-либо) являются службами операционной системы. Это просто функции, которые могут выделять память с шагом, меньшим, чем страница.

Как правило, при вызове malloc функция пытается найти блок памяти, достаточно большой, чтобы вернуться к вызывающей стороне. Если такой блок недоступен, malloc вызовет службу операционной системы для сопоставления страниц в адресном пространстве, чтобы увеличить объем доступной памяти в куче, чтобы он мог вернуть достаточно большой блок памяти.

...