baddy:核心函数入口

上一篇<<baddy:初始化内存域>>分析了内存域的初始化过程,及扩展内容cpu热插拔函数的注册及热插拔线程的作用等等。UMA下只有一个pglist_data对象也就是对应一个内存域,而NUMA下最多拥有5个内存域(zone), ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_HIGHMEM、ZONE_MOVABLE等等,内存域类型根据具体硬件来决定。

baddy系统的入口(核心)函数为__alloc_pages,经典调用可以追溯到slub分配器的kmalloc函数,参考<<slub构建过程>>


__alloc_pages

  __alloc_pages函数实现在mm/page_alloc.c文件:

struct page *__alloc_pages(gfp_t gfp, unsigned int order, int preferred_nid,
                                                        nodemask_t *nodemask)
{
        struct page *page;
        unsigned int alloc_flags = ALLOC_WMARK_LOW; // 默认低水位线

		// #define ALLOC_WMARK_MIN         WMARK_MIN		// 最低水位线
		// #define ALLOC_WMARK_LOW         WMARK_LOW		// 低水位线
		// #define ALLOC_WMARK_HIGH        WMARK_HIGH		// 高水位线
		// #define ALLOC_NO_WATERMARKS     0x04  			// 不检查水位线

		// enum zone_watermarks {
		//        WMARK_MIN,				// 内存区域的空闲页面最低水位线,说明本区域的内存严重不足
		//        WMARK_LOW,				// 内存区域的空闲页面低水位线,说明本区域的内存轻微不足,默认值为WMARK_MIN的125%
		//        WMARK_HIGH,				// 内存区域的空闲页数高水位线,说明本区域内存充足,默认值为WMARK_MAX的150%
		//        WMARK_PROMO,			// 内存区域的空闲页面大于高水位线;当内存需要回收时,对快速内存节点通过kswapd以回收页面,至空闲页面略高于WMARK_PROMO水位线
		//        NR_WMARK					// unsigned long _watermark[NR_WMARK]; 表示区域(zone)水平线,由WMARK_MIN... WMARK_PROMO索引
		// };


        gfp_t alloc_gfp; /* The gfp_t that was actually used for allocation */
        struct alloc_context ac = { };

        /*
         * There are several places where we assume that the order value is sane
         * so bail out early if the request is out of bound.
         */
        if (WARN_ON_ONCE_GFP(order >= MAX_ORDER, gfp)) // 调用dump_stack,打印堆栈信息,指定__GFP_NOWARN标志时,不输出警告信息
                return NULL;

        gfp &= gfp_allowed_mask;
        // 在早期引导期间,gfp_allowed_mask被设置为GFP_BOOT_MASK,
        // 以限制在启用中断之前使用的GFP标志
        // 一旦启用了中断,在系统运行时将其设置为__GFP_BITS_MASK
        // 在休眠期间,PM使用它来避免设备挂起时内存分配期间的I/O


		/* 应用作用域分配约束。这主要是关于GFP_NOFS的。
		GFP_NOIO必须从一个特定的上下文继承所有的分配请求,
		该上下文被标记为memalloc_no{fs,io}_{save,restore}。
		PF_MEMALLOC_PIN确保在分配时不使用可移动区域 */
		gfp = current_gfp_context(gfp);
        alloc_gfp = gfp;
		if (!prepare_alloc_pages(gfp, order, preferred_nid, nodemask, &ac,
                        &alloc_gfp, &alloc_flags))
                return NULL;

  进入prepare_alloc_pages函数:

static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,
                int preferred_nid, nodemask_t *nodemask,
                struct alloc_context *ac, gfp_t *alloc_gfp,
                unsigned int *alloc_flags)
{
        ac->highest_zoneidx = gfp_zone(gfp_mask);  
        // highest_zoneidx表示分配请求的最高可用区域索引
        // 由于分区的性质,低于highest_zoneidx的分区上的内存将受到lowmem_reserve[highest_zoneidx]的保护
        // 回收/压缩也使用最高的zoneidx来限制目标区域
        // 因为高于此索引的区域不能用于此分配请求
        
        ac->zonelist = node_zonelist(preferred_nid, gfp_mask); // 获取区域列表
        ac->nodemask = nodemask;
        ac->migratetype = gfp_migratetype(gfp_mask); // 将GFP标志转换为其相应的迁移类型

		if (cpusets_enabled()) {  // cpusets有效
                *alloc_gfp |= __GFP_HARDWALL; // __GFP_HARDWALL 强制执行cpuset内存分配策略
                /*
                 * When we are in the interrupt context, it is irrelevant
                 * to the current task context. It means that any node ok.
                 */
                if (in_task() && !ac->nodemask) // 在任务上下文中并且nodemask为NULL
                        ac->nodemask = &cpuset_current_mems_allowed;
                        // #define cpuset_current_mems_allowed (current->mems_allowed)

                else
                        *alloc_flags |= ALLOC_CPUSET;  // 检查cpuset是否正确
        }

		fs_reclaim_acquire(gfp_mask); // 检查(符合)/增加页面回收独占锁
        fs_reclaim_release(gfp_mask); // 释放锁

		might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM); // 可以回收的情况下,主动触发一次调度

        if (should_fail_alloc_page(gfp_mask, order)) //  should_fail_alloc_page做一些预检查, 一些无法分配的条件会直接报错
          
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坤昱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值