最近在看 glibc 的源码,想要看一下 glibc 中的动态内存管理器 ptmalloc 是如何实现的。

刚才去看了 brk 和 sbrk 函数的实现,发现一些很有趣的地方。

brk 库函数的实现是在 misc/brk.c

/* sbrk.c expects this.  */
void *__curbrk;

/* Set the end of the process's data space to ADDR.
   Return 0 if successful, -1 if not.  */
int
__brk (void *addr)
{
  __set_errno (ENOSYS);
  return -1;
}
stub_warning (brk)

weak_alias (__brk, brk)

但是,当我们自己查看这个函数的时候,很明显,这个并不是真正的定义,至少并不是我们想找的那个。那么,这个函数是什么呢?

它其实是brk的一个alias,而且还是weak alias。换而言之,如果一个平台上没有定义自己的 brk ,那么就用这个。而且从上面的代码也可以看得出来,这个函数仅仅是处理了一下errno,别的什么都不做。

另外,关于Glibcweak aliasstrong alias的定义与含义,可以参考博客以及 Stack Overflow 上的一个回答

现在问题来了,brk的真正定义位置是在哪里呢?

经过了一番搜索,我找到了一些资料:

sysdeps/unix/make-syscalls.sh
sysdeps/unix/syscalls.list(sysdeps/unix/inet/syscalls.list)
sysdeps/unix/syscall-template.S

其中,make-syscalls.sh就是用模板和那个列表来构建生成系统调用定义。

首先,这个shell脚本要从sysdir中对应syscall的定义,这些sysdir如下所示:

  • sysdeps/unix/sysv/linux
  • sysdeps/posix
  • sysdeps/x86_64 (My CPU Arch is x86_64).

那么我们现在就找一下 brk 的真正定义位置。

glibc/sysdeps$ find . -name brk.[cS] -print
./unix/sysv/linux/x86_64/brk.c
./unix/sysv/linux/hppa/brk.c
./unix/sysv/linux/sh/brk.c
./unix/sysv/linux/microblaze/brk.c
./unix/sysv/linux/mips/brk.c
./unix/sysv/linux/alpha/brk.S
./unix/sysv/linux/m68k/brk.c
./unix/sysv/linux/generic/brk.c
./unix/sysv/linux/s390/brk.c
./unix/sysv/linux/sparc/sparc64/brk.S
./unix/sysv/linux/sparc/sparc32/brk.c
./unix/sysv/linux/i386/brk.c
./unix/sysv/linux/ia64/brk.S
./unix/sysv/linux/powerpc/powerpc32/brk.S
./unix/sysv/linux/powerpc/powerpc64/brk.S
./unix/sysv/linux/arm/brk.c
./mach/hurd/brk.c
./nacl/brk.c
/* This must be initialized data because commons can't have aliases.  */
void *__curbrk = 0;

int
__brk (void *addr)
{
  void *newbrk;

  __curbrk = newbrk = (void *) INLINE_SYSCALL (brk, 1, addr);

  if (newbrk < addr)
    {
      __set_errno (ENOMEM);
      return -1;
    }

  return 0;
}
weak_alias (__brk, brk)

从上面看出,针对我的系统框架,brk的定义在sysdeps/unix/sysv/linux/x86_64/brk.c中,

其次,如果某些 syscall 没有对应的.c.S文件的话,就需要以 模板文件(syscall-template.S)以及系统调用列表来创建对应的系统调用定义。