当前位置:首页>Linux>Linux内存管理:mmap内存映射

Linux内存管理:mmap内存映射

  • 2026-03-22 14:54:19
Linux内存管理:mmap内存映射
mmap/munmap是常用的一个系统调用,使用场景是:分配内存、读写大文件、连接动态库文件、多进程间共享内存。

一、mmap/munmap介绍

mmap/munmap函数声明如下:

#include<sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,                  int fd, off_t offset);intmunmap(void *addr, size_t length);
  • addr:如果不为NULL,内核会在此地址创建映射;否则,内核会选择一个合适的虚拟地址。

  • length:表示映射到进程地址空间的大小。

  • prot:内存区域的读/写/执行属性。

  • flags:内存映射的属性,共享、私有、匿名、文件等。

  • fd:表示这是一个文件映射,fd是打开文件的句柄。

  • offset:在文件映射时,表示相对文件头的偏移量;返回的地址是偏移量对应的虚拟地址。

下面是prot对应的参数组合:

#define PROT_READ    0x1        /* page can be read */#define PROT_WRITE    0x2        /* page can be written */#define PROT_EXEC    0x4        /* page can be executed */#define PROT_SEM    0x8        /* page may be used for atomic ops */#define PROT_NONE    0x0        /* page can not be accessed */#define PROT_GROWSDOWN    0x01000000    /* mprotect flag: extend change to start of growsdown vma */#define PROT_GROWSUP    0x02000000    /* mprotect flag: extend change to end of growsup vma */

flags参数组合有:

#define MAP_SHARED    0x01        /* Share changes */---------创建一个共享映射的区域,多个进程可以映射到一个文件,掐进程可以看到映射内容的改变,修改后内容会同步到磁盘中。#define MAP_PRIVATE    0x02        /* Changes are private */--创建一个私有的写时复制的映射,其他进程看不到映射内容的改变,也不会同步到磁盘中。#define MAP_TYPE    0x0f        /* Mask for type of mapping */#define MAP_FIXED    0x10        /* Interpret addr exactly */-使用指定的映射起始地址,如果有start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。#define MAP_ANONYMOUS    0x20        /* don't use a file */---匿名映射,映射区不与任何文件关联。此时fd应设置为-1。#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZEDdefine MAP_UNINITIALIZED 0x4000000    /* For anonymous mmap, memory could be uninitialized */#elsedefine MAP_UNINITIALIZED 0x0        /* Don't support this flag */#endif#define MAP_GROWSDOWN    0x0100        /* stack-like segment */--------------告诉内核VM系统,映射区可以向下扩展。#define MAP_DENYWRITE    0x0800        /* ETXTBSY */#define MAP_EXECUTABLE    0x1000        /* mark it as an executable */#define MAP_LOCKED    0x2000        /* pages are locked */-------------------锁定映射区页面,从而防止页面被交换出内存。#define MAP_NORESERVE    0x4000        /* don't check for reservations */#define MAP_POPULATE    0x8000        /* populate (prefault) pagetables */---对文件映射来说,会提前预读文件内容到映射区域,只支持私有映射。#define MAP_NONBLOCK    0x10000        /* do not block on IO */--------------和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。#define MAP_STACK    0x20000        /* give out an address that is best suited for process/thread stacks */#define MAP_HUGETLB    0x40000        /* create a huge page mapping */

二、mmap映射类型

mmap系统调用并不完全是为了共享内存而设计的,它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件操作。

mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read()/write()等操作。

mmap并不分配空间,只是将文件映射到调用进程的地址空间里(占用虚拟地址空间),然后就可以使用memcpy()等操作,内存中内容并不立即更行到文件中,而是有一段时间的延迟,可以使用msync()显式同步。

取消内存映射通过munmap()。

下面这张图示意了mmap的内存映射,起始地址是返回的addr,off和len分别对应参数offset和length。

根据mmap是否映射到文件、是共享还是私有映射,将映射类型分成四类,使用场景如下:

场景私有影射共享映射
匿名映射通常用于内存分配fd=-1,flags=MAP_ANONYMOUS|MAP_PRIVATE通常用于进程间内存共享,常用于父子进程之间通信。FD=-1,flags=MAP_ANONYMOUS|MAP_SHARED
文件映射通常用于加载动态库flags=MAP_PRIVATE通常用于内存映射IO、进程间通信、读写文件。flags=MAP_SHARED

三、mmap流程

用户空间的 mmap,在内核中的起点是 sys_mmap_pgoff。整个调用链大致为:SyS_mmap() -> SyS_mmap_pgoff() -> vm_mmap_pgoff() -> do_mmap()。其中 do_mmap() 是核心实现,可分为三个关键步骤:

3.1 寻找空闲虚拟地址空间:get_unmapped_area ()

do_mmap() 首先调用 get_unmapped_area() 函数,在进程的虚拟地址空间中找到一段足够大小的空闲区域 [addr, addr+len]

  • 若用户未指定 addr(即 addr=NULL),内核会通过 arch_get_unmapped_area()(从低地址向高地址)或 arch_get_unmapped_area_topdown()(从高地址向低地址)遍历红黑树,寻找合适的空闲区间。

  • 若指定了 MAP_FIXED 标志,则直接使用用户指定的 addr(需确保页对齐且不与现有映射冲突,否则会覆盖旧映射)。

3.2 确定映射属性与权限

根据 prot(读 / 写 / 执行权限)和 flags(共享 / 私有 / 匿名 / 文件等属性),结合进程的 mm_struct 标志,计算出最终的 vm_flags(虚拟内存区域标志)。

  • 若为文件映射,需检查文件的读写权限,更新 vm_flags(如 VM_SHAREDVM_MAYSHARE 等)。

  • 若为匿名映射,则根据 MAP_SHARED 或 MAP_PRIVATE 调整 vm_flags 和 pgoff(页偏移)。

3.3 创建虚拟内存区域:mmap_region ()

mmap_region() 是实际创建虚拟内存区域(VMA)的核心函数,流程如下:

  1. 检查并处理冲突区域:调用 find_vma_links() 查找是否已有 VMA 覆盖目标地址 [addr, addr+len],若有则调用 do_munmap() 解除旧映射。

  2. 尝试合并 VMA:调用 vma_merge() 检查新 VMA 是否能与前后相邻的 VMA 合并(需属性相同),以减少 VMA 数量和内存碎片。

  3. 创建新 VMA:若无法合并,则通过 kmem_cache_zalloc() 分配新的 VMA 结构体,初始化其 vm_startvm_endvm_flagsvm_page_prot 等成员。

  4. 关联文件或匿名映射:

    • 若为文件映射,关联文件 struct file,调用文件操作函数集的 mmap 方法(如 ext2_file_mmap)。

    • 若为共享匿名映射,调用 shmem_zero_setup() 初始化共享内存区。

  5. 插入 VMA 到进程地址空间:调用 vma_link() 将新 VMA 插入到进程的 mm_struct 红黑树和链表中,并更新相关统计信息。

  6. 处理页锁定(可选):若设置了 VM_LOCKED 标志,则锁定映射页面,防止被交换出内存。

最终,mmap_region() 返回映射的起始虚拟地址 addr,完成整个 mmap 流程。

四、mmap使用两个小问题

问题1:两次对相同地址执行mmap是否成功?

#include<stdio.h>#include<sys/mman.h>voidmain(void){    char *pmap1, *pmap2;    pmap1 = (char *)mmap(0x2000000010240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -10);    if(MAP_FAILED == pmap1)        printf("pmap1 failed\n");    pmap2 = (char *)mmap(0x200000001024, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -10);    if(MAP_FAILED == pmap2)        printf("pmap1 failed\n");}

 在Ubuntu上执行strace ./mmap结果如下:

...mmap(0x20000000, 10240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000mmap(0x20000000, 1024, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000...

如果将第二个mmap的MAP_FIXED去掉呢?结果如下:

...mmap(0x20000000, 10240, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000mmap(0x20000000, 1024, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcb336e1000...

 可以看出如果映射区属性包含MAP_FIXED,则会覆盖原来区域;如果没有MAP_FIXED,内核会找到一个区域。

unsignedlongmmap_region(struct file *file, unsignedlong addr,        unsigned long len, vm_flags_t vm_flags, unsigned long pgoff){...munmap_back:    if (find_vma_links(mm, addr, addr + len, &prev, &rb_link, &rb_parent)) {        if (do_munmap(mm, addr, len))-----------------------------将冲突区域去映射            return -ENOMEM;        goto munmap_back;    }...}

问题2:在一个播放系统中同时打开几十个不同高清视频文件,发现播放有些卡顿,打开文件使用的是mmap,分析原因并解决。

mmap建立文件映射时,只建立了VMA,而没有分配对应的页面和建立映射关系。

播放时会不同发生缺页异常去读取文件内容,导致性能较差。

解决方法:

1.对mmap映射后的地址用madvise(addr, len, MADV_SEQUENTIAL)。

 2.通过"blockdev --setra"来增大内核默认预读窗口,默认是128KB。

五、mmap API解释

 对mmap()/munmap()的使用比较简单,有两个参数组合导致了多样性,分别是protflags

#include<sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);intmunmap(void *addr, size_t length);

下面对这些参数做一个简单的介绍:

  • addr:如果不为NULL,内核会在此地址创建映射;否则,内核会选择一个合适的虚拟地址。大部分情况不指定虚拟地址,意义不大,而是让内核选择返回一个地址给用户空间使用。

  • length:表示映射到进程地址空间的大小。

  • prot:内存区域的读/写/执行属性。

  • flags:内存映射的属性,共享、私有、匿名、文件等。

  • fd:表示这是一个文件映射,fd是打开文件的句柄。如果是文件映射,需要指定fd;匿名映射就指定一个特殊的-1。

  • offset:在文件映射时,表示相对文件头的偏移量;返回的地址是偏移量对应的虚拟地址。

5.1 mmap优点

5.1.1 提升效率

一般读写文件需要open、read、write,需要先将磁盘文件读取到内核cache缓冲区,然后再拷贝到用户空间内存区,设计两次读写操作。

mmap通过将磁盘文件映射到用户空间,当进程读文件时,发生缺页中断,给虚拟内存分配对应的物理内存,在通过磁盘调页操作将磁盘数据读到物理内存上,实现了用户空间数据的读取,整个过程只有一次内存拷贝。

5.1.2 用于进程间大数据量通信

两个进程映射同一个文件,在两个进程中,同一个文件区域映射的虚拟地址空间不同。一个进程操作文件时,先通过缺页获取物理内存,进而通过磁盘文件调页操作将文件数据读入内存。

另一个进程访问文件的时候,发现没有物理页面映射到虚拟内存,通过fs的缺页处理查找cache区是否有读入磁盘文件,有的话建立映射关系,这样两个进程通过共享内存就可以进行通信。

5.1.3 文件关闭,内存可以继续使用

因为在内核中已经通过fd找到对应的磁盘文件,从而将文件跟vma关联。

5.2 mmap缺点

映射时文件长度已经确定,没法通过mmap访问操作len的区间。

5.3 私有/共享、文件/匿名映射组合

共有四种组合,下面逐一介绍。

5.3.1 私有文件映射

多个进程使用同样的物理页面进行初始化,但是各个进程对内存文件的修改不会共享,也不会反映到物理文件中。

比如对linux .so动态库文件就采用这种方式映射到各个进程虚拟地址空间中。

5.3.2 私有匿名映射

mmap会创建一个新的映射,各个进程不共享,主要用于分配内存(malloc分配大内存会调用mmap)。

5.3.3 共享文件映射

多个进程通过虚拟内存技术共享同样物理内存,对内存文件的修改会反应到实际物理内存中,也是进程间通信的一种。

5.3.4 共享匿名映射

这种机制在进行fork时不会采用写时复制,父子进程完全共享同样的物理内存页,也就是父子进程通信。

六、mmap内核实现

系统调用的入口是entry_SYSCALL_64_fastpath,然后根据系统调用号在sys_call_table中找到对应的函数。

mmap()和munmap()对应的系统调用分别是SyS_mmap()和SyS_munmap()下面就来分析一下实现。

6.1 mmap/munmap调用路径

在分析具体内核实现之前,通过脚本来看看mmap/munmap调用路径。

通过增加set_ftrace_filter的函数,修改current_tracer发现函数的调用者,逐步丰富调用路径。

#!/bin/bash DPATH="/sys/kernel/debug/tracing"PID=$$ ## Quick basic checks [ `id -u` -ne 0 ] && { echo "needs to be root" ; exit 1; } # check for root permissions [ -z $1 ] && { echo "needs process name as argument" ; exit 1; } # check for args to this function mount | grep -i debugfs &> /dev/null [ $? -ne 0 ] && { echo "debugfs not mounted, mount it first"exit 1; } #checks for debugfs mount # flush existing trace data echo > $DPATH/traceecho nop > $DPATH/current_tracerecho > $DPATH/set_ftrace_filterecho "SyS_mmap SyS_mmap_pgoff SyS_munmap SyS_open SyS_read SyS_write SyS_close SyS_brk SyS_msync" >> $DPATH/set_ftrace_filterecho "do_brk elf_map load_elf_binary" >> $DPATH/set_ftrace_filterecho "do_mmap do_munmap get_unmapped_area mmap_region vm_mmap vm_munmap vm_mmap_pgoff" >> $DPATH/set_ftrace_filterecho "__split_vma* unmap_region" >> $DPATH/set_ftrace_filter# set function tracerecho function_graph > $DPATH/current_tracer# write current process id to set_ftrace_pid fileecho $PID > $DPATH/set_ftrace_pid#echo "common_pid==$PID" > /sys/kernel/debug/tracing/events/syscalls/sys_enter_mmap/filter#echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_mmap/enable#echo "common_pid==$PID" > /sys/kernel/debug/tracing/events/syscalls/sys_enter_munmap/filter#echo 1 > /sys/kernel/debug/tracing/events/syscalls/sys_enter_munmap/enable# start the tracingecho 1 > $DPATH/tracing_on# execute the processexec $*#sudo cat $DPATH/trace > /home/al/v4l2/trace.txt

最后使用function_graph跟踪器查看调用关系如下:

 1)               |  SyS_mmap() { 1)               |    SyS_mmap_pgoff() { 1)               |      vm_mmap_pgoff() { 1)               |        do_mmap() { 1)   0.548 us    |          get_unmapped_area(); 1)   3.388 us    |          mmap_region(); 1)   4.598 us    |        } 1)   5.286 us    |      } 1)   5.756 us    |    } 1)   6.058 us    |  } 1)               |  SyS_munmap() { 1)               |    vm_munmap() { 1)               |      do_munmap() { 1) + 99.985 us   |        unmap_region(); 1) ! 101.439 us  |      } 1) ! 101.838 us  |    } 1) ! 102.410 us  |  }

