-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memory manager #621
Comments
You can use the
You can read the draft of the upcoming post here. It already explains Rust's allocator API and how to set up the Update: I just finished the first draft of the bump allocation section.
Try
Yeah, you typically use different allocator designs in the kernel that are much simpler. So porting an userspace allocator isn't the way to go in my opinion. I hope this helps! Let me know if you have any more questions. |
OK... so I found a buddy system allocator (ironically, its called buddy_system_allocator) and have slipped that into my kernel as rusts default allocator. Here's my issues.
// in src/main.rs
#[global_allocator]
static allocator: LockedHeap = LockedHeap::empty(); And in my main code, I do this: for region in boot_info.memory_map.iter() {
if region.region_type == MemoryRegionType::Usable {
unsafe {
allocator.lock().add_to_heap(region.range.start_addr() as usize, region.range.end_addr() as usize);
}
}
} The reason I do the type cast is because this function takes an "usize" as parameter, not u64. This naturally causes a page fault:
This, of course, isn't very helpful. So if I request it to dump the stack frame, I get this (slightly) more useful message:
And...
Also, adding a memory allocator to the crate (along with a heap) is far simpler this way. I'll outline the steps below.
use buddy_system_allocator::*;
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty(); What this does is set the global memory allocator that the rust (and the stdlib later on) will use as its default. It may or may not be your system allocator, but it will be your kernel allocator. The ::empty() method sets up the allocator and ensures it has nothing in it so we can populate it. for region in boot_info.memory_map.iter() {
if region.region_type == MemoryRegionType::Usable {
unsafe {
ALLOCATOR.lock().add_to_heap((boot_info.physical_memory_offset+region.range.start_addr()) as usize, (boot_info.physical_memory_offset+region.range.end_addr()) as usize);
}
}
} This loops through the memory map provided by the bootloader and loads it into the buddy system allocator, making your system memory available to the heap. I do not believe, however, that this makes your "pages" available. #[alloc_error_handler]
fn handle_alloc_failure(layout: core::alloc::Layout) -> ! {
panic!("Cannot allocate memory of min. size {} and min. alignment of {}", layout.size(), layout.align())
} This is primitive but effective. Try compiling your kernel and see if it page faults. It should not. extern crate alloc; Hello, DMA! Let's allocate a vec, shall we? use alloc::vec::Vec;
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3); Test that and see if you can print it! You've now got a memory allocator, albeit one that ignors your frame allocator completely. (I don't know how safe this is,and I don't know if it will clean up after itself or whether we still need to use the paging frame allocator.) This brings me back to one of my other problems -- how do I safely handle page faults? The OSDev wiki says:
I'm not really sure how to do this in rust? Could someone help me out with that part? |
Update: OK, so this issue isn't solved. I can get a temporary allocator working, but that's only if I increase the memory limit to 16 GB in QEMU the buddy system allocator throws an error and panics because the overall amount of indexes in the list of free blocks grows beyond 32. So I'm not really sure what to do.
|
While this works, I wouldn't recommend this approach. Instead, try creating a separate virtual heap memory area and mapping it: pub const HEAP_START: usize = 0x_4444_4444_0000;
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
pub fn init_heap(
mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
) -> Result<(), MapToError> {
let page_range = {
let heap_start = VirtAddr::new(HEAP_START as u64);
let heap_end = heap_start + HEAP_SIZE - 1u64;
let heap_start_page = Page::containing_address(heap_start);
let heap_end_page = Page::containing_address(heap_end);
Page::range_inclusive(heap_start_page, heap_end_page)
};
for page in page_range {
let frame = frame_allocator
.allocate_frame()
.ok_or(MapToError::FrameAllocationFailed)?;
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() };
}
unsafe {
super::ALLOCATOR.inner.lock().init(HEAP_START, HEAP_SIZE);
}
Ok(())
}
The page fault error code gets passed a
The simplest solution is to map the complete heap up front, as shown in the code example above. On alloc and dealloc, you only keep track of which parts are used and free, but do not map/unmap anything. (It's possible to dynamically grow the heap when it runs out of memory by mapping new pages, but I wouldn't recommend this for kernel code.)
I don't know anything about the buddy allocator crate, but you could try the |
I'll try this (I've switched to using the slab allocator that Redox has up on their github repo). The allocator requires that the "Heap size should be a multiple of minimum heap size". That's hardcoded to: pub const NUM_OF_SLABS: usize = 8;
pub const MIN_SLAB_SIZE: usize = 4096;
pub const MIN_HEAP_SIZE: usize = NUM_OF_SLABS * MIN_SLAB_SIZE; So, I could calculate the right page by using something like heap_size % 32768. The difficulty is locating a small page -- I would like it if the kernel operated in a tiny memory page (max should be 2-5 MB) so that it takes up as little RAM as possible to leave room for as many usermode programs as needed. |
AFAIK, the Redox kernel allocator is automatically growing, which makes things a bit more complicated. Also, the slab allocator uses the |
I don't think it auto-grows; I remember there being a function to grow it that was pub... so I don't know why it would auto-grow. That wouldn't make any sense. |
Seems like the slab allocator doesn't auto grow. The default linked list allocator, however, does: https://github.com/redox-os/kernel/blob/78e79fc4d629069a65c1b7c8f65231953db6c99c/src/allocator/linked_list.rs#L28-L42 |
Yeah, it does use the linked list allocator. Not sure of any way of turning the auto-growing feature off, unfortunately. If it amkes things more complex, I can probably live with it if its not ridiculously so. :) |
I meant make (git hub is not allowing me to edit my comments for some reason :() |
The linked list allocator itself doesn't do any auto-growing, just the linked list module of redox. So you should be fine |
Oh. Good to know. |
Trying to use your code in any way gives me "error[E0107]: wrong number of type arguments: expected 2, found 1". I'm passing it my frame allocator and mapper that I initialized and its still complaining, not sure what I'm doing wrong. (Other han the fact that I'm obviously bad at generic types in rust. :)) |
OK, fixed it. |
So, as I wrote above, I fixed it. The way I did it was: the slab allocator requires that the heap size be a multiple of the minimum heap size, or exactly 32 KB. Knowing this, I located the first unused memory region with the boot memory map, mapped it into the page table, then allocated it as my kernel heap (on anything above 5 MB of RAM its always between 1.5-1.6 MB or so). In that same for loop to iterate through free regions, I added a while loop that determined if the heap size was a multiple of 32 KB. If it wasn't, it would keep subtracting the end address ntil it was. I didn't want ot have it add because that could easily cause huge problems. Then I mapped that now subtracted address (only 16384 subtractions, so 16 KB unused) and used that as the heap. I tested this, pushing my krnel to its limits to see how low it could go on RAM, and using that I found it could not even boot on anything under 5 MB of RAM. That was (mainly) because the bootlaoder ran into problems; I think the kernel could safely operate on 2 MB of RAM if directly booted without a boot loader, though it wouldn't be able to do much. |
This seems to be solved. |
So, I have a paging frame allocator, as the guide prompted (with my own modifications), but it looks similar. Here's what it is:
How would I write a frame deallocator that works with this? I know how to allocate frames but how do I free them? Do I just use the x86_64 crate and clear the page, then set its unused flag?
Also, how would I go about implementing a heap/memory manager while I wait for the new post to come out? I can't find any memory manager that works with Rust that doesn't require some kind of host OS, and to do anything else (i.e. get keyboard input and store it in a buffer and so on) requires a memory manager or some way of allocating and freeing memory, something I don't have and am not sure how to code. Porting a memory manager would probably be a pain since (if I'm not mistaken) all the memory managers' source code out there requires an existnig host OS with frame freeing and all that, as well as a heap.
The text was updated successfully, but these errors were encountered: