首页 > 系统相关 >linux内存管理(二)- vmalloc

linux内存管理(二)- vmalloc

时间:2024-06-11 10:56:45浏览次数:11  
标签:node addr area unsigned long vmalloc 内存 linux pages

个人笔记,谨慎观看.

先看看vmalloc是怎么实现的。它能在非连续物理内存之上建立连续的虚拟内存映射。这里有一篇博客Linux内存管理 (6)vmalloc - ArnoldLu - 博客园 (cnblogs.com)

调用链vmalloc->_vmalloc_node->_vmalloc_node_range

void *__vmalloc_node(unsigned long size, unsigned long align,
                gfp_t gfp_mask, int node, const void *caller)
{
    return __vmalloc_node_range(size, align, VMALLOC_START, VMALLOC_END,
                gfp_mask, PAGE_KERNEL, 0, node, caller);
}

 * Map them into contiguous kernel virtual space, using a pagetable
 * protection of @prot.
 *
 * Return: the address of the area or %NULL on failure
 */
void *__vmalloc_node_range(unsigned long size, unsigned long align,
            unsigned long start, unsigned long end, gfp_t gfp_mask,
            pgprot_t prot, unsigned long vm_flags, int node,
            const void *caller)
{
    if ((size >> PAGE_SHIFT) > totalram_pages()) {
        warn_alloc(gfp_mask, NULL,
            "vmalloc error: size %lu, exceeds total pages",
            real_size);
        return NULL;
    }

        size_per_node = size;
        if (node == NUMA_NO_NODE)
            size_per_node /= num_online_nodes();

//分配并初始化一个vm_struct area = __get_vm_area_node(real_size, align, shift, VM_ALLOC | VM_UNINITIALIZED | vm_flags, start, end, node, gfp_mask, caller); /* Allocate physical pages and map them into vmalloc space. */ ret = __vmalloc_area_node(area, gfp_mask, prot, shift, node); if (!ret) goto fail; return area->addr; ... }

totalram_pages是一个保存系统总可用内存页的全局变量。__get_vm_area_node分配一个vm_struct并初始化,这个结构描述了要分配的vmalloc。

static struct vm_struct *__get_vm_area_node(unsigned long size,
        unsigned long align, unsigned long shift, unsigned long flags,
        unsigned long start, unsigned long end, int node,
        gfp_t gfp_mask, const void *caller)
{
    BUG_ON(in_interrupt());
    size = ALIGN(size, 1ul << shift);//按page size对齐
        //分配一个vm_struct
    area = kzalloc_node(sizeof(*area), gfp_mask & GFP_RECLAIM_MASK, node);
    if (!(flags & VM_NO_GUARD))
        size += PAGE_SIZE;
        //分配一个vmap_area
    va = alloc_vmap_area(size, align, start, end, node, gfp_mask, 0);
        //设置vmap_area到vm_struct
    setup_vmalloc_vm(area, va, flags, caller);
    return area;
}

这里涉及到俩结构体。vm_struct, vmap_area.

struct vm_struct {
    struct vm_struct    *next;
    void            *addr;
    unsigned long        size;
    unsigned long        flags;
    struct page        **pages;
#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC
    unsigned int        page_order;
#endif
    unsigned int        nr_pages;
    phys_addr_t        phys_addr;
    const void        *caller;
};

描述vmalloc区域。

struct vmap_area {
    unsigned long va_start;
    unsigned long va_end;

    struct rb_node rb_node;         /* address sorted rbtree */
    struct list_head list;          /* address sorted list */

    /*
     * The following two variables can be packed, because
     * a vmap_area object can be either:
     *    1) in "free" tree (root is free_vmap_area_root)
     *    2) or "busy" tree (root is vmap_area_root)
     */
    union {
        unsigned long subtree_max_size; /* in "free" tree */
        struct vm_struct *vm;           /* in "busy" tree */
    };
    unsigned long flags; /* mark type of vm_map_ram area */
};

也用来描述vmalloc的那个区域,主要描述区域的范围,并且链接到一个全局rbtree上。alloc_vmap_area会找到当前地址最低的一个空闲区域。

__vmalloc_area_node是核心函数,分配物理内存,建立映射。

static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,
                 pgprot_t prot, unsigned int page_shift,
                 int node)
{
    const gfp_t nested_gfp = (gfp_mask & GFP_RECLAIM_MASK) | __GFP_ZERO;
    bool nofail = gfp_mask & __GFP_NOFAIL;
    unsigned long addr = (unsigned long)area->addr;
    unsigned long size = get_vm_area_size(area);
    unsigned long array_size;
    unsigned int nr_small_pages = size >> PAGE_SHIFT;
    unsigned int page_order;
    unsigned int flags;
    int ret;

    // 计算需要存储page指针的内存大小
    array_size = (unsigned long)nr_small_pages * sizeof(struct page *);

    // 可能会使用递归来分配给area->pages的内存,pages保存的是page指针数组
    /* Please note that the recursion is strictly bounded. */
    if (array_size > PAGE_SIZE) {
        area->pages = __vmalloc_node(array_size, 1, nested_gfp, node,
                    area->caller);
    } else {
        area->pages = kmalloc_node(array_size, nested_gfp, node);
    }

    // 如果没有enable CONFIG_HAVE_ARCH_HUGE_VMALLOC阶就是0
    set_vm_area_page_order(area, page_shift - PAGE_SHIFT);
    page_order = vm_area_page_order(area);

    // 分配内存页面,因为我们要的是不连续物理页面,对于大多数情形每次获取1页,这样就可以得到nr_pages个不连续的页面
    area->nr_pages = vm_area_alloc_pages(gfp_mask | __GFP_NOWARN,
        node, page_order, nr_small_pages, area->pages);

    // nr_vmalloc_pages应该是保存vmalloc分配总页数的全局变量
    atomic_long_add(area->nr_pages, &nr_vmalloc_pages);

    do {
        // 页面分配好了,建立映射吧,看起如果不允许失败那就要一直循环知道成功
        ret = vmap_pages_range(addr, addr + size, prot, area->pages,
            page_shift);
        if (nofail && (ret < 0))
            schedule_timeout_uninterruptible(1);
    } while (nofail && (ret < 0));

    return area->addr;
}
static int vmap_pages_range(unsigned long addr, unsigned long end,
        pgprot_t prot, struct page **pages, unsigned int page_shift)
{
    int err;
    // 用连续的虚拟地址区映射离散的物理页面
    err = vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
// 页面映射好了,刷一下cache flush_cache_vmap(addr, end); return err; }
int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
        pgprot_t prot, struct page **pages, unsigned int page_shift)
{
    int ret = kmsan_vmap_pages_range_noflush(addr, end, prot, pages,
                         page_shift);

    if (ret)
        return ret;
    return __vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
}

忽略kmsan相关的操作,直接看看__vmap_pages_range_noflush.

int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
        pgprot_t prot, struct page **pages, unsigned int page_shift)
{
    unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
...
    for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
        int err;

        // 每次只映射一个page
        err = vmap_range_noflush(addr, addr + (1UL << page_shift),
                    page_to_phys(pages[i]), prot,
                    page_shift);
        if (err)
            return err;

        addr += 1UL << page_shift;
    }

    return 0;
}
static int vmap_range_noflush(unsigned long addr, unsigned long end,
            phys_addr_t phys_addr, pgprot_t prot,
            unsigned int max_page_shift)
{
。。。
    start = addr;
    pgd = pgd_offset_k(addr);
    do {
        next = pgd_addr_end(addr, end);
        //终于看到熟悉的建页表的逻辑了
        err = vmap_p4d_range(pgd, addr, next, phys_addr, prot,
                    max_page_shift, &mask);
        if (err)
            break;
    } while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
。。。

    return err;
}

 从代码可以看到vmalloc的分配的页面是虚拟地址连续而物理页面不连续的,分配逻辑复杂,只能是按page分配,因此相对域kmalloc可以分配连续物理页面和小内存,vmalloc比较耗时,只针对较大内存。

标签:node,addr,area,unsigned,long,vmalloc,内存,linux,pages
From: https://www.cnblogs.com/banshanjushi/p/17990798

相关文章

  • 在Linux中,如何规划⼀台 Linux 主机,步骤是怎样?
    在Linux中规划一台主机是一个涉及多个方面的过程,以下是一个详细的步骤指南:1.确定需求首先,明确你的需求,包括预期的硬件配置、操作系统版本、软件需求等。这有助于为后续的磁盘分区和资源分配提供依据。2.选择合适的硬件配置处理器(CPU):选择适合应用场景的处理器。对于小型系......
  • Linux 相关基础知识(无关命令)
    Linux相关基础知识(无关命令)目录Linux相关基础知识(无关命令)1.Linux系统的启动过程2.linux内核的作用3.linux七种文件以及对应符号4.linux的目录结构5.MMU内存管理单元6.文件系统(FAT32和NTFS)7.内存1.Linux系统的启动过程1)上电 2)执行启动引导程序 3)加载linux内核......
  • 玄机——第一章 应急响应- Linux入侵排查 wp
    文章目录一、前言二、概览简介三、参考文章四、步骤(解析)步骤#1.1web目录存在木马,请找到木马的密码提交步骤#1.2服务器疑似存在不死马,请找到不死马的密码提交步骤#1.3不死马是通过哪个文件生成的,请提交文件名步骤#1.4黑客留下了木马文件,请找出黑客的服务器ip提交步骤......
  • 文件系统(六):一文看懂linux ext4文件系统工作原理
    liwen012024.06.09前言Linux系统中的ext2、ext3、ext4文件系统,它们都有很强的向后和向前兼容性,可以在数据不丢失的情况下进行文件系统的升级。目前ext4是一个相对较成熟、稳定且高效的文件系统,适用于绝大部分规模和需求的Linux环境。ext4它突出的特点有:数据分段管理、多块分......
  • linux:centos7升级glibc到2.36
    #安装依赖的东东yuminstall-ygccgcc-c++glibc-developenssl-developenssl-staticzlib-devellzmatk-develxz-develbisonbzip2bzip2-develncurses-develgdbm-develreadline-develsqlite-devellibffi-develtexinfolibmpc-devel#安装makecd/chz/ins......
  • Linux下安装python3.10.12环境
    安装python3编程环境检查是否安装python3python3-V安装python3依赖sudoyum-yinstallzlib-develbzip2-developenssl-develncurses-develsqlite-develreadline-develtk-develgdbm-develdb4-devellibpcap-develxz-devellibffi-devel下载python3资源包......
  • 【安装笔记-20240608-Linux-动态域名更新服务之YDNS】
    安装笔记-系列文章目录安装笔记-20240608-Linux-动态域名更新服务之YDNS文章目录安装笔记-系列文章目录安装笔记-20240608-Linux-动态域名更新服务之YDNS前言一、软件介绍名称:YDNS主页官方介绍二、安装步骤测试版本:openwrt-23.05.3-x86-64注册填写子域名激活邮箱更......
  • 情景题之小明的Linux实习之旅:linux实战练习1(上)【基础命令,权限修改,日志查询,进程管理...
    小明的Linux实习之旅:基础指令练习情景练习题背景介绍场景1:初识Linux,创建目录和文件场景2:权限管理,小明的权限困惑场景3:打包与解压,小明的备份操作场景4:使用Grep,小明的搜索技能场景5:系统服务管理,小明的首次接触场景6:进程管理,小明的多任务处理场景7:定时任务与系统状态场景8:d......
  • linux常用指令介绍
    linux简介ubuntu安装常用指令介绍软件包管理VIM编辑器的使用用户和组账户管理文件权限管理软件的安装及删除jdk,tomcat,eclipse远程登录文件目录操作命令ls显示文件和目录列表-l列出文件的详细信息-a列出当前目录所有文件,包含隐藏文件mkdir创建目录-p父目录不存在情......
  • 01、Linux网络设置
    目录1.1查看及测试网络1.1.1查看网络配置        1、查看网络接口地址2、查看主机状态3、查看路由表条目4、查看网络连接qing1.1.2测试网络连接1.测试网络连接2.跟踪数据包的路由路径3.测试DNS域名解析1.2设置网络地址参数1.2.1使用网络配置命令 ......