【问题标题】:How does memory allocation happen at the lowest level in an operating system?内存分配如何在操作系统的最低级别发生?
【发布时间】:2019-09-20 06:57:36
【问题描述】:

我试图弄清楚在操作系统的最低级别是如何分配内存的。据我所知,操作系统只是在记录可用和不可用的内存,而 C 编程语言将在最低级别进行分配。

所以,第一个例子是我想出的一个简单的内存分配系统,然后我从以下资源中举了一个例子:https://github.com/levex/osdev

示例 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 MB 标记处开始分配内存。是的,我知道我的内核在内存中的位置很奇怪且不传统,但我知道超过 1 MB 标记后内存是空闲的。

所以,如果我知道以下内容:

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 内存地址附近,但不是。这是一些完全不同的地方,这就是为什么我认为我没有在物理上告诉 char 指针在内存中的位置,而是 C 编程语言将它放在不同的地方。我无法弄清楚我做错了什么,理解这一点应该不难。另外,我查看了 OSDev Wiki 并没有找到任何东西。

【问题讨论】:

  • 现在,最低级别是virtual memory manager
  • 我想在开始分页之前实现这个。
  • 请注意char * ptr = malloc(5); printf("The memory address for this pointer is at: %d\n", &amp;ptr); 不正确有两个原因:错误的格式说明符,它传递的是指针的位置,而不是它的值。应该是printf("%p\n", (void*)ptr);
  • 在某些系统中,您可能会“逃避”使用错误的格式说明符,但通常情况下,如果 int* 占用 8 个字节而 int 占用 4 个字节,您将不会不要报告正确的地址。

标签: c memory memory-management x86 operating-system


【解决方案1】:

我试图弄清楚内存是如何在最低级别分配的 在操作系统中。据我所知,操作 系统只是在做可用内存的簿记 并且不可用,它是 C 编程语言会做 最低级别的分配。

操作系统确实会记录哪些内存可用和哪些不可用,但将其放在这些术语中会大大简化,我怀疑您对“内存”的含义有不同的理解。

操作系统的虚拟内存管理子系统管理物理内存和其他存储资源(例如基于磁盘的交换空间)如何映射到每个进程的虚拟地址空间,包括多少以及一个进程的哪些部分进程的虚拟地址空间映射到可用内存。它服务于增加和减少进程可用虚拟内存的请求,以及创建内存映射的显式请求,例如基于普通文件的那些。

就用户空间程序中的malloc() 调用而言,您或多或少是正确的。程序通常以相当大的块从操作系统获取内存,malloc()free() 和朋友们瓜分并管理。通常,这些细节只有在进程填满了它已经可用的内存并且需要向内核请求更多时才会涉及内核。

但最低层肯定是在内核中。 C 库的内存管理功能只能使用操作系统分配给进程的内存。

从这两个示例中,我预计我从 malloc() 分配的内存 将具有与我分配它的位置相同的起始地址,如果 这就说得通了?如果我知道我的内核的结尾在 0x9000 标记,我想在 1 MB 标记处开始分配内存。是的,我 知道我的内核在内存中的位置很奇怪而且不传统,但我 知道内存超过 1 MB 标记后是空闲的。

内核对内存的看法与用户空间进程不同。每个进程都在自己的虚拟地址空间中运行,无法查看它正在使用的物理地址。

【讨论】:

  • 在 POSIX 操作系统上,malloc 实现通常使用 mmap(MAP_ANONYMOUS) 和/或 brk() / sbrk() 从内核获取新页面(glibc 使用 brk 进行小分配,@987654329 @ 对于大的,所以它总是可以在free 上将内存返回给内核,而不是在用户空间空闲列表上分配大量内存)。在 Windows 上,VirtualAlloc()
【解决方案2】:

我试图弄清楚在操作系统的最低级别是如何分配内存的。

您的问题是您没有查看操作系统如何分配内存。您正在查看应用程序级别的内存分配。

对于进程,操作系统仅在 PAGES 中分配内存。进程通过调用将更多页面映射到进程地址(即使更多页面有效)的系统服务来获得更多内存。

因为应用程序通常需要小于一页的内存分配(例如字符串),所以经常使用堆管理函数。 malloc 和 free 很少(如果有的话)操作系统服务。它们只是可以以小于页面的增量分配内存的简单函数。

通常,对 malloc 的调用会导致函数试图找到足够大的内存块以返回给调用者。如果这样的块不可用,malloc 会调用操作系统服务将页面映射到地址空间,以增加堆中的可用内存量,从而可以返回足够大的内存块。

【讨论】:

    猜你喜欢
    • 2016-08-06
    • 1970-01-01
    • 2021-10-16
    • 2017-07-11
    • 2010-09-12
    • 2012-10-29
    • 1970-01-01
    • 2012-07-09
    • 1970-01-01
    相关资源
    最近更新 更多