linux内存管理--实际分配函数 buffered_rmqueue

Linux内存管理的关键在于buffered_rmqueue()函数,它负责实际的内存分配工作,无论快速还是慢速分配。该函数考虑了如zone、order、gfp_flags等参数,与per_cpu_pageset配合处理冷热分配。当高阶分配得到的内存块不满足需求时,会通过调整挂载到低阶,并进行裁剪,确保满足指定阶数的内存分配。若常规分配失败,则会使用备用迁移列表。

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



不管是快速分配还是慢速分配,实际分配内存的都是 buffered_rmqueue()函数,其他的都是在选择从哪个地方来分配比较合适;

还是先来说说各个参数:

struct zone *preferred_zone 表示分配所能接受的最大zone类型

struct zone *zone  表示就在该zone上分配内存;

int  order  表示分配页的阶数

gfp_t gfp_flags  分配的标识


page = buffered_rmqueue(preferred_zone, zone, order,
                        gfp_mask, migratetype);



/*
 * Really, prep_compound_page() should be called from __rmqueue_bulk().  But
 * we cheat by calling it from here, in the order > 0 path.  Saves a branch
 * or two.
 */
static inline
struct page *buffered_rmqueue(struct zone *preferred_zone,
            struct zone *zone, int order, gfp_t gfp_flags,
            int migratetype)
{
    unsigned long flags;
    struct page *page;
    int cold = !!(gfp_flags & __GFP_COLD);//是否指定冷热页

again:
    if (likely(order == 0)) {//分配单页
        struct per_cpu_pages *pcp;
        struct list_head *list;

        local_irq_save(flags);//禁止本地CPU中断,禁止前先保存中断状态
        pcp = &this_cpu_ptr(zone->pageset)->pcp;//获取到cpu高速缓存页
        list = &pcp->lists[migratetype];//根据迁移类型,得到高速缓存区的freelist
        if (list_empty(list)) {//空的,高速缓存没有数据;这可能是上次获取的cpu高速缓存迁移类型和这次不一样
            pcp->count += rmqueue_bulk(zone, 0,
                    pcp->batch, list,
                    migratetype, cold);//该函数向高速缓存中添加内存页,具体分析见文章后面
            if (unlikely(list_empty(list)))
                goto failed;
        }

        if (cold)
            page = list_entry(list->prev, struct page, lru);
        else
            page = list_entry(list->next, struct page, lru);

        list_del(&page->lru);
        pcp->count--;
    } else {
        if (unlikely(gfp_flags & __GFP_NOFAIL)) {
            /*
             * __GFP_NOFAIL is not to be used in new code.
             *
             * All __GFP_NOFAIL callers should be fixed so that they
             * properly detect and handle allocation failures.
             *
             * We most definitely don't want callers attempting to
             * allocate greater than order-1 page units with
             * __GFP_NOFAIL.
             */
            WARN_ON_ONCE(order > 1);
        }
        spin_lock_irqsave(&zone->lock, flags);
        page = __rmqueue(zone, order, migratetype);
        spin_unlock(&zone->lock);
        if (!page)
            goto failed;
        __mod_zone_freepage_state(zone, -(1 << order),
                      get_pageblock_migratetype(page));
    }

    __count_zone_vm_events(PGALLOC, zone, 1 << order);
    zone_statistics(preferred_zone, zone, gfp_flags);
    local_irq_restore(flags);

    VM_BUG_ON(bad_range(zone, page));
    if (prep_new_page(page, order, gfp_flags))
        goto again;
    return page;

failed:
    local_irq_restore(flags);
    return NULL;
}


struct zone结构体中有个  struct per_cpu_pageset __percpu *pageset; 成员,该成员用于冷热分配器,热页表示已经在cpu的高速缓存中了;

struct per_cpu_pageset {
    struct per_cpu_pages pcp;
#ifdef CONFIG_NUMA
    s8 expire;
#endif
#ifdef CONFIG_SMP
    s8 stat_threshold;
    s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS];
#endif
};

cpu缓存页数组

struct per_cpu_pages {
    int count;      /* number of pages in the list */列表中页数
    int high;       /* high watermark, emptying needed */列表页数的上限
    int batch;      /* chunk size for buddy add/remove */添加和删除页时,一次操作多少页。不是单页删除和填充的,而是以该单位页来操作的

    /* Lists of pages, one per migrate type stored on the pcp-lists */
    struct list_head lists[MIGRATE_PCPTYPES];//迁移类型的链表
};

