裁剪内核和新内核available差异大原因分析

文章探讨了在内核裁剪后,available内存数值比裁剪前增加10GB的问题。通过分析代码和内核参数,发现增加归因于watermark_scale_factor的调整,该参数影响内存回收的触发点。在新内核中,这个值的增大导致内存开始回收的阈值提高,从而在空闲内存较多时,available值显得更高。解决方案是调整watermark_scale_factor以适应系统的内存管理需求。

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

背景

host内核裁剪时会进行收益的比较,比如裁剪前用5.10最新内核得出内存数据,然后和裁剪后的内存数据进行对比。

在进行对比中,发现裁剪后的内存available比裁剪前多了10个G,有点不正常了,需要分析下这10个G到底是怎么多出来的

5.10 最新内核:

裁剪后的内核:

 

available计算方法:

available是通过 si_mem_available 函数获取的,分析 si_mem_available 就可以给出available计算方法

        available = global_zone_page_state(NR_FREE_PAGES) - totalreserve_pages;

        /*
         * Not all the page cache can be freed, otherwise the system will
         * start swapping. Assume at least half of the page cache, or the
         * low watermark worth of cache, needs to stay.
         */
        pagecache = pages[LRU_ACTIVE_FILE] + pages[LRU_INACTIVE_FILE];
        pagecache -= min(pagecache / 2, wmark_low);
        available += pagecache;

        /*
         * Part of the reclaimable slab and other kernel memory consists of
         * items that are in use, and cannot be freed. Cap this estimate at the
         * low watermark.
         */
        reclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B) +
                global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE);
        available += reclaimable - min(reclaimable / 2, wmark_low);

通过代码可以大致概括 available = free + pagecache + kernel_reclaimable - totalreserve_pages

上面公式里的 free 是 "free 命令" 里看到的 free,另外pagecache和kernel_reclaimable 也都是大于0的,所以 available 比 free 大10G,肯定是出在 totalreserve_pages 上了,所以接下来需要分析 totalreserve_pages 是怎么计算的

static void calculate_totalreserve_pages(void)
{
	struct pglist_data *pgdat;
	unsigned long reserve_pages = 0;
	enum zone_type i, j;

	for_each_online_pgdat(pgdat) {

		pgdat->totalreserve_pages = 0;

		for (i = 0; i < MAX_NR_ZONES; i++) {
			struct zone *zone = pgdat->node_zones + i;
			long max = 0;
			unsigned long managed_pages = zone_managed_pages(zone);

			/* Find valid and maximum lowmem_reserve in the zone */
			for (j = i; j < MAX_NR_ZONES; j++) {
				if (zone->lowmem_reserve[j] > max)
					max = zone->lowmem_reserve[j];
			}

			/* we treat the high watermark as reserved pages. */
			max += high_wmark_pages(zone);

			if (max > managed_pages)
				max = managed_pages;

			pgdat->totalreserve_pages += max;

			reserve_pages += max;
		}
	}
	totalreserve_pages = reserve_pages;
}

calculate_totalreserve_pages 函数会遍历numa node下所有的zone,然后把zone下的 lowmem_reserve 和 zone的 watermark[WMARK_HIGH] (也就是内存回收的高水位) 相加,然后得到 totalreserve_pages;

zone下的 lowmem_reserve 和 zone 的内存回收高水位他们的值分别是多少,去哪里看呢,可以通过 /proc/zoneinfo 查看。

lowmem_reserve:

cat /proc/zoneinfo | grep protection 输出的就是lowmem_reserve,有两行,一行表示numa0, 一行表示numa1,数组里几个数字分别表示不同zone下的lowmem_reserve,具体是 dma zone, dma32 zone, normal zone, moveable zone, device zone。

[root@localhost /mnt]# cat /proc/zoneinfo | grep protection
        protection: (0, 1221, 9401, 9401, 9401)
        protection: (0, 0, 8180, 8180, 8180)

[root@localhost ~]# cat /proc/zoneinfo | grep protection
        protection: (0, 1221, 9401, 9401, 9401)
        protection: (0, 0, 8180, 8180, 8180)

zoneinfo里的 protection就是 lowmem_reserve ,能看出5.10单机最新内核和我们裁剪过的内核,两者值是一样的

所以导致available不同的只可能是 watermark[WMARK_HIGH],所以接下来分析zone的高水位是怎么算出来的。

zone高水位值 high_wmark_pages:

zone的low水位和high水位是在 __setup_per_zone_wmarks 里设置的,需要分析 __setup_per_zone_wmarks 函数

 
static void __setup_per_zone_wmarks(void)
{
	unsigned long pages_min = min_free_kbytes >> (PAGE_SHIFT - 10);
	unsigned long lowmem_pages = 0;
	struct zone *zone;
	unsigned long flags;

	/* Calculate total number of !ZONE_HIGHMEM pages */
	for_each_zone(zone) {
		lowmem_pages += zone_managed_pages(zone);
	}

	for_each_zone(zone) {
		u64 tmp;

		spin_lock_irqsave(&zone->lock, flags);
		tmp = (u64)pages_min * zone_managed_pages(zone);
		do_div(tmp, lowmem_pages);
        /*
         * If it's a lowmem zone, reserve a number of pages
         * proportionate to the zone's size.
         */
        zone->_watermark[WMARK_MIN] = tmp;

		/*
		 * Set the kswapd watermarks distance according to the
		 * scale factor in proportion to available memory, but
		 * ensure a minimum size on small systems.
		 */
		tmp = max_t(u64, tmp >> 2,
			    mult_frac(zone_managed_pages(zone),
				      watermark_scale_factor, 10000));

		zone->watermark_boost = 0;
		zone->_watermark[WMARK_LOW]  = min_wmark_pages(zone) + tmp;
		zone->_watermark[WMARK_HIGH] = min_wmark_pages(zone) + tmp * 2;

		spin_unlock_irqrestore(&zone->lock, flags);
	}
}

这个函数初步看会很比较迷糊,linux kernel的一些写法和普通的用户态程序相比会有些奇怪,mult_frac是用第一个参数乘以 第二个参数分子和第三个参数的分母组成的分数。

mult_frac(zone_managed_pages(zone), watermark_scale_factor, 10000) 的意思相当于 总内存 * watermark_scale_factor / 10000, watermark_scale_factor 表示是总内存的万分之几。

watermark_scale_factor 这是个内核参数,可以配置,也可以使用默认值。

于是sysctl -a | grep watermark_scale_factor 发现两个内核版本之间这个值果然不一样。

结论

我们新内核的代码把 watermark_scale_factor 改成了100,这样高水位内存值自然就上去了。

那为什么我们内核裁剪的内核available会多些了,在空闲内存都是25G的情况下,available增加必然和 watermark_scale_factor有关,因为我是基于master分支打的包,于是查看master的watermark_scale_factor相关改动,https://console.google./devops/icode/cbu/linux-5-10/commits/e35d40db5c44f1ceae7b240c547fcbc0e1a2fb1a/mm/page_alloc.c,原来前段时间解决pdd抖动

所以是因为这个改动导致的available减少10G的,并不是我们内核裁剪增加了10G的available

watermark_scale_factor 决定多少空闲内存的时候开始回收内存,

10, 是千分之一整个系统内存的时候开始回收,1T的物理机,也就是只剩下1G的时候开始回收,2g的时候结束回收。

100, 是百分之一整个系统内存的时候开始回收,1T的物理机,也就是只剩下10G的时候开始回收,20G的时候结束回收。

对于我们的1T的机器,只有30g左右不是内存大页,10g开始回收内存太频繁了。

最近内核大佬在解决pdd虚拟机抖动问题的时候把这个也解决了,所以下次出的5.10内核rpm包是不会有available和free差异很大的情况了。

