【问题标题】:mmap implementation in glibc - dynamic library with symbol mmapglibc 中的 mmap 实现 - 带有符号 mmap 的动态库
【发布时间】:2021-02-17 19:50:20
【问题描述】:

我想看看Linux内核函数mmap()是如何实现的,所以我从GNU page下载了GNU C库(glibc)源码。 (我下载了glibc-2.27 因为ldd --version 告诉我我正在使用GLIBC 2.27
现在要找到mmap() 的定义,我做了grep -r "mmap(void" *,它什么也没返回,所以我尝试了grep -r "mmap (void" *,它返回了以下内容:

conform/data/sys/mman.h-data:function {void*} mmap (void*, size_t, int, int, int, off_t)
include/sys/mman.h:extern void *__mmap (void *__addr, size_t __len, int __prot,
malloc/memusage.c:mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
manual/llio.texi:@deftypefun {void *} mmap (void *@var{address}, size_t @var{length}, int @var{protect}, int @var{flags}, int @var{filedes}, off_t @var{offset})
misc/sys/mman.h:extern void *mmap (void *__addr, size_t __len, int __prot,
misc/mmap.c:__mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset)
support/xunistd.h:void *xmmap (void *addr, size_t length, int prot, int flags, int fd);
support/xmmap.c:xmmap (void *addr, size_t length, int prot, int flags, int fd)
sysdeps/unix/sysv/linux/mmap.c:__mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset)
sysdeps/mach/hurd/dl-sysdep.c:__mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset)
sysdeps/mach/hurd/mmap.c:__mmap (void *addr, size_t len, int prot, int flags, int fd, off_t offset)

在所有关于mmap() 而不是__mmap() 的结果中,我发现mmap() 的定义在malloc/memusage.c 中,它定义mmap() 如下:

/* `mmap' replacement.  We do not have to keep track of the size since
   `munmap' will get it as a parameter.  */
void *
mmap (void *start, size_t len, int prot, int flags, int fd, off_t offset)
{
  void *result = NULL;

  /* Determine real implementation if not already happened.  */
  if (__glibc_unlikely (initialized <= 0))
    {
      if (initialized == -1)
        return NULL;

      me ();
    }

  /* Always get a block.  We don't need extra memory.  */
  result = (*mmapp)(start, len, prot, flags, fd, offset);

  ...

  /* Return the pointer to the user buffer.  */
  return result;
}

我认为result = (*mmapp)(start, len, prot, flags, fd, offset); 才是最重要的,在这个文件中还有另外两个部分处理这个mmapp 函数指针,它们是:

  1. 声明:static void *(*mmapp) (void *, size_t, int, int, int, off_t);
  2. 在一些名为me()的函数中初始化:mmapp = (void *(*)(void *, size_t, int, int, int, off_t))dlsym (RTLD_NEXT, "mmap");
    根据the manualdlsym() 函数采用 dlopen() 返回的动态库的“句柄”和以 null 结尾的符号名称,返回该符号加载到内存中的地址。

因此,整个过程可以总结如下:

  1. mmap() 调用指针mmapp 指向的函数
  2. mmapp 设置为指向加载到内存中的动态库中的符号 "mmap"

但我找不到任何关于带有符号"mmap" 的动态库的信息。
我在代码分析过程中做错了吗?我在代码分析方面没有太多经验,更不用说研究系统调用函数或内核代码了,所以任何建议或朝着正确的方向推进都将不胜感激。
提前致谢!

【问题讨论】:

  • 这些对你来说都不是很有趣,因为在底部,你将在库中看到的只是一个系统调用。这里真正的动作都在内核中。参见例如github.com/torvalds/linux/blob/master/mm/mmap.c。但是您可能更愿意查看一些较旧的或“玩具”操作系统,其中代码可能更简单且更具可读性。
  • @NateEldredge 嘿,我认为这实际上可能很有帮助。但我想你可以从我的问题中看出我对 Linux 内核的东西不是很熟悉 - 我想问一下,在 mmap 的手册中它说我们需要包含 但没有名为的目录git 中的 sys (有 linux/include/linux/mman.h 但它没有 mmap 定义或声明)......我在这里误解了什么吗?
  • 这个故事太长了,无法评论。问一个单独的问题。

标签: linux linux-kernel operating-system system-calls mmap


【解决方案1】:

Linux 内核有多个不同的mmap 系统调用,它们在不同的版本和体系结构中并不相同。 libc mmap 函数将其抽象化并向其用户呈现 POSIX 接口。然而,Glibc 代码由于它还针对非 Linux 平台这一事实而变得复杂,并且具有 off_t/off64_t 转换的所有部分的代码路径(有关此的更多信息,请参阅 Glibc manual § Feature Test Macros)。您可以更轻松地查看替代的最小 libc,例如 musl,它仅针对现代 Linux,不针对历史或完全 POSIX 兼容性。

在 musl 的情况下:原型和常量在 include/sys/mman.h 中。实现在 src/mman/mmap.c 中,它在移交给 syscall(SYS_mmap2) 之前进行一些参数处理(在大多数情况下)。

在 Glibc 的情况下:原型在 misc/sys/mman.h。常量分散,因​​为并非所有平台都相同;例如,参见bits/mman.hsysdeps/unix/sysv/linux/bits/mman-shared.hsysdeps/unix/sysv/linux/bits/mman-map-flags-generic.hsysdeps/unix/sysv/linux/x86/bits/mman.h。实际定义在sysdeps/unix/sysv/linux/mmap.c 中,和musl 的一样,它在传递给syscall(SYS_mmap2) 之前会进行一些参数处理(在大多数情况下)。

不管怎样,libc 中的mmap 只能在内核支持下工作。因此,@NateEldredge 的评论认为它相当无趣。

顺便说一句,您在malloc/memusage.c 中看到的不是您正在使用的mmap 的定义。它被内置到一个单独的 libmemusage 库中,该库拦截 mmap 调用(以及其他)并在它们周围添加一些跟踪。您的分发可能会也可能不会发货;如果是这样,您必须通过使用LD_PRELOAD=libmemusage.so 运行程序来选择使用它(这实际上是memusage 包装脚本所做的)。这就是它使用dlsym 的原因:它定义了一个替换mmap 符号,但需要查找原始mmap 才能包装对它的调用。

在 Linux 内核中,SYS_mmap2 在大多数架构上映射到 sys_mmap_pgoff,在 mm/mmap.c 中定义为

SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
        unsigned long, prot, unsigned long, flags,
        unsigned long, fd, unsigned long, pgoff)

如果给定一个文件描述符,它会调用

struct file {
    const struct file_operations {
        int (*mmap) (struct file *, struct vm_area_struct *);
    } *f_op;
}

由提供该文件的文件系统定义的回调(来自include/linux/fs.h)。它们有各种不同的实现,但一般来说,它们向进程的页表添加一个新的 vma,这要么使其他内核机器将特定页面映射到进程中,要么使该区域内的页面错误回调到文件系统以提供要映射的页面那个时候。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-13
    • 1970-01-01
    • 1970-01-01
    • 2012-09-18
    • 2016-12-21
    • 2021-08-06
    • 1970-01-01
    相关资源
    最近更新 更多