下面就围绕这条路径展开分析。

6.2 mmap()

mmap()系统调用的核心是do_mmap(),可以分为三部分。

第一部分通过get_unmapped_area()函数,找到一段虚拟地址,范围是[addr, addr+len]。

用户进程一般不会指定addr,也就是由内核指定这个虚拟空间的首地址addr在哪里。

在函数do_mmap_pgoff()调用get_unmapped_area()之前会预指定addr,通过round_hint_to_min()实现,然后用这个预指定addr为参数调用get_unmapped_area()。

第二部分确定vma线性区的flags,针对文件、匿名,私有、共享有所不同。

第三部分是实际创建vma先行区,通过函数mmap_region()实现。

asmlinkage unsigned longsys_mmap (unsigned long addr, unsigned long len, int prot, int flags, int fd, long off){    if (offset_in_page(off) != 0)        return -EINVAL;    addr = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);    if (!IS_ERR((void *) addr))        force_successful_syscall_return();    return addr;}SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,        unsigned long, prot, unsigned long, flags,        unsigned long, fd, unsigned long, pgoff){    struct file *file = NULL;    unsigned long retval;    if (!(flags & MAP_ANONYMOUS)) {------------------------------------------对非匿名文件映射的检查,必须能根据文件句柄找到struct file。        audit_mmap_fd(fd, flags);        file = fget(fd);        if (!file)            return -EBADF;        if (is_file_hugepages(file))            len = ALIGN(len, huge_page_size(hstate_file(file)));-------------根据file->f_op来判断是否是hugepage,然后进行hugepage页面对齐。        retval = -EINVAL;        if (unlikely(flags & MAP_HUGETLB && !is_file_hugepages(file)))            goto out_fput;    } else if (flags & MAP_HUGETLB) {        struct user_struct *user = NULL;        struct hstate *hs;        hs = hstate_sizelog((flags >> MAP_HUGE_SHIFT) & SHM_HUGE_MASK);        if (!hs)            return -EINVAL;        len = ALIGN(len, huge_page_size(hs));        /*         * VM_NORESERVE is used because the reservations will be         * taken when vm_ops->mmap() is called         * A dummy user value is used because we are not locking         * memory so no accounting is necessary         */        file = hugetlb_file_setup(HUGETLB_ANON_FILE, len,                VM_NORESERVE,                &user, HUGETLB_ANONHUGE_INODE,                (flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK);        if (IS_ERR(file))            return PTR_ERR(file);    }    flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);    retval = vm_mmap_pgoff(file, addr, len, prot, flags, pgoff);out_fput:    if (file)        fput(file);    return retval;}unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr,    unsigned long len, unsigned long prot,    unsigned long flag, unsigned long pgoff){    unsigned long ret;    struct mm_struct *mm = current->mm;    unsigned long populate;    ret = security_mmap_file(file, prot, flag);    if (!ret) {        down_write(&mm->mmap_sem);        ret = do_mmap_pgoff(file, addr, len, prot, flag, pgoff,                    &populate);        up_write(&mm->mmap_sem);        if (populate)            mm_populate(ret, populate);    }    return ret;}unsigned long do_mmap(struct file *file, unsigned long addr,            unsigned long len, unsigned long prot,            unsigned long flags, vm_flags_t vm_flags,            unsigned long pgoff, unsigned long *populate){    struct mm_struct *mm = current->mm;    *populate = 0;    if (!len)        return -EINVAL;    if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC))        if (!(file && path_noexec(&file->f_path)))            prot |= PROT_EXEC;    if (!(flags & MAP_FIXED))-------------------------------------------------对于非MAP_FIXED,addr不能小于mmap_min_addr大小,如果小于则使用mmap_min_addr页对齐后的地址。        addr = round_hint_to_min(addr);    /* Careful about overflows.. */    len = PAGE_ALIGN(len);    if (!len)-----------------------------------------------------------------这里不是判断len是否为0,而是检查len是否溢出。        return -ENOMEM;    /* offset overflow? */    if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)--------------------------------检查offset是否溢出        return -EOVERFLOW;    /* Too many mappings? */    if (mm->map_count > sysctl_max_map_count)---------------------------------进程中mmap个数限制,超出返回ENOMEM错误。        return -ENOMEM;    addr = get_unmapped_area(file, addr, len, pgoff, flags);------------------在创建新的ma区域之前首先寻找一块足够大小的空闲区域,本函数就是用于查找未映射的区域,返回值addr就是这段空间的首地址。    if (offset_in_page(addr))        return addr;    vm_flags |= calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |            mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;------------根据prot/flags以及mm->flags来得到vm_flags。    if (flags & MAP_LOCKED)        if (!can_do_mlock())            return -EPERM;    if (mlock_future_check(mm, vm_flags, len))        return -EAGAIN;    if (file) {---------------------------------------------------------------文件映射情况处理,主要更新vm_flags。        struct inode *inode = file_inode(file);        if (!file_mmap_ok(file, inode, pgoff, len))            return -EOVERFLOW;        switch (flags & MAP_TYPE) {        case MAP_SHARED:------------------------------------------------------共享文件映射            if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE))                return -EACCES;            if (IS_APPEND(inode) && (file->f_mode & FMODE_WRITE))                return -EACCES;            if (locks_verify_locked(file))                return -EAGAIN;            vm_flags |= VM_SHARED | VM_MAYSHARE;            if (!(file->f_mode & FMODE_WRITE))                vm_flags &= ~(VM_MAYWRITE | VM_SHARED);        case MAP_PRIVATE:-----------------------------------------------------私有文件映射            if (!(file->f_mode & FMODE_READ))                return -EACCES;            if (path_noexec(&file->f_path)) {                if (vm_flags & VM_EXEC)                    return -EPERM;                vm_flags &= ~VM_MAYEXEC;            }            if (!file->f_op->mmap)                return -ENODEV;            if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))                return -EINVAL;            break;        default:            return -EINVAL;        }    } else {------------------------------------------------------------------匿名映射情况处理        switch (flags & MAP_TYPE) {        case MAP_SHARED:------------------------------------------------------共享匿名映射            if (vm_flags & (VM_GROWSDOWN|VM_GROWSUP))                return -EINVAL;            pgoff = 0;--------------------------------------------------------为什么为0            vm_flags |= VM_SHARED | VM_MAYSHARE;            break;        case MAP_PRIVATE:-----------------------------------------------------私有匿名映射            pgoff = addr >> PAGE_SHIFT;            break;        default:            return -EINVAL;        }    }    if (flags & MAP_NORESERVE) {        /* We honor MAP_NORESERVE if allowed to overcommit */        if (sysctl_overcommit_memory != OVERCOMMIT_NEVER)            vm_flags |= VM_NORESERVE;        /* hugetlb applies strict overcommit unless MAP_NORESERVE */        if (file && is_file_hugepages(file))            vm_flags |= VM_NORESERVE;    }    addr = mmap_region(file, addr, len, vm_flags, pgoff);--------------------实际创建vma    if (!IS_ERR_VALUE(addr) &&        ((vm_flags & VM_LOCKED) ||         (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE))        *populate = len;    return addr;}

