问题描述
我想看看如何实现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
函数指针,它们是:
- 声明:
static void *(*mmapp) (void *,off_t);
- 在名为
me()
的某些函数中的初始化:mmapp = (void *(*)(void *,off_t))dlsym (RTLD_NEXT,"mmap");
根据{{3}},dlsym()
函数采用dlopen()返回的动态库的“句柄”和以空值结尾的符号名称,并返回将该符号加载到内存中的地址。 / li>
因此,整个过程可以总结如下:
-
mmap()
调用指针mmapp
指向的函数 -
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.h,sysdeps/unix/sysv/linux/bits/mman-shared.h,sysdeps/unix/sysv/linux/bits/mman-map-flags-generic.h,sysdeps/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,这会使其他内核机制将特定的页面映射到进程中,或者使该区域内的页面错误回调到文件系统中以提供要映射的页面。那个时候。