Linux内存管理之vmalloc与low_memory

探讨在PowerPC架构e500芯片中如何通过调整low_memory大小实现内存区域控制,涉及MEMORY_DMA、MEMORY_NORMAL及MEMORY_HIGHMEM区域划分,重点分析lowmem_end_addr计算与调整过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思考:如何在PowerPC架构e500芯片实现low_memory的大小控制?

内存区域zone

三个内存区域zone,分别为ZONE_DMA,ZONE_NORMAL和ZONE_HIGHMEM,在e500上ZONE_NORMAL实际划分的内存为空,所以所有低端内存(low_memory)都划分到ZONE_DMA中;

内存区域的划分信息保存在max_zone_pfns中,其定义如下:

static unsigned long max_zone_pfns[MAX_NR_ZONES] = {
    [0 ... MAX_NR_ZONES - 1] = ~0UL
};

每个zone都有一个max_zone_pfn对应来限定内存页帧上限,同时作为更高内存区域内存页帧的下限。在paging_init函数中计算各个zone区域的划分信息:

/*
 * paging_init() sets up the page tables - in fact we've already done this.
 */
void __init paging_init(void)
{
    unsigned long long total_ram = memblock_phys_mem_size();
    phys_addr_t top_of_ram = memblock_end_of_DRAM();
    enum zone_type top_zone;

#ifdef CONFIG_PPC32
    unsigned long v = __fix_to_virt(__end_of_fixed_addresses - 1);
    unsigned long end = __fix_to_virt(FIX_HOLE);

    for (; v < end; v += PAGE_SIZE)
        map_page(v, 0, 0); /* XXX gross */
#endif

#ifdef CONFIG_HIGHMEM
    map_page(PKMAP_BASE, 0, 0);    /* XXX gross */
    pkmap_page_table = virt_to_kpte(PKMAP_BASE);

    kmap_pte = virt_to_kpte(__fix_to_virt(FIX_KMAP_BEGIN));
    kmap_prot = PAGE_KERNEL;
#endif /* CONFIG_HIGHMEM */

    printk(KERN_DEBUG "Top of RAM: 0x%llx, Total RAM: 0x%llx\n",
           (unsigned long long)top_of_ram, total_ram);
    printk(KERN_DEBUG "Memory hole size: %ldMB\n",
           (long int)((top_of_ram - total_ram) >> 20));

#ifdef CONFIG_HIGHMEM
    top_zone = ZONE_HIGHMEM;
    limit_zone_pfn(ZONE_NORMAL, lowmem_end_addr >> PAGE_SHIFT);
#else
    top_zone = ZONE_NORMAL;
#endif

    limit_zone_pfn(top_zone, top_of_ram >> PAGE_SHIFT);
    zone_limits_final = true;
    free_area_init_nodes(max_zone_pfns);

    mark_nonram_nosave();
}

首先看函数尾部,分别调用两次limit_zone_pfn对ZONE_NORMAL和ZONE_HIGHMEM页帧进行限制,这里需要注意的一点是因为没有对ZONE_DMA进行特殊处理,因此会导致ZONE_DMA占用所有低端内存,ZONE_NORMAL为空;

ZONE_HIGHMEM的设置用到了top_of_ram,为RAM的最高空间,通过memblock可以查询到,在内存节点的添加以及后面的一些运算过程中会设置物理内存的上限;

ZONE_NORMAL的设置用到了lowmem_end_addr即低端内存的末尾,其数值又下面计算过程决定:
在MMU_init中:

lowmem_end_addr = memstart_addr + total_lowmem;

其值直接依赖于total_lowmem,该计算过程进行了两次,分别在adjust_total_lowmem之前和之后,而在adjust_total_lowmem中对__max_low_memory进行了计算,从而将可能进一步限定total_lowmem的取值,那么可以看到total_lowmem(在可能被__max_low_memory覆盖之前)和__max_low_memory的最小值决定了lowmem_end_addr的值,并且此时将更小者赋值给了total_lowmem作为最新值;total_lowmem即为RAM的大小;
__max_low_memory通过下面过程取得:

1. 定义和初始化

#define CONFIG_LOWMEM_SIZE 0x30000000
#define MAX_LOW_MEM    CONFIG_LOWMEM_SIZE

/* max amount of low RAM to map in */
unsigned long __max_low_memory = MAX_LOW_MEM;

其中CONFIG_LOWMEM_SIZE为内核.config中定义的大小;


2. 内核启动参数bootargs中vmalloc参数的影响

/*
 * vmalloc=size forces the vmalloc area to be exactly 'size'
 * bytes. This can be used to increase (or decrease) the low
 * memory  area. Thus this can be also used to decrease (or increase)
 * low memory area.
 */