get_unmapped_area()根据输入的addr,以及其它参数通过get_area()来找到一个满足条件的虚拟空间,返回这个虚拟空间的首地址。

get_area()是一个函数指针,有两种可能使用mm->get_unmapped_area()或者file->f_op->get_unmapped_area()。

unsigned longget_unmapped_area(structfile *file, unsigned long addr, unsigned long len,        unsigned long pgoff, unsigned long flags){    unsigned long (*get_area)(structfile *, unsigned long,                  unsigned long, unsigned long, unsigned long);    unsigned long error = arch_mmap_check(addr, len, flags);    if (error)        return error;    /* Careful about overflows.. */    if (len > TASK_SIZE)        return -ENOMEM;    get_area = current->mm->get_unmapped_area;------------使用mm_struct->get_unmapped_area()方法,即arch_get_unmapped_area()。    if (file && file->f_op->get_unmapped_area)------------如果是文件映射,并且该文件的file_operations定义了get_unmapped_area方法,那么使用它实现定位虚拟区间。        get_area = file->f_op->get_unmapped_area;    addr = get_area(file, addr, len, pgoff, flags);    if (IS_ERR_VALUE(addr))        return addr;    if (addr > TASK_SIZE - len)        return -ENOMEM;    if (offset_in_page(addr))        return -EINVAL;    addr = arch_rebalance_pgtables(addr, len);    error = security_mmap_addr(addr);    return error ? error : addr;}

看arch_get_unmapped_area()名字就知道,可能有各架构自己的实现函数。这里以平台无关的函数进行分析。

arch_get_unmapped_area()完成从低地址向高地址创建新的映射,而arch_get_unmapped_area_topdown()完成从高地址向低地址创建新的映射。

