does brk and sbrk round the program break to the nearest page boundary?

505 Views Asked by At

My question is as tilte says, accroding to my text book

int brk(void *end_data_segment);

The brk() system call sets the program break to the location specified by end_data_segment. Since virtual memory is allocated in units of pages, end_data_segment is effectively rounded up to the next page boundary.

and since on Linux, sbrk() is implemented as a library function that uses the brk() system call, so I expect that both function will round program break to the next page boundary. but when I test on a x86_64 Linux machine(ubuntu), it turns out both functions move the program break to the exact position as requested(I tried using brk, result is the same).

    int main(int argc, char *argv[])
    {
       void *ori = sbrk(100);
       printf("original program break at %p\n", ori);
       void *now = sbrk(0);
       printf("program break now at %p\n", now);
      return 0;
  }

this is the output

original program break at 0x56491e28f000
program break now at 0x56491e28f064

so what's going on here?

1

There are 1 best solutions below

0
horstr On BEST ANSWER

brk allocates/deallocates pages. That implementation detail based on the fact that the smallest unit of data for memory management in a virtual memory operating system is a page is transparent to the caller, however.

In the Linux kernel, brk saves the unaligned value and uses the aligned value to determine if pages need to be allocated/deallocated:

asmlinkage unsigned long sys_brk(unsigned long brk)
{
[...]
    newbrk = PAGE_ALIGN(brk);
    oldbrk = PAGE_ALIGN(mm->brk);
    if (oldbrk == newbrk)
        goto set_brk;
[...]
    if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
        goto out;
set_brk:
    mm->brk = brk;
[...]
}

As for sbrk: glibc calls brk and maintains the (unaligned) value of the current program break (__curbrk) in userspace:

void *__curbrk;
[...]
void *
__sbrk (intptr_t increment)
{
  void *oldbrk;
  if (__curbrk == NULL || __libc_multiple_libcs)
    if (__brk (0) < 0)          /* Initialize the break.  */
      return (void *) -1;
  if (increment == 0)
    return __curbrk;
  oldbrk = __curbrk;
[...]
  if (__brk (oldbrk + increment) < 0)
    return (void *) -1;
  return oldbrk;
}

Consequently, the return value of sbrk does not reflect the page alignment that happens in the Linux kernel.