static int __init early_vmalloc(char *arg)
{
    unsigned long vmalloc_reserve = memparse(arg, NULL);

    PRT("vmalloc_reserve = 0x%lx", vmalloc_reserve);
    if (vmalloc_reserve < SZ_16M) {
        vmalloc_reserve = SZ_16M;
        PRT("vmalloc area too small, limiting to %luMB\n",
            vmalloc_reserve >> 20);
    }

    if (vmalloc_reserve > VMALLOC_RESERVE_MAX) {
        vmalloc_reserve = VMALLOC_RESERVE_MAX;
        PRT("vmalloc area is too big, limiting to %luMB\n",
            vmalloc_reserve >> 20);
    }

    /* low memory aligned 16M*/
    PRT("__max_low_memory = 0x%lx", __max_low_memory);
    __max_low_memory = __pa(IOREMAP_TOP) - VMALLOC_OFFSET - vmalloc_reserve;
    __max_low_memory &= ~(SZ_16M - 1);
    
    PRT("__max_low_memory = 0x%lx", __max_low_memory);
    return 0;
}
early_param("vmalloc", early_vmalloc);

early_vmalloc函数根据vmalloc参数解析出vmalloc_reserve的大小,大小有一定的限制,指定最小16M,最大为1G的内核虚拟地址空间除去一个VMALLOC_OFFSET和64M,64M应该是作为系统低端保障内存的空间大小;
接下来直接算出__max_low_memory的大小,并进行16M对其:

    __max_low_memory = __pa(IOREMAP_TOP) - VMALLOC_OFFSET - vmalloc_reserve;
    __max_low_memory &= ~(SZ_16M - 1);

3. adjust_total_lowmem进行调整,这是CONFIG_FSL_BOOKE架构特性特有

取__max_low_memory和total_lowmem中较小者,然后通过map_mem_in_cams接口进行映射建立tlb条目,将可以映射的最大RAM作为最终结果重新赋值给__max_low_memory;可以看到如果tlb条目可映射的RAM数量较小,则__max_low_memory会进一步在此被修改变小

按照上面1/2/3条逐步进行限制,如果bootargs未配置vmalloc参数则无第2条执行;

回到最开始的部分,通过__max_low_memory参数最终的值和total_lowmem,在MMU_init中确定total_lowmem的最终值,从而决定了lowmem_end_addr的大小,相对total_lowmem偏移memstart_addr,在此偏移为0;进而决定了ZONE_DMA和ZONE_NORMAL的内存布局

<think>我们正在讨论Linux内核配置选项CONFIG_LOW_RAM_DEVICE和CONFIG_LOW_RAM_2GB_DEVICE。这两个选项通常用于针对低内存设备(如仅有1GB或2GBRAM的设备)进行内核优化。根据用户提供的引用[1],虽然它展示的是device_initialize函数,但并没有直接提到这两个配置选项,因此我们需要结合内核知识进行解释。首先,这两个配置选项通常出现在Android/Linux内核中,特别是在移动设备上,用于调整内存管理、进程管理以及其他子系统,以便在低内存设备上获得更好的性能。1.CONFIG_LOW_RAM_DEVICE:当定义为true时,表示设备内存较少(通常指1GB或更少)。启用此选项后,内核会调整一些参数以降低内存消耗,例如:-减少内存页面的缓存(如inodecache,dentrycache等)-调整内存回收策略,使其更积极地回收内存-减少一些内核数据结构的大小(如进程描述符、缓冲区等)-禁用一些内存消耗大的特性2.CONFIG_LOW_RAM_2GB_DEVICE:当定义为true时,表示设备内存为2GB。它也是低内存设备,但内存比1GB设备多,因此调整的参数可能没有1GB设备那么激进。它也会调整内存相关参数,但目标是在性能和内存消耗之间取得更好的平衡。问题:如果这两个配置被设置为true,可能会带来一些副作用,例如:-由于更积极的内存回收,可能导致应用启动变慢(因为需要更频繁地从ZRAM或文件系统中读取)-系统缓存减少,可能导致文件操作变慢-多任务处理能力下降(因为系统倾向于保留更少的后台进程)解决方案:-如果遇到性能问题(例如应用启动慢、多任务切换卡顿),可以考虑调整这些配置选项,但需要权衡内存消耗。注意:这些选项通常在编译内核时设置,修改需要重新编译内核。-针对低内存设备,也可以考虑使用ZRAM(压缩内存技术)来扩展可用内存,并调整ZRAM的大小和压缩算法。-调整虚拟内存参数(如swappiness、vfs_cache_pressure等)以改善内存回收行为。-在用户空间,可以通过调整LMK(LowMemoryKiller)参数来控制杀死后台进程的阈值,从而为前台应用保留更多内存。注意:如果设备的内存本身大于2GB,那么不应该将这两个选项设为true,否则会导致系统资源未被充分利用,性能下降。根据引用[1],虽然其中没有直接提到这两个配置,但我们可以推断在设备初始化时,可能会根据这些配置设置一些设备相关的参数。例如,在device_pm_init(dev)中可能会根据系统内存配置调整电源管理策略。由于用户提供的引用信息有限,我们主要依靠对内核配置选项的理解进行分析。为了进一步帮助用户,我们生成以下相关问题:§§相关问题§§1.如何在内核中启用ZRAM以优化低内存设备性能?2.如何调整Android系统的LMK参数?3.在低内存设备上,有哪些推荐的内核参数调整?4.如何重新编译内核以修改CONFIG_LOW_RAM_DEVICE配置?</think> ### CONFIG_LOW_RAM_DEVICE 和 CONFIG_LOW_RAM_2GB_DEVICE 配置的影响及解决方案 这两个内核配置选项主要用于优化低内存设备(通常指≤2GB RAM的Android设备),启用后会对系统资源管理产生以下影响: --- #### 📌 配置为 true 的主要影响 1. **内存压缩优化** - 启用更激进的ZRAM(虚拟内存压缩)策略,例如: ```c // drivers/staging/android/lowmemorykiller.c #ifdef CONFIG_LOW_RAM_DEVICE static int lowmem_minfree[6] = { 18432, 23040, 27648, 32256, 55296, 80640 }; #endif ``` 调整了内存回收阈值,更快触发内存回收[^1]。 2. **进程管理调整** - 降低 `max_threads` 数量(约减少50%),限制并发进程数 - 优化cgroup配置,减少系统后台服务的内存占用 3. **文件系统缓存优化** - 显著降低 `dentry` 和 `inode` 缓存上限(如从默认10%降至5%) - 减少文件预读(readahead)缓冲区大小 4. **驱动层优化** 您引用的 `device_initialize` 中相关初始化可能被简化: ```c #if !defined(CONFIG_LOW_RAM_DEVICE) INIT_LIST_HEAD(&dev->links.suppliers); // 非低配设备才初始化供应商链表 #endif ``` 5. **功能裁剪** - 禁用透明大页(THP)等内存密集型功能 - 减少DMA内存池数量(参考 `INIT_LIST_HEAD(&dev->dma_pools)`) --- #### ⚠️ 常见问题解决方案 1. **应用频繁被杀(后台保活失败)** **原因**:LMK(低内存回收器)阈值过于激进 **解决**: ```bash # 调整最小空闲内存阈值 (单位: 4KB pages) echo "18432,23040,27648,55296,80640,147456" > /sys/module/lowmemorykiller/parameters/minfree ``` 2. **ZRAM压缩导致的CPU占用过高** **优化方案**: ```bash # 改用LZ4压缩算法 (需内核支持) echo lz4 > /sys/block/zram0/comp_algorithm echo 1.5G > /sys/block/zram0/disksize ``` 3. **文件操作卡顿** **调优命令**: ```bash # 增加dentry缓存保留比例 echo 20 > /proc/sys/vm/vfs_cache_pressure # 启用swap即使内存未耗尽 echo 100 > /proc/sys/vm/swappiness ``` 4. **设备驱动兼容性问题** 检查驱动中类似以下条件编译: ```c #ifdef CONFIG_LOW_RAM_2GB_DEVICE dev->dma_pools = simplified_dma_init(); // 简化DMA初始化 #endif ``` 若出现DMA异常,需验证驱动是否完整支持低内存配置 --- #### 🔧 配置建议 1. **典型场景启用规则**: | 设备 RAM | 启用配置 | |---------|----------| | ≤1 GB | `CONFIG_LOW_RAM_DEVICE=y` | | 2 GB | `CONFIG_LOW_RAM_2GB_DEVICE=y` | | >2 GB | 两者均禁用 | 2. **性能平衡建议**: - 在 `defconfig` 中启用 `CONFIG_ZSMALLOC_STAT` 监控内存分配 - 配合 `CONFIG_ANDROID_LOW_MEMORY_KILLER` 使用最新优化策略 > 📝 注意:修改配置需重新编译内核,验证时建议使用内核内存分析工具: > ```bash > dmesg | grep -i "lowmem" # 检查LMK日志 > cat /proc/meminfo | grep -e Swap -e Vmalloc # 监控ZRAM使用 > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值