glibc中的mmap实现-具有符号mmap的动态库

问题描述

我想看看如何实现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,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,misc/mmap.c:__mmap (void *addr,off_t offset)
support/xunistd.h:void *xmmap (void *addr,size_t length,int fd);
support/xmmap.c:xmmap (void *addr,int fd)
sysdeps/unix/sysv/linux/mmap.c:__mmap (void *addr,off_t offset)
sysdeps/mach/hurd/dl-sysdep.c:__mmap (void *addr,off_t offset)
sysdeps/mach/hurd/mmap.c:__mmap (void *addr,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,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,offset);,并且在此文件中还有其他两个部分处理此mmapp函数指针,它们是:

  1. 声明:static void *(*mmapp) (void *,off_t);
  2. 在名为me()的某些函数中的初始化:mmapp = (void *(*)(void *,off_t))dlsym (RTLD_NEXT,"mmap");
    根据{{​​3}},dlsym()函数采用dlopen()返回的动态库的“句柄”和以空值结尾的符号名称,并返回将该符号加载到内存中的地址。 / li>

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

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

但是在动态库中找不到任何带有符号"mmap"的信息。
我在代码分析过程中做错什么了吗?我在代码分析方面没有太多经验,更不用说研究系统调用函数或内核代码了,因此,向正确的方向提出任何建议或建议将不胜感激。
预先感谢!

解决方法

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,len,prot,flags,fd,pgoff)

如果提供了文件描述符,则会导致调用

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

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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...