... if memory is paged it might be possible to [implement realloc()
via] remap[ping] the page addresses without physically copying data.
This occurred to me, too.
If you think about it, though, it won't work unless malloc()
cooperates.
Well, certainly; but malloc() and realloc() must always cooperate.
(This is one of the pitfalls of attempting to substitute in a
different malloc(). Even if you handle malloc()+free()+realloc(),
you may not realize that you also had to gimmick the __vmalloc()
and __pagealloc() functions, which are attempting to cooperate with
malloc() and which are called directly from, e.g., the stdio
routines, for I/O-via-page-swapping.)
The expansion takes place in _virtual memory space_ not
physical. I.e. if malloc() hands out blocks that are contiguous in
virtual space, remapping won't help because when you try to grow a
block, malloc() has probably already given up the virtual addresses
immediately above.
This is not a problem, because the caller must write:
original_region = malloc(original_size); /* called O and S below */
... do appropriate work with it ...
new_region = realloc(original, new_size); /* called N and T below */
The realloc() call can obtain any available (and suitable, if there
are restrictions) set of virtual addresses, then ask the OS to move
the physical pages from the old address-space -- the region underlying
the virtual address range in [O..O+S-1] -- to the new range [N..N+T-1]
(with "new" pages added at the end if needed, or "old" pages removed
if T < S; and of course S and T are page-rounded and O and N must be
page-aligned).
If the pages are *moved* (as opposed to simply multiply-mapped),
this also gives you a Feature: subsequent access to original_region
will fail ("segfault" or "bus error" or whatever), which will help
the programmer find any "stale" pointers. This technique is quite
useful for debugging, and hence doing page-moving into "fresh"
virtual space on *every* memory allocation -- even those that can
be done without such a move -- can be valuable. Similarly, one
can unmap the page(s) backing any region that has been free()d.
(I used this trick to find a bug in the 4.3BSD kernel once.)
Now 64-bit addresses open a new possibility. If every malloc (at
least up up to 4 billion of them) returns an address with the lower
(say) 32-bits zero, then every block can be grown to 4gb by remapping
pages.
If you parcel out virtual addresses on 4GB boundaries, you can
expand in-place even *without* page-remapping. (Well, assuming
the physical page size is less than 4GB, at least. )