unsignedlongarch_get_unmapped_area(struct file *filp, unsignedlong addr,        unsigned long len, unsigned long pgoff, unsigned long flags){    struct mm_struct *mm = current->mm;    struct vm_area_struct *vma;    int do_align = 0;    int aliasing = cache_is_vipt_aliasing();    struct vm_unmapped_area_info info;    if (aliasing)        do_align = filp || (flags & MAP_SHARED);    if (flags & MAP_FIXED) {------------------这里可以看出MAP_FIXED不参与选址,固定地址创建。        if (aliasing && flags & MAP_SHARED &&            (addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1))            return -EINVAL;        return addr;    }    if (len > TASK_SIZE)        return -ENOMEM;    if (addr) {--------------------------------当addr非0,表示制定了一个特定的优先选用地址,内核会检查该区域是否与现存区域重叠,有find_vma()完成查找功能。        if (do_align)            addr = COLOUR_ALIGN(addr, pgoff);        else            addr = PAGE_ALIGN(addr);        vma = find_vma(mm, addr);        if (TASK_SIZE - len >= addr &&            (!vma || addr + len <= vm_start_gap(vma)))            return addr;    }    info.flags = 0;    info.length = len;    info.low_limit = mm->mmap_base;    info.high_limit = TASK_SIZE;    info.align_mask = do_align ? (PAGE_MASK & (SHMLBA - 1)) : 0;    info.align_offset = pgoff << PAGE_SHIFT;    return vm_unmapped_area(&info);-----------当addr为空或者指定的优选地址不满足分配条件时,内核必须遍历进程中可用的区域,设法找到一个大小适当的空闲区域,vm_unmapped_area()完成实际的工作。}staticinlineunsignedlongvm_unmapped_area(struct vm_unmapped_area_info *info){    if (info->flags & VM_UNMAPPED_AREA_TOPDOWN)        return unmapped_area_topdown(info);--从高地址到低地址穿点映射。    else        return unmapped_area(info);----------从低地址到高地址创建映射。}unsignedlongunmapped_area(struct vm_unmapped_area_info *info){    /*     * We implement the search by looking for an rbtree node that     * immediately follows a suitable gap. That is,     * - gap_start = vma->vm_prev->vm_end <= info->high_limit - length;     * - gap_end   = vma->vm_start        >= info->low_limit  + length;     * - gap_end - gap_start >= length     */    struct mm_struct *mm = current->mm;    struct vm_area_struct *vma;    unsigned long length, low_limit, high_limit, gap_start, gap_end;    /* Adjust search length to account for worst case alignment overhead */    length = info->length + info->align_mask;    if (length < info->length)        return -ENOMEM;    /* Adjust search limits by the desired length */    if (info->high_limit < length)        return -ENOMEM;    high_limit = info->high_limit - length;    if (info->low_limit > high_limit)        return -ENOMEM;    low_limit = info->low_limit + length;    /* Check if rbtree root looks promising */    if (RB_EMPTY_ROOT(&mm->mm_rb))        goto check_highest;    vma = rb_entry(mm->mm_rb.rb_node, struct vm_area_struct, vm_rb);    if (vma->rb_subtree_gap < length)        goto check_highest;    while (true) {        /* Visit left subtree if it looks promising */        gap_end = vm_start_gap(vma);----------------------------------先从低地址开始查询。        if (gap_end >= low_limit && vma->vm_rb.rb_left) {            struct vm_area_struct *left =                rb_entry(vma->vm_rb.rb_left,                     struct vm_area_struct, vm_rb);            if (left->rb_subtree_gap >= length) {                vma = left;                continue;            }        }        gap_start = vma->vm_prev ? vm_end_gap(vma->vm_prev) : 0;------当前结点rb_subtree_gap已经是最后一个可能满足这次分配。check_current:        /* Check if current node has a suitable gap */        if (gap_start > high_limit)            return -ENOMEM;        if (gap_end >= low_limit &&            gap_end > gap_start && gap_end - gap_start >= length)            goto found;        /* Visit right subtree if it looks promising */        if (vma->vm_rb.rb_right) {            struct vm_area_struct *right =                rb_entry(vma->vm_rb.rb_right,                     struct vm_area_struct, vm_rb);            if (right->rb_subtree_gap >= length) {                vma = right;                continue;            }        }        /* Go back up the rbtree to find next candidate node */        while (true) {            struct rb_node *prev = &vma->vm_rb;            if (!rb_parent(prev))                goto check_highest;            vma = rb_entry(rb_parent(prev),                       struct vm_area_struct, vm_rb);            if (prev == vma->vm_rb.rb_left) {                gap_start = vm_end_gap(vma->vm_prev);                gap_end = vm_start_gap(vma);                goto check_current;            }        }    }check_highest:    /* Check highest gap, which does not precede any rbtree node */    gap_start = mm->highest_vm_end;    gap_end = ULONG_MAX;  /* Only for VM_BUG_ON below */    if (gap_start > high_limit)        return -ENOMEM;found:    /* We found a suitable gap. Clip it with the original low_limit. */    if (gap_start < info->low_limit)        gap_start = info->low_limit;    /* Adjust gap address to the desired alignment */    gap_start += (info->align_offset - gap_start) & info->align_mask;    VM_BUG_ON(gap_start + info->length > info->high_limit);    VM_BUG_ON(gap_start + info->length > gap_end);    return gap_start;}

mmap_region()首先调用find_vma_links()查找是否已有vma线性区包含addr,如果有调用do_munmap()把这个vma干掉。

Linux不希望vma和vma之间存在空洞,只要新创建vma的flags属性和前面或者后面vma仙童,就尝试合并成一个新的vma,减少slab缓存消耗量,同时也减少了空洞浪费。

如果无法合并,那么只好新创建vma并对vma结构体初始化相关成员;根据vma是否有页锁定标志(VM_LOCKED),决定是否立即分配物理页。

最后将新建的vma插入进程空间vma红黑树中,并返回addr。

unsignedlongmmap_region(struct file *file, unsignedlong addr,        unsigned long len, vm_flags_t vm_flags, unsigned long pgoff){    struct mm_struct *mm = current->mm;    struct vm_area_struct *vma, *prev;    int error;    struct rb_node **rb_link, *rb_parent;    unsigned long charged = 0;    /* Check against address space limit. */    if (!may_expand_vm(mm, len >> PAGE_SHIFT)) {--------------------检查当前total_vm+len是否查过RLIMIT_AS,确保虚拟映射可以进行。        unsigned long nr_pages;        if (!(vm_flags & MAP_FIXED))            return -ENOMEM;        nr_pages = count_vma_pages_range(mm, addr, addr + len);        if (!may_expand_vm(mm, (len >> PAGE_SHIFT) - nr_pages))            return -ENOMEM;    }    while (find_vma_links(mm, addr, addr + len, &prev, &rb_link,                  &rb_parent)) {-----------------------------------遍历该进程已有的vma红黑树,如果找到vma覆盖[addr, end]区域,那么返回0,表示找到。如果覆盖已有的vma区域,返回ENOMEM。        if (do_munmap(mm, addr, len))------------------------------存在覆盖已有区域的情况,那么尝试取munmap这块区域。如果munmap成功返回0,不成功则mmap_region()失败。            return -ENOMEM;    }    if (accountable_mapping(file, vm_flags)) {        charged = len >> PAGE_SHIFT;        if (security_vm_enough_memory_mm(mm, charged))            return -ENOMEM;        vm_flags |= VM_ACCOUNT;    }    vma = vma_merge(mm, prev, addr, addr + len, vm_flags,            NULL, file, pgoff, NULL, NULL_VM_UFFD_CTX);-----------------------至此表示已经可以找到合适的vma区域,原有映射是否可以被新的映射复用,减少因为vma导致的slab消耗和虚拟内存的空洞。    if (vma)        goto out;    vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);----------------------在没有找到的情况下,新建一个vma。    if (!vma) {        error = -ENOMEM;        goto unacct_error;    }    vma->vm_mm = mm;---------------------------------------------------------初始化vma数据    vma->vm_start = addr;    vma->vm_end = addr + len;    vma->vm_flags = vm_flags;    vma->vm_page_prot = vm_get_page_prot(vm_flags);---------------------------设置vma区域内页面属性。    vma->vm_pgoff = pgoff;    INIT_LIST_HEAD(&vma->anon_vma_chain);    if (file) {--------------------------------------------------------------如果是文件映射        if (vm_flags & VM_DENYWRITE) {            error = deny_write_access(file);            if (error)                goto free_vma;        }        if (vm_flags & VM_SHARED) {            error = mapping_map_writable(file->f_mapping);            if (error)                goto allow_write_and_free_vma;        }        vma->vm_file = get_file(file);        error = file->f_op->mmap(file, vma);---------------------------------调用文件操作函数集的mmap成员。        if (error)            goto unmap_and_free_vma;        WARN_ON_ONCE(addr != vma->vm_start);        addr = vma->vm_start;        vm_flags = vma->vm_flags;    } else if (vm_flags & VM_SHARED) {--------------------------------------共享匿名区        error = shmem_zero_setup(vma);        if (error)            goto free_vma;    }    vma_link(mm, vma, prev, rb_link, rb_parent);----------------------------将新建的vma插入到进程地址空间的vma红黑树中,已经做一些计数更新等。    /* Once vma denies write, undo our temporary denial count */    if (file) {        if (vm_flags & VM_SHARED)            mapping_unmap_writable(file->f_mapping);        if (vm_flags & VM_DENYWRITE)            allow_write_access(file);    }    file = vma->vm_file;out:    perf_event_mmap(vma);    vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT);    if (vm_flags & VM_LOCKED) {        if (!((vm_flags & VM_SPECIAL) || is_vm_hugetlb_page(vma) ||                    vma == get_gate_vma(current->mm)))            mm->locked_vm += (len >> PAGE_SHIFT);        else            vma->vm_flags &= VM_LOCKED_CLEAR_MASK;    }    if (file)        uprobe_mmap(vma);    vma->vm_flags |= VM_SOFTDIRTY;    vma_set_page_prot(vma);-------------------------------------设置vma区域内页面属性。    return addr;unmap_and_free_vma:    vma->vm_file = NULL;    fput(file);    /* Undo any partial mapping done by a device driver. */    unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);    charged = 0;    if (vm_flags & VM_SHARED)        mapping_unmap_writable(file->f_mapping);allow_write_and_free_vma:    if (vm_flags & VM_DENYWRITE)        allow_write_access(file);free_vma:    kmem_cache_free(vm_area_cachep, vma);unacct_error:    if (charged)        vm_unacct_memory(charged);    return error;}

