I'm a bit confused about some things, so could someone please confirm if my explanation is correct/incorrect.

This is how I think it works:
1. A physical memory allocator provides frame_alloc(pages) and frame_free(position, pages), which allocates physical memory in blocks of however big a page on the system architecture is. This can be implemented with a bitset, a stack, a buddy allocator, etc.
2. A virtual address allocator provides page_alloc(pages) and page_free(position, pages), which allocates virtual memory addresses in blocks of however big a page on the system architecture is, making use of frame_alloc() and frame_free(). This can be implemented with ???.
3. Finally, the memory allocator itself provides malloc(size) and free(position), which allocates virtual memory of any size. This can be implemented in several ways, and the code is usually linked with program binaries. This makes use of page_alloc() and page_free() through system calls (or directly for the kernel's memory allocator).
Please tell me if anything's incorrect!

-bace