Linux中的内存分配和释放之__alloc_pages()函数分析

本文详细分析了Linux内核中的__alloc_pages()函数,讲解了内存分配过程中涉及的gfp_mask、伙伴系统、等待策略等关键点,并介绍了相关宏定义和控制字的含义,为理解内存分配提供了深入见解。

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

  在上篇文章的结尾,我们说会在接下来的文章分析分配函数的具体代码,结合我上篇文章说的伙伴机制和冷热区的概念,更好得去理解这个分配过程。好了,我们不再多说了,我们现在开始分析代码吧。

  struct page * fastcall __alloc_pages(unsigned int gfp_mask, unsigned int order,struct zonelist *zonelist)

  {
       const int wait = gfp_mask & __GFP_WAIT;//gfp_mask是申请内存时用到的控制字,这一句就是为了检测我们的控制字里面是

                                                                       //否有__GPF_WAIT这个属性,我们先来看些宏定义吧,这样你会更清楚的。

        //#define GFP_LEVEL_MASK (__GFP_WAIT|__GFP_HIGH|__GFP_IO|__GFP_FS| /
                                                      __GFP_COLD|__GFP_NOWARN|__GFP_REPEAT| /
                                                      __GFP_NOFAIL|__GFP_NORETRY|__GFP_NO_GROW|__GFP_COMP)

        //#define GFP_ATOMIC (__GFP_HIGH)
        //#define GFP_NOIO (__GFP_WAIT)
        //#define GFP_NOFS (__GFP_WAIT | __GFP_IO)
        //#define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS)
        //#define GFP_USER (__GFP_WAIT | __GFP_IO | __GFP_FS)
       //#define GFP_HIGHUSER (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HIGHMEM)

     //可以看到上面的每个define都代表一个控制字,上面大部分都有__GPF_WAIT,只是GFP_ATOMIC是不包含的。如果当申请内存的控制字有__GFP_WAIT时,我们就要求一直等待到申请到内存为止,反之进程就进入睡眠状态,不进行等待。

  

   unsigned long min;
   struct zone **zones, *z;
   struct page *page;
   struct reclaim_state reclaim_state;
   struct task_struct *p = current;
   int i;
   int alloc_type;
   int do_retry;
   int can_try_harder;

 

   might_sleep_if(wait);//如果wait不等于0,则调用该函数的进程可能休眠。

   can_try_harder = (unlikely(rt_task(p)) && !in_interrupt()) || !wait;//如果当前进程是实时进程的话同时又不在软中断和硬中断

                                                                                               //或者是wait=0。这样can_try_harder=1。

                              //#define rt_task(p)  (unlikely((p)->prio < MAX_RT_PRIO))当前进程的优先级prio是否小于MAX_RT_PRIO

                       //#define MAX_RT_PRIO  MAX_USER_RT_PRIO其中MAX_USER_RT_PRIO=100。如果prio<100

                       //我们就说当前进程是实时进程。

   zones = zonelist->zones;//zonelist是struct node中的一个成员,它表示系统内所有normal内存页区的连接链表。

                                      //我们这里的zonelist->zones是这个节点的normal内存zone的struct zoon指针。我们一般把zones称为

                                    //struct zoon结构指针列表,这个表是以本次内存node的对应类型页区的struct zoon为头指针,即头指针是

                                   //指向struct zoon这个结构体的。整个表都是结构类型为struct zoon的指针。

   if (unlikely(zones[0] == NULL)) {

       return NULL;

   }//如果发现头指针为空,即没有指向struct zoon的有效指针,我们就直接返回错误。

   alloc_typ