6.3 munmap

检查目标地址在当前进程的虚拟空间是否已经在使用,如果已经在使用就要将老的映射撤销,要是这个操作失败,则goto free_vma。因为flags的标志位为MAP_FIXED为1时,并未对此检查。

munmap()用于解除内存映射,其核心函数是do_munmap()。

SYSCALL_DEFINE2(munmap, unsigned long, addr, size_t, len){    profile_munmap(addr);    return vm_munmap(addr, len);}intvm_munmap(unsignedlong start, size_t len){    int ret;    struct mm_struct *mm = current->mm;    down_write(&mm->mmap_sem);    ret = do_munmap(mm, start, len);    up_write(&mm->mmap_sem);    return ret;}intdo_munmap(struct mm_struct *mm, unsignedlong start, size_t len){    unsigned long end;    struct vm_area_struct *vma, *prev, *last;    if ((offset_in_page(start)) || start > TASK_SIZE || len > TASK_SIZE-start)        return -EINVAL;    len = PAGE_ALIGN(len);    if (len == 0)        return -EINVAL;    /* Find the first overlapping VMA */    vma = find_vma(mm, start);-----------------找到起始地址落在哪个vma内。    if (!vma)----------------------------------如果没有找到的话,直接返回0        return 0;    prev = vma->vm_prev;    end = start + len;    if (vma->vm_start >= end)------------------如果要释放空间的结束地址都小于vma起始地址,说明这两者没有重叠,直接退出。        return 0;    if (start > vma->vm_start) {        int error;        if (end < vma->vm_end && mm->map_count >= sysctl_max_map_count)            return -ENOMEM;        error = __split_vma(mm, vma, start, 0);----由于start>vma->vm_start,说明要释放空间和vm_start有一段空隙。这里就是分离这段gap。        if (error)            return error;        prev = vma;    }    last = find_vma(mm, end);----------------------找到要释放空间结束地址的vma。    if (last && end > last->vm_start) {        int error = __split_vma(mm, last, end, 1);-如果if成立,说明要释放空间end和vm_start之间有gap,就需要分离这段gap。        if (error)            return error;    }    vma = prev ? prev->vm_next : mm->mmap;    if (mm->locked_vm) {        struct vm_area_struct *tmp = vma;        while (tmp && tmp->vm_start < end) {            if (tmp->vm_flags & VM_LOCKED) {                mm->locked_vm -= vma_pages(tmp);                munlock_vma_pages_all(tmp);-------如果这段空间是VM_LOCKED,就需要unlock。            }            tmp = tmp->vm_next;        }    }    detach_vmas_to_be_unmapped(mm, vma, prev, end);    unmap_region(mm, vma, prev, start, end);------释放实际占用的页面。    arch_unmap(mm, vma, start, end);    /* Fix up all other VM information */    remove_vma_list(mm, vma);---------------------删除mm_struct结构中的vma信息。    return 0;}staticvoidunmap_region(struct mm_struct *mm,        struct vm_area_struct *vma, struct vm_area_struct *prev,        unsigned long start, unsigned long end){    struct vm_area_struct *next = prev ? prev->vm_next : mm->mmap;    struct mmu_gather tlb;    lru_add_drain();    tlb_gather_mmu(&tlb, mm, start, end);    update_hiwater_rss(mm);    unmap_vmas(&tlb, vma, start, end);---------扫描线性地址空间的所有页表项    free_pgtables(&tlb, vma, prev ? prev->vm_end : FIRST_USER_ADDRESS,                 next ? next->vm_start : USER_PGTABLES_CEILING);---回收上一步已经清空的进程页表。    tlb_finish_mmu(&tlb, start, end);----------刷新TLB,在多处理器系统中,调用freepages_and_swap_cache()释放页框。}voidunmap_vmas(struct mmu_gather *tlb,        struct vm_area_struct *vma, unsigned long start_addr,        unsigned long end_addr){    struct mm_struct *mm = vma->vm_mm;    mmu_notifier_invalidate_range_start(mm, start_addr, end_addr);    for ( ; vma && vma->vm_start < end_addr; vma = vma->vm_next)        unmap_single_vma(tlb, vma, start_addr, end_addr, NULL);    mmu_notifier_invalidate_range_end(mm, start_addr, end_addr);}

6.4 msync()

进程对映射的内存空间内容改变并不直接回写到磁盘中,往往在调用munmap()后才执行操作。