<think>我们正在处理一个关于Android logcat过滤特定事件(am_proc_start)的问题。根据用户需求,我们需要从logcat中过滤出与进程启动相关的事件,即am_proc_start事件。 根据引用[2]中提供的信息,我们知道logcat有不同的缓冲区,其中事件缓冲区(events)记录了系统事件,而am_proc_start事件通常会被记录在事件缓冲区中。因此,我们需要使用`adb logcat -b events`来指定事件缓冲区。 另外,根据引用[3],我们知道logcat输出的格式通常包含优先级、标签和消息。但是,对于事件缓冲区,消息的格式是特定的。am_proc_start事件在事件缓冲区中的标签为"am_proc_start",因此我们可以通过grep来过滤这个标签。 但是,需要注意的是,在事件缓冲区中,事件是以特定格式的字符串记录的,我们也可以直接通过事件标签来过滤。所以,我们可以使用以下命令: ``` adb logcat -b events | grep "am_proc_start" ``` 然而,引用[1]中提供了使用grep和find的例子,但注意在Windows环境下,find是Windows命令,而grep是Linux命令。由于用户可能在不同的操作系统上使用adb,我们需要给出通用的方法。通常,在Windows命令提示符下,我们可以使用findstr,而在Linux或Mac终端上使用grep。因此,我们可以根据用户的环境给出不同的建议,但考虑到用户的问题中没有指定操作系统,我们可以先给出使用grep的通用方法(因为很多开发者使用Mac或Linux,或者在Windows上安装了grep工具),同时给出Windows下的替代方案。 另外,根据引用[2]中的其他命令,我们还可以使用`-v`参数来指定输出格式,比如`threadtime`格式,这样我们可以得到更详细的时间信息。因此,我们可以建议用户使用: ``` adb logcat -b events -v threadtime | grep "am_proc_start" ``` 这样输出的日志会包含时间戳、进程ID、线程ID等信息,便于分析。 如果用户使用的是Windows且没有grep,可以使用findstr: ``` adb logcat -b events -v threadtime | findstr "am_proc_start" ``` 此外,我们还可以通过标签(tag)来过滤,但是事件缓冲区中的事件通常以事件名作为标签,所以直接grep事件名即可。 总结命令如下: 1. 对于Linux/Mac或已安装grep的Windows环境: ``` adb logcat -b events -v threadtime | grep "am_proc_start" ``` 2. 对于Windows命令提示符: ``` adb logcat -b events -v threadtime | findstr "am_proc_start" ``` 注意:如果只想查看当前开始的进程,可以实时查看日志。如果想查看已经发生的日志,可以使用`-d`参数(dump)然后退出,但这样会输出整个缓冲区然后退出。例如: ``` adb logcat -b events -d -v threadtime | grep "am_proc_start" ``` 但是,通常我们使用管道过滤时,不加`-d`参数会持续输出,直到用户按Ctrl+C停止。 另外,根据引用[4],我们还可以使用广播命令,但这里不相关。 因此,针对用户的问题,我们给出以上解决方案。 §§相关问题§§ 1. 如何过滤logcat中其他类型的事件(例如Activity启动事件)? 2. 如何将过滤后的日志保存到文件中? 3. 除了事件缓冲区,logcat还有哪些缓冲区?它们分别记录什么信息?</think>### 过滤 Android logcat 中的 am_proc_start 事件 要过滤 Android 系统日志中与进程启动相关的事件(`am_proc_start`),请使用以下命令组合: #### 基础过滤命令 ```bash adb logcat -b events | grep "am_proc_start" ``` - `-b events`:指定读取事件缓冲区(`am_proc_start` 事件记录在此缓冲区)[^2] - `grep "am_proc_start"`:筛选包含进程启动事件的日志行 #### 增强版命令(推荐) ```bash adb logcat -b events -v threadtime | grep -E "am_proc_start|I/ActivityManager" ``` - `-v threadtime`:显示详细时间戳和线程信息[^2] - `grep -E`:同时匹配进程启动事件和关联的 ActivityManager 日志 - 输出示例: ``` 07-20 10:30:15.123 1000 1015 I am_proc_start: [0,1234,com.example.app, ...] ``` #### 其他实用技巧 1. **保存到文件**: ```bash adb logcat -b events | grep "am_proc_start" > proc_starts.log ``` 2. **过滤特定包名**: ```bash adb logcat -b events | grep "am_proc_start.*com.your.app" ``` 3. **实时监控新进程**: ```bash adb logcat -b events -v raw | grep --line-buffered "am_proc_start" ``` > **关键说明**: > `am_proc_start` 是 Android ActivityManager 的核心事件,记录进程启动时的 PID、UID、包名等信息[^3]。该事件仅存在于 `events` 缓冲区,因此必须使用 `-b events` 参数才能捕获[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值