You should keep those two kind of function in separate methods:
* The function that gives you a "free virtual memory slice". Actually, you'll want many functions that give a memory slice, because you'll want to allocate kernel heap, thread stacks, and maybe shared memory between CPU cores.
* The function that maps a free memory slice to a physical memory location
to answer to your question, you'll want other things than contiguous memory, because you'll have to deal with page freeing. When calling kfree multiple time, your dynamic memory management should notice that you emptied the last occupied bit of a virtual page, and should free it. That way, you'll end up with virtual memory holes in your heap.
I personally use two self-balancing-trees ( see https://en.wikipedia.org/wiki/AVL_tree
): one that keep track of free virtual area sizes (in order to quickly allocate a number of contiguous memory pages), and one that keep track of virtual area starting point (in order to quickly merge free virtual memory areas when i free a busy one)