msync()函数将映射内存空间内容同步到磁盘文件中。

SYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags){    unsigned long end;    struct mm_struct *mm = current->mm;    struct vm_area_struct *vma;    int unmapped_error = 0;    int error = -EINVAL;    if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC))        goto out;    if (offset_in_page(start))        goto out;    if ((flags & MS_ASYNC) && (flags & MS_SYNC))        goto out;    error = -ENOMEM;    len = (len + ~PAGE_MASK) & PAGE_MASK;    end = start + len;    if (end < start)        goto out;    error = 0;    if (end == start)        goto out;    /*     * If the interval [start,end) covers some unmapped address ranges,     * just ignore them, but return -ENOMEM at the end.     */    down_read(&mm->mmap_sem);    vma = find_vma(mm, start);    for (;;) {        struct file *file;        loff_t fstart, fend;        /* Still start < end. */        error = -ENOMEM;        if (!vma)            goto out_unlock;        /* Here start < vma->vm_end. */        if (start < vma->vm_start) {            start = vma->vm_start;            if (start >= end)                goto out_unlock;            unmapped_error = -ENOMEM;        }        /* Here vma->vm_start <= start < vma->vm_end. */        if ((flags & MS_INVALIDATE) &&                (vma->vm_flags & VM_LOCKED)) {            error = -EBUSY;            goto out_unlock;        }        file = vma->vm_file;        fstart = (start - vma->vm_start) +             ((loff_t)vma->vm_pgoff << PAGE_SHIFT);        fend = fstart + (min(end, vma->vm_end) - start) - 1;        start = vma->vm_end;        if ((flags & MS_SYNC) && file &&                (vma->vm_flags & VM_SHARED)) {            get_file(file);            up_read(&mm->mmap_sem);            error = vfs_fsync_range(file, fstart, fend, 1);            fput(file);            if (error || start >= end)                goto out;            down_read(&mm->mmap_sem);            vma = find_vma(mm, start);        } else {            if (start >= end) {                error = 0;                goto out_unlock;            }            vma = vma->vm_next;        }    }out_unlock:    up_read(&mm->mmap_sem);out:    return error ? : unmapped_error;}intvfs_fsync_range(structfile *file, loff_t start, loff_t end, int datasync){    struct inode *inode = file->f_mapping->host;    if (!file->f_op->fsync)        return -EINVAL;    if (!datasync && (inode->i_state & I_DIRTY_TIME)) {        spin_lock(&inode->i_lock);        inode->i_state &= ~I_DIRTY_TIME;        spin_unlock(&inode->i_lock);        mark_inode_dirty_sync(inode);    }    return file->f_op->fsync(file, start, end, datasync);}

6.5 malloc和brk()/mmap()关系

通过getconf PAGESIZE查看当前系统页面大小,可知当前系统页面大小为4096。

malloc()分配内存,并不一定都通过brk()进行;如果分配的内存达到128K,就要通过mmap进行。

#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include<sys/mman.h>#define MAX (4096*31+4072)intmain(){    int i=0;    char *array = (char *)malloc(MAX);    for( i=0; i<MAX; ++i )        ++array[ i ];    free(array);    return 0;}

下面就来看看MAX不同大小,对malloc的影响。

当MAX为(4096*31+4072)时,跟踪系统调用如下:

...brk(0x244c000) = 0x244c000brk(0x242c000) = 0x242c000exit_group(0) = ?+++ exited with 0 +++

当MAX为(4096*31+4073)时,跟踪系统调用如下:

...mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f12b88c9000munmap(0x7f12b88c9000, 135168) = 0exit_group(0) = ?+++ exited with 0 +++

可以看出当分配的内存接近128KB时,malloc()会对齐到128KB,并且附加了1页作为gap。实际分配的虚拟地址空间达到了132kB。

七、mmap测试

7.1 mmap()/munmap()相对于read()/write()优势

上面有提到mmap()后对内存的操作相对于普通的read()/write()速度更快,这里进行一个简单测试。

#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include<sys/time.h>#include<fcntl.h>#include<sys/mman.h>#define MAX 1024*128intmain(){    int i=0;    int count=0, fd=0;    struct timeval tv1, tv2;    char *array = (char *)malloc(MAX);    /*read*/    gettimeofday( &tv1, NULL );    fd = open"./mmap_test", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);    if(fd<0)        printf("Open file failed\n");    if(MAX != read( fd, (char*)array, MAX ))    {        printf("Reading data failed...\n");        return -1;    }    memset(array, 'a', MAX);    lseek(fd,0,SEEK_SET);    if(MAX != write(fd, (void *)array, MAX))    {        printf"Writing data failed...\n" );        return -1;    }    close( fd );    gettimeofday( &tv2, NULL );    free( array );    printf"Time of read/write: %ldus\n", (tv2.tv_usec - tv1.tv_usec));    /*mmap*/    gettimeofday( &tv1, NULL );    fd = open"./mmap_test2", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);    array = mmapNULL, MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);    memset(array, 'b', MAX);    munmap( array, MAX );    msync( array, MAX, MS_SYNC );    close( fd );    gettimeofday( &tv2, NULL );    printf"Time of mmap/munmap/msync: %ldus\n", (tv2.tv_usec - tv1.tv_usec));    return 0;}

 首先创建两个128KB的空文件。

dd bs=1024 count=128 if=/dev/zero of=./mmap_test

dd bs=1024 count=128 if=/dev/zero of=./mmap_test2

两个文件内容分别变成了'A'和'B',可以看出mmap领先不少:

Time of read/write: 134usTime of mmap/munmap/msync: 91us

7.2 mmap和/proc/xxx/maps解析

#include<stdio.h>#include<unistd.h>voidmain(){    sleep(1000);}

通过strace执行如上应用,得到如下的系统调用过程。

execve("./sleep", ["./sleep"], [/* 77 vars */]) = 0brk(NULL)                               = 0x1286000access("/etc/ld.so.nohwcap"F_OK)      = -1 ENOENT (No such file or directory)access("/etc/ld.so.preload"R_OK)      = -1 ENOENT (No such file or directory)open("/etc/ld.so.cache"O_RDONLY|O_CLOEXEC= 3fstat(3, {st_mode=S_IFREG|0644, st_size=145720...}) = 0mmap(NULL145720PROT_READMAP_PRIVATE30= 0x7fa2e0dec000--------------------------------------------------------1,只读私有文件映射,在a处释放close(3)                                = 0access("/etc/ld.so.nohwcap"F_OK)      = -1 ENOENT (No such file or directory)open("/lib/x86_64-linux-gnu/libc.so.6"O_RDONLY|O_CLOEXEC= 3read(3"\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"...832= 832fstat(3, {st_mode=S_IFREG|0755, st_size=1868984...}) = 0mmap(NULL4096PROT_READ|PROT_WRITEMAP_PRIVATE|MAP_ANONYMOUS-10= 0x7fa2e0deb000--------------------------------2,匿名映射一页,范围0x7fa2e0deb000-0x7fa2e0dec000,可读写mmap(NULL3971488PROT_READ|PROT_EXECMAP_PRIVATE|MAP_DENYWRITE30= 0x7fa2e0821000-------------------------------3,创建可读可执行,私有文件映射,范围0x7fa2e0821000-0x7fa2e0beb000mprotect(0x7fa2e09e10002097152PROT_NONE= 0-------------------------------------------------------------------------4,修改0x7fa2e09e1000-0x7fa2e0be1000属性,不可读写执行mmap(0x7fa2e0be100024576PROT_READ|PROT_WRITEMAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE30x1c0000= 0x7fa2e0be1000-----5,私有文件固定地址映射,可读写,0x7fa2e0be1000-0x7fa2e0be7000mmap(0x7fa2e0be700014752PROT_READ|PROT_WRITEMAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS-10= 0x7fa2e0be7000-----------6,私有匿名固定地址映射,可读写,0x7fa2e0be7000-0x7fa2e0beb000close(3)                                = 0mmap(NULL4096PROT_READ|PROT_WRITEMAP_PRIVATE|MAP_ANONYMOUS-10= 0x7fa2e0dea000--------------------------------7,匿名映射一页,范围0x7fa2e0dea000-0x7fa2e0deb000,可读写mmap(NULL4096PROT_READ|PROT_WRITEMAP_PRIVATE|MAP_ANONYMOUS-10= 0x7fa2e0de9000--------------------------------8,匿名映射一页,范围0x7fa2e0de9000-0x7fa2e0dea000,可读写arch_prctl(ARCH_SET_FS0x7fa2e0dea700= 0mprotect(0x7fa2e0be100016384PROT_READ= 0---------------------------------------------------------------------------9,将5创建的内存映射的0x7fa2e0be1000-0x7fa2e0be5000变成只读mprotect(0x6000004096PROT_READ)     = 0mprotect(0x7fa2e0e100004096PROT_READ= 0munmap(0x7fa2e0dec000145720)          = 0------------------------------------------------------------------------------a,释放1创建的内存映射nanosleep({10000}, 0x7ffef87e2c10)    = 0------------------------------------------------------------------------------此时cat /proc/xxx/maps,1创建的内存映射已经被释放exit_group(0)                           = ?+++ exited with 0 +++

 下面逐一分析mmap()/munmap()对进程映射空间的影响。

00400000-00401000 r-xp 00000000 08:08 3415949                            /home/al/mmap/sleep00600000-00601000 r--p 00000000 08:08 3415949                            /home/al/mmap/sleep00601000-00602000 rw-p 00001000 08:08 3415949                            /home/al/mmap/sleep7fa2e0821000-7fa2e09e1000 r-xp 00000000 08:08 3185985                    /lib/x86_64-linux-gnu/libc-2.23.so--------------3创建私有文件映射,可读可执行。7fa2e09e1000-7fa2e0be1000 ---p 001c0000 08:08 3185985                    /lib/x86_64-linux-gnu/libc-2.23.so--------------3创建私有文件映射,4修改属性从可读可执行变成不可读写不可执行。7fa2e0be1000-7fa2e0be5000 r--p 001c0000 08:08 3185985                    /lib/x86_64-linux-gnu/libc-2.23.so--------------3创建私有文件映射,5修改属性从可读可执行变成可读写,9修改属性为只读。7fa2e0be5000-7fa2e0be7000 rw-p 001c4000 08:08 3185985                    /lib/x86_64-linux-gnu/libc-2.23.so--------------3创建私有文件映射,5修改属性从可读可执行变成可读写。7fa2e0be7000-7fa2e0beb000 rw-p 00000000 00:00 0 -------------------------------------------------------------------------3创建私有文件映射,6覆盖创建的私有匿名固定地址映射,可读写。7fa2e0beb000-7fa2e0c11000 r-xp 00000000 08:08 3185983                    /lib/x86_64-linux-gnu/ld-2.23.so7fa2e0de9000-7fa2e0dec000 rw-p 00000000 00:00 0 -------------------------------------------------------------------------278三个匿名映射因为属性都是私有匿名映射,可读写,所以vma区域合并。7fa2e0e10000-7fa2e0e11000 r--p 00025000 08:08 3185983                    /lib/x86_64-linux-gnu/ld-2.23.so7fa2e0e11000-7fa2e0e12000 rw-p 00026000 08:08 3185983                    /lib/x86_64-linux-gnu/ld-2.23.so7fa2e0e12000-7fa2e0e13000 rw-p 00000000 00:00 0 7ffef87c3000-7ffef87e4000 rw-p 00000000 00:00 0                          [stack]7ffef87e4000-7ffef87e7000 r--p 00000000 00:00 0                          [vvar]7ffef87e7000-7ffef87e9000 r-xp 00000000 00:00 0                          [vdso]ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

 对于解释可以参考UNIX系统编程手册如下描述。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-03-28 04:40:11 HTTP/2.0 GET : https://f.mffb.com.cn/a/479194.html
  2. 运行时间 : 0.169326s [ 吞吐率:5.91req/s ] 内存消耗:4,772.77kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=b42fb1e6984b35b9125c0707fef48efd
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000950s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000823s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000316s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.004506s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000495s ]
  6. SELECT * FROM `set` [ RunTime:0.000192s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000618s ]
  8. SELECT * FROM `article` WHERE `id` = 479194 LIMIT 1 [ RunTime:0.001156s ]
  9. UPDATE `article` SET `lasttime` = 1774644011 WHERE `id` = 479194 [ RunTime:0.004340s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.000794s ]
  11. SELECT * FROM `article` WHERE `id` < 479194 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000475s ]
  12. SELECT * FROM `article` WHERE `id` > 479194 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000621s ]
  13. SELECT * FROM `article` WHERE `id` < 479194 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001168s ]
  14. SELECT * FROM `article` WHERE `id` < 479194 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.004914s ]
  15. SELECT * FROM `article` WHERE `id` < 479194 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.004551s ]
0.171112s