<think>好的,我需要解释一下alloc_pageorder=0情况下alloc_pages_node函数的作用机制。首先,用户可能是在学习Linux内核的内存管理,尤其是页分配相关的函数。 用户提到了alloc_pagealloc_pages_node在order=0时的情形。我应该先区分这两个函数alloc_page通常是一个宏,可能调用alloc_pages,而alloc_pages_node则是更底层的函数,允许指定节点标志。 需要明确order=0意味着分配一个物理页(4KB)。这两个函数在这种情况下是否等价?可能alloc_page内部调用了alloc_pages_node,但需要确认参数传递,比如gfp标志节点选择。 接下来要说明函数原型参数。例如,alloc_pages_node需要节点ID、gfp_maskorder。当order=0时,就是单页分配。而alloc_page可能隐藏了节点参数,使用当前节点或NUMA_NO_NODE? 需要解释内存分配策略,比如GFP标志的影响,节点的重要性(NUMA架构下),以及可能的失败情况。还要提到可能的封装关系,比如alloc_page可能是alloc_pages的简化版。 还要注意上下文,比如是否可以在中断上下文中调用,GFP_ATOMIC的使用。此外,函数返回的是page结构指针还是其他形式,比如struct page *,这关系到后续如何使用分配的页。 需要确保术语准确,比如“单页”对应的order=0,以及伙伴系统的作用。可能还要比较alloc_pages_node与其他分配函数,比如alloc_pages的区别,是否涉及节点选择。 最后,总结使用场景,帮助用户理解何时选择哪个函数,以及参数设置的注意事项。检查是否有常见的错误用法或需要注意的地方,比如节点选择错误导致性能问题。</think>在Linux内核内存管理中,`alloc_page``alloc_pages_node`是用于物理页分配的核心函数。以下从原理实现角度逐步分析它们在`order=0`(即分配单个物理页)时的行为: --- ### 1. **`alloc_page`函数** - **定义**: ```c #define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0) ``` 本质是`alloc_pages`的宏封装,固定`order=0`,表示分配$$2^0=1$$个连续物理页(通常4KB)。 - **参数**: - `gfp_mask`:内存分配标志(如`GFP_KERNEL`、`GFP_ATOMIC`),控制分配行为(是否可休眠、内存域等)。 - **返回值**: 成功返回`struct page*`指针,失败返回`NULL`。 - **典型场景**: 需要单个页时使用,如小对象的Slab分配、页缓存等。 --- ### 2. **`alloc_pages_node`函数** - **定义**: ```c struct page *alloc_pages_node(int nid, gfp_t gfp_mask, unsigned int order); ``` 更底层的接口,允许显式指定NUMA节点。 - **参数解析**: - `nid`:NUMA节点ID。若`nid < 0`(如`NUMA_NO_NODE`),由内核自动选择节点。 - `gfp_mask`:同`alloc_page`,但可能包含`__GFP_THISNODE`强制本地分配。 - `order`:分配阶数。`order=0`时行为与`alloc_page`等价。 - **实现逻辑**: - 检查`nid`有效性,若无效则回退到当前节点。 - 调用`__alloc_pages_node(nid, gfp_mask, order)`进入伙伴系统分配流程。 - 若指定节点内存不足,可能触发回收或跨节点分配(取决于`gfp_mask`)。 - **典型场景**: NUMA优化场景,需显式控制页面的物理位置(如减少远程内存访问)。 --- ### 3. **关键差异对比** | 特性 | `alloc_page` | `alloc_pages_node` (order=0) | |---------------------|----------------------------------|---------------------------------------| | **节点选择** | 自动选择 | 可显式指定NUMA节点 | | **封装层级** | 高层封装(基于`alloc_pages`) | 更底层,直接对接伙伴系统 | | **灵活性** | 简单但限制多 | 更精细控制(节点、内存策略) | --- ### 4. **底层机制** - **伙伴系统(Buddy System)**: 通过维护不同`order`的空闲页块链表,实现快速拆分与合并。`order=0`时直接返回最小单位。 - **NUMA优化**: `alloc_pages_node`通过`nid`参数确保内存本地性,减少跨节点访问延迟。 - **GFP标志影响**: - `GFP_KERNEL`:允许休眠页面回收,适用于进程上下文。 - `GFP_ATOMIC`:禁止休眠,用于原子上下文(如中断处理)。 --- ### 5. **典型代码路径** ```c // alloc_page调用链 alloc_page(gfp_mask) → alloc_pages(gfp_mask, 0) → alloc_pages_node(numa_node_id(), gfp_mask, 0) // alloc_pages_node核心逻辑 alloc_pages_node(nid, gfp_mask, order) → if (nid < 0) nid = numa_mem_id(); → __alloc_pages_node(nid, gfp_mask, order) → __alloc_pages(gfp_mask, order, nid, NULL) ``` --- ### 6. **使用建议** - **优先选择**: - 无NUMA需求 → `alloc_page` - 需控制节点 → `alloc_pages_node` - **错误处理**: 必须检查返回值,内核无法容忍未处理的分配失败。 --- **总结**:两者在`order=0`时功能等价,但`alloc_pages_node`提供了NUMA节点的显式控制能力,适合对内存位置敏感的场景。选择时需权衡封装简洁性与控制粒度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值