![[linux内存管理] 第023篇 watermark详解](https://halo-19274848.oss-cn-shanghai.aliyuncs.com/2025/06/halo_pw9ljvu.png?x-oss-process=image/resize,w_800,m_lfit)
[linux内存管理] 第023篇 watermark详解
本文探讨了 Linux 内存管理中的水位机制,特别是 `zoned page frame allocator` 如何使用水位来控制内存分配和回收。文章首先介绍了 `struct zone` 结构体和三种水位 `WMARK_MIN`、`WMARK_LOW` 和 `WMARK_HIGH` 的概念及其作用。随后,文章详细分析了水位的初始化过程,包括计算 `min_free_kbytes`、更新内存区水位、刷新内存区统计阈值和初始化低内存保留等步骤。接着,文章讨论了快速分配和慢速分配中的水位检测机制,以及 `kswapd` 和内存规整过程中的水位检测。最后,文章强调了调整内存水位的重要性,以及如何根据不同业务场景进行优化。
![[linux内存管理] 第21篇 buddy内存管理之快速分配](https://halo-19274848.oss-cn-shanghai.aliyuncs.com/2025/06/halo_zarkuub.png?x-oss-process=image/resize,w_800,m_lfit)
[linux内存管理] 第21篇 buddy内存管理之快速分配
### 4. 慢速分配 当快速分配无法满足分配请求时,会进入慢速分配流程。 ```c static struct page *__alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, struct alloc_context *ac) { gfp_t alloc_flags = ALLOC_WMARK_LOW | ALLOC_CPUSET; unsigned int alloc_flags_idx = ALLOC_INDEX(gfp_mask); struct zone *zone; nodemask_t *alloc_nodemask; int alloc_nid; struct pglist_data *pgdat; struct page *page = NULL; unsigned long mark; /* The fallback path may need to allocate memory on another node */ alloc_nid = numa_node_id(); /* * On the first allocation attempt, we try to allocate from the * preferred zone only. On the second attempt, we try to allocate * from the whole zonelist. */ if (ac->preferred_zoneref->zone != NULL) zone = ac->preferred_zoneref->zone; else zone = NULL; /* * The first allocation attempt can ignore memory policy. For the * second attempt, we need to honor the memory policy. */ if (ac->nodemask == &cpuset_current_mems_allowed) { alloc_nodemask = NULL; alloc_flags &= ~ALLOC_CPUSET; } else { alloc_nodemask = ac->nodemask; } pgdat = zone->zone_pgdat; alloc_flags |= pgdat->alloc_flags; /* If we are re-filling the per-cpu pageset, then honor the * zone reclaim mode. */ if (order == 0 && ac->pcp) { alloc_flags |= pgdat->alloc_flags; if (!node_reclaim_enabled()) alloc_flags &= ~ALLOC_CPUSET; } /* * We have already checked if the zone is online and the node is * online. If the zone is not online, we cannot use it for * allocation. If the node is not online, then it is possible * that the zone is online but the node is not. If we cannot use * the zone, then we must use a different node. */ if (!zone->node->online) { zone = NULL; alloc_nid = numa_next_node(alloc_nid, alloc_nodemask); if (alloc_nid == NUMA_NO_NODE) { if (alloc_flags_idx == ALLOC_INDEX(GFP_KERNEL)) { return NULL; } alloc_flags_idx = ALLOC_INDEX(GFP_KERNEL); alloc_flags &= ~ALLOC_CPUSET; alloc_flags &= ~ALLOC_WMARK_LOW; alloc_flags |= ALLOC_WMARK_MIN; goto retry; } pgdat = NODE_DATA(alloc_nid); alloc_flags |= pgdat->alloc_flags; } /* We are going to sleep. If this allocation is for a page cache * page, then we want to go to sleep only if the dirty pages are * within the limit. */ if (!ac->spread_dirty_pages && !node_dirty_ok(pgdat)) { if (alloc_flags_idx == ALLOC_INDEX(GFP_KERNEL)) { return NULL; } alloc_flags_idx = ALLOC_INDEX(GFP_KERNEL); alloc_flags &= ~ALLOC_CPUSET; alloc_flags &= ~ALLOC_WMARK_LOW; alloc_flags |= ALLOC_WMARK_MIN; goto retry; } /* * The zone has enough free pages. We can allocate a new page. */ if (zone_page_state(zone, NR_FREE_PAGES) >= zone_page_state(zone, NR_ZONE_FREE_MIN) + (1 << order)) { alloc_flags &= ~ALLOC_CPUSET; } retry: alloc_flags |= gfp_to_alloc_flags(gfp_mask, alloc_flags_idx); /* If the zone is not online, we need to allocate from another zone. */ if (!zone) { if (!alloc_nodemask) { zone = zone_reclaim(zone, gfp_mask, order); if (zone) goto retry; } else { zone = zone_reclaim_nodemask(pgdat, gfp_mask, order); if (zone) goto retry; } } /* * If we are here, it means that we have found a zone that has enough * free pages and we can allocate a new page. */ if (zone_page_state(zone, NR_FREE_PAGES) >= zone_page_state(zone, NR_ZONE_FREE_MIN) + (1 << order)) { page = get_page_from_freelist(gfp_mask, order, alloc