<think>好的,我现在需要解决用户的问题:在MTK安卓手机的/sys/kernel/debug/路径下没有tracing节点,该怎么办。用户之前询问过如何确认某个内核函数是否被调用,其中提到了使用ftrace的方法,但现在遇到了路径不存在的问题。 首先,我需要回忆一下Linux内核调试相关的知识。Ftrace是内核的跟踪工具,通常挂载在/sys/kernel/debug/tracing下。但安卓系统可能因为安全或配置原因禁用了这个功能。MTK平台(联发科)的安卓手机可能在内核编译时没有开启ftrace,或者debugfs没有被正确挂载。 接下来,用户的问题可能涉及几个方面:内核配置是否支持ftrace,debugfs是否被挂载,权限问题,或者系统版本的限制。我需要逐步分析可能的原因解决方法。 首先,检查debugfs是否挂载。虽然用户提到路径不存在,但可能需要手动挂载。在Linux系统中,debugfs通常挂载在/sys/kernel/debug,但某些系统可能需要手动执行mount命令。可以尝试使用`mount -t debugfs none /sys/kernel/debug`来挂载,但需要root权限。 其次,确认内核是否编译了ftrace相关的配置。如果内核编译时没有启用CONFIG_FTRACE、CONFIG_FUNCTION_TRACER等选项,那么tracing目录就不会出现。对于MTK的安卓设备,厂商可能为了节省资源或提高安全性,关闭了这些调试选项。 另外,安卓系统的SELinux策略可能限制了访问debugfs的权限。即使debugfs存在,普通用户或没有足够权限的进程可能无法访问。需要检查SELinux的状态,并尝试临时禁用或调整策略,但这可能需要root权限。 还有可能是内核版本的问题。较旧的安卓内核可能不支持某些ftrace功能,或者路径有所变化。需要确认设备的内核版本,并查阅对应的文档。 如果上述方法都无效,可能需要考虑替代方案,比如使用其他内核跟踪工具(如trace-cmd),或者使用动态打印(dynamic debug)功能,或者在代码中添加printk语句进行调试。 总结可能的解决步骤: 1. 检查并挂载debugfs。 2. 确认内核配置支持ftrace。 3. 检查SELinux权限设置。 4. 使用root权限尝试访问。 5. 寻找替代的调试方法。 同时,需要考虑到用户的操作环境是MTK安卓手机,可能涉及到系统分区只读、需要解锁Bootloader、获取root权限等问题,这些都可能影响解决方案的实施。用户可能需要具备一定的设备权限才能进行这些操作,否则可能需要寻找其他不需要内核调试的方法,比如通过日志或应用层工具间接观察函数调用情况。</think>### **MTK安卓设备缺失/sys/kernel/debug/tracing的解决方案** ```mermaid graph TD A[问题现象] --> B{可能原因} B --> C1(debugfs未挂载) B --> C2(内核未启用ftrace) B --> C3(SELinux限制) B --> C4(内核裁剪) B --> C5(路径差异) C1 --> D1[手动挂载debugfs] C2 --> D2[重编译内核] C3 --> D3[临时关闭SELinux] C4 --> D4[使用替代方案] C5 --> D5[搜索实际路径] ``` --- ### **方案 1:手动挂载debugfs** ```bash # 需要root权限 su mount -t debugfs none /sys/kernel/debug # 验证挂载结果 ls /sys/kernel/debug/tracing ``` - **典型输出**: ``` available_events current_tracer tracing_on ``` --- ### **方案 2:检查内核配置** ```bash # 查看内核编译配置(需root) cat /proc/config.gz | gunzip | grep CONFIG_FTRACE # 预期应包含的配置项 CONFIG_FTRACE=y CONFIG_FUNCTION_TRACER=y CONFIG_DEBUG_FS=y ``` - **若未启用**:需要重新编译内核(需解锁bootloader) --- ### **方案 3:SELinux策略调整** ```bash # 临时关闭SELinux(重启失效) setenforce 0 # 查看当前模式 getenforce # 应显示Permissive ``` --- ### **方案 4:使用替代调试方法** #### **动态打印调试** ```bash # 查看所有可用的动态打印点 cat /sys/kernel/debug/dynamic_debug/control # 启用特定函数的打印(需内核支持) echo 'func init_ddr_vendor_size +p' > /sys/kernel/debug/dynamic_debug/control ``` #### **Kprobe工具** ```bash # 需要CONFIG_KPROBES=y echo 'p:myprobe init_ddr_vendor_size' > /sys/kernel/debug/tracing/kprobe_events echo 1 > /sys/kernel/debug/tracing/events/kprobes/myprobe/enable ``` --- ### **方案 5:查找特殊路径** 某些MTK设备可能使用非标准路径: ```bash # 搜索可能的路径 find /sys -name "tracing" 2>/dev/null find /proc -name "trace*" 2>/dev/null # 常见替代路径案例: /sys/kernel/tracing /proc/trace ``` --- ### **内核版本适配情况** | 内核版本 | FTRACE支持 | 典型路径 | |---------|------------|----------| | 4.4.x | ✔️ | /sys/kernel/debug/tracing | | 4.9.x | ✔️ | /sys/kernel/tracing | | 4.14.x | ✔️ | 支持多实例路径 | | 4.19.x | ✔️ | /sys/kernel/tracing | --- ### **MTK设备特殊处理** 1. **工程模式**: * 拨号界面输入`*#*#3646633#*#*` * 在`Log and Debugging`中开启调试功能 2. **内核模块加载**: ```bash insmod /system/lib/modules/ftrace.ko ``` 3. **厂商定制工具**: - 使用MTKLogger工具抓取内核日志 - 通过`cat /proc/kmsg`实时监控 --- ### **验证流程图** ```mermaid graph TD Start[开始] --> CheckRoot[检查root权限] CheckRoot -- 无权限 --> GetRoot[获取root] CheckRoot -- 已root --> MountDebugfs[挂载debugfs] MountDebugfs --> CheckPath[检查路径存在] CheckPath -- 存在 --> Success[可进行ftrace调试] CheckPath -- 不存在 --> CheckKernel[检查内核配置] CheckKernel -- 支持ftrace --> CheckSELinux[调整SELinux] CheckKernel -- 不支持 --> UseAlt[使用替代方案] ``` --- ### **常见错误处理** ```bash # 出现"Permission denied"错误时的处理 chmod 755 /sys/kernel/debug chmod 644 /sys/kernel/debug/tracing/* # 出现"No such device"错误 dmesg | grep debugfs # 查看内核是否支持debugfs ``` > 📌 **终极建议**:对于量产机型,建议使用`trace-cmd`工具替代直接操作sysfs: > ```bash > trace-cmd record -p function_graph -g init_ddr_vendor_size > trace-cmd report > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值