调度器30—调度相关结构体—p->flags

标志定义:

//kernel-6.1/include/linux/sched.h

/* Per process flags */
define PF_VCPU                0x00000001    /* I'm a virtual CPU */
#define PF_IDLE                0x00000002    /* I am an IDLE thread */
#define PF_EXITING            0x00000004    /* Getting shut down */
#define PF_POSTCOREDUMP        0x00000008    /* Coredumps should ignore this task */
#define PF_IO_WORKER        0x00000010    /* Task is an IO worker */
#define PF_WQ_WORKER        0x00000020    /* I'm a workqueue worker */
#define PF_FORKNOEXEC        0x00000040    /* Forked but didn't exec */
#define PF_MCE_PROCESS        0x00000080    /* Process policy on mce errors */
#define PF_SUPERPRIV        0x00000100    /* Used super-user privileges */
#define PF_DUMPCORE            0x00000200    /* Dumped core */
#define PF_SIGNALED            0x00000400    /* Killed by a signal */
#define PF_MEMALLOC            0x00000800    /* Allocating memory */
#define PF_NPROC_EXCEEDED    0x00001000    /* set_user() noticed that RLIMIT_NPROC was exceeded */
#define PF_USED_MATH        0x00002000    /* If unset the fpu must be initialized before use */
#define PF__HOLE__00004000    0x00004000
#define PF_NOFREEZE            0x00008000    /* This thread should not be frozen */
#define PF__HOLE__00010000    0x00010000
#define PF_KSWAPD            0x00020000    /* I am kswapd */
#define PF_MEMALLOC_NOFS    0x00040000    /* All allocation requests will inherit GFP_NOFS */
#define PF_MEMALLOC_NOIO    0x00080000    /* All allocation requests will inherit GFP_NOIO */
#define PF_LOCAL_THROTTLE    0x00100000    /* Throttle writes only against the bdi I write to */
#define PF_KTHREAD            0x00200000    /* I am a kernel thread */
#define PF_RANDOMIZE        0x00400000    /* Randomize virtual address space */
#define PF__HOLE__00800000    0x00800000
#define PF__HOLE__01000000    0x01000000
#define PF__HOLE__02000000    0x02000000
#define PF_NO_SETAFFINITY    0x04000000    /* Userland is not allowed to meddle with cpus_mask */
#define PF_MCE_EARLY        0x08000000    /* Early kill for mce process policy */
#define PF_MEMALLOC_PIN        0x10000000    /* Allocation context constrained to zones which allow long term pinning. */
#define PF__HOLE__20000000    0x20000000
#define PF__HOLE__40000000    0x40000000
#define PF_SUSPEND_TASK        0x80000000    /* This thread called freeze_processes() and should not be frozen */

 

一、PF_EXITING

1. 赋值路径

各驱动和内核机制中直接调用
SYSCALL_DEFINE1(exit, int, error_code) //exit.c
    do_exit(code); //exit.c
        exit_signals(tsk); //signal.c
            tsk->flags |= PF_EXITING; //此标志唯一赋值位置

2. 作用

标记进程已在exit退出流程中了。

 

二、PF_MEMALLOC

基于 Linux-6.1.115

此标志表示当前线程正处于"为了释放内存而分配内存"的紧急上下文,允许它在分配路径上获得比普通线程更高的优先级和保留内存访问权。

1. 关键作用:

(1) 允许走紧急内存通道(越过常规 watermark)

在页分配器里,PF_MEMALLOC 会把分配标志提升为 ALLOC_NO_WATERMARKS(本质是可用紧急保留内存),对应逻辑在 __gfp_pfmemalloc_flags()。普通线程受 min/low/high 水位约束更严格,PF_MEMALLOC 线程可以在更"饥荒"的状态下拿到页。

(2) 避免回收递归/死锁

PF_MEMALLOC 线程在慢路径里会避免再次进入直接回收,防止"回收过程再触发回收"导致递归卡死。
a. lockdep reclaim 入口直接判断 PF_MEMALLOC 不再进入 reclaim 跟踪,见 __need_reclaim().
b. 内存分配 slowpath 里也有 Avoid recursion of direct reclaim,见 __alloc_pages_slowpath().

2. 典型案例

典型的就是 kswapd 全程带有 PF_MEMALLOC 标志,因为 kswapd 的工作是回收内存,它自己偶发的小分配不能被普通水位限制卡死,否则会出现"要回收先分配,但分配又被水位拒绝"的自锁。

3. memcg 处理

memcg 里对 PF_MEMALLOC 有明确"放行/必须成功"语义:
(1) over-limit 处理时,PF_MEMALLOC 可走 force 分支,见 try_charge_memcg().
(2) zswap 计费路径明确要求 PF_MEMALLOC,上注释写了 charging must succeed, 见 obj_cgroup_charge_zswap().
这体现了设计目标:回收关键路径优先活下去,再追求严格配额。

4. 网络栈中案例

网络接收 PF_MEMALLOC skb 时,会临时进入 memalloc_noreclaim_save/restore 上下文(本质设置/恢复 PF_MEMALLOC):
(1) 注释说明“用 PF_MEMALLOC 避免把上下文传播到所有分配点”, 见 __netif_receive_skb()

5. 相近标志区别

PF_MEMALLOC:线程级上下文标志,影响该线程后续分配行为,直到 restore。
__GFP_MEMALLOC:内存分配标志,影响单次分配请求,作用于这一次 alloc。
PF_MEMALLOC_NOFS/PF_MEMALLOC_NOIO:不是“越水位拿保留内存”,而是约束分配时是否允许 FS/IO 回收。

6. 使用注意

(1) 必须短作用域并严格成对 save/restore,否则会长期"吃保留内存",影响系统稳定。
(2) 不应在普通业务路径滥用,它是回收/生存通道,不是性能优化开关。
(3) 在告警可见性上,PF_MEMALLOC 场景会触发一些特例过滤(例如 show_mem 过滤条件),见 warn_alloc_show_mem()。

7. 赋值:

reclaim_clean_pages_from_list //mm/vmscan.c
__reclaim_pages //mm/vmscan.c
lru_gen_seq_write //mm/vmscan.c
try_to_free_mem_cgroup_pages //mm/vmscan.c
shrink_all_memory //mm/vmscan.c
__node_reclaim //mm/vmscan.c
__alloc_pages_direct_compact //mm/page_alloc.c
__perform_reclaim //mm/page_alloc.c
    memalloc_noreclaim_save(void) //sched/mm.h 
        current->flags |= PF_MEMALLOC;

    memalloc_noreclaim_restore(flags) //sched/mm.h 
        current->flags = (current->flags & ~PF_MEMALLOC) | flags;

    kswapd //mm/vmscan.c kswapd内核线程
        tsk->flags |= PF_MEMALLOC | PF_KSWAPD;
        ...
        tsk->flags &= ~(PF_MEMALLOC | PF_KSWAPD); //不会执行到,永远带这两个标志

    handle_softirqs(bool ksirqd) //kernel/softirq.c
        current->flags &= ~PF_MEMALLOC; //临时屏蔽,中断处理完后再恢复
        ...
        current_restore_flags(old_flags, PF_MEMALLOC);

这里只列出了部分赋值路径,网络代码中有很多直接使用 current->flags 或 PF_MEMALLOC 操作。


三、PF_MEMALLOC_NOIO

基于 Linux-6.1

内存分配路径中禁止触发 IO 路径(清掉 __GFP_IO),同时也禁止 FS 回收路径(清掉 __GFP_FS)。详见 current_gfp_context()。

1. 赋值路径

    blk_revalidate_disk_zones //block/blk-zoned.c
    sd_zbc_revalidate_zones //scsi/sd_zbc.c
    __zonefs_io_error //zonefs/super.c
    __vmalloc_area_node //mm/vmalloc.c
    device_del //drivers/base/core.c
    usb_reset_device //usb/hub.c
        memalloc_noio_save(void) //sched/mm.h
            current->flags |= PF_MEMALLOC_NOIO;
    
        memalloc_noio_restore(flags) //sched/mm.h 调用路径同上
            current->flags = (current->flags & ~PF_MEMALLOC_NOIO) | flags; //不担心改动到其它标志吗?

        loop_process_work //drivers/block/loop.c 
            current->flags |= PF_LOCAL_THROTTLE | PF_MEMALLOC_NOIO;

    SYSCALL_DEFINE5(prctl, option, arg2, arg3, arg4, arg5) //kernel/sys.c
        case PR_SET_IO_FLUSHER:
            if (arg2 == 1)
                current->flags |= (PF_MEMALLOC_NOIO | PF_LOCAL_THROTTLE);
            else if (!arg2)
                current->flags &= ~(PF_MEMALLOC_NOIO | PF_LOCAL_THROTTLE);
        case PR_GET_IO_FLUSHER:
            error = (current->flags & PR_IO_FLUSHER) == PR_IO_FLUSHER;


2. 生效路径

    __alloc_pages //page_alloc.c 分配物理页时的归一路径
        current_gfp_context(gfp_t flags) //sched/mm.h
            pflags = current->flags;
            if (pflags & PF_MEMALLOC_NOIO)
                flags &= ~(__GFP_IO | __GFP_FS);

prctl() 系统调用可设置,块设备驱动和外设驱动中都有使用。作用是分配物理页时将本任务中的分配掩码 (__GFP_IO | __GFP_FS) 都给清掉。

参考: /kernel-6.1/Documentation/core-api/gfp_mask-from-fs-io.rst


四、PF_MEMALLOC_NOFS

基于 Linux-6.1

内存分配路径中禁止进入文件系统回收路径(清掉 __GFP_FS)。详见 current_gfp_context()。

1. 赋值路径

    //fs/btrfs、fs/xfs、fs/cachefiles、fs/erofs 等文件系统路径下使用较多
    page_cache_ra_unbounded //mm/readahead.c
        memalloc_nofs_save(void) //sched/mm.h
            current->flags |= PF_MEMALLOC_NOFS;

        memalloc_nofs_restore(flags) //sched/mm.h 调用路径同上
            current->flags = (current->flags & ~PF_MEMALLOC_NOFS) | flags;


2. 生效路径

    __alloc_pages //page_alloc.c 分配物理页时的归一路径
        current_gfp_context(gfp_t flags) //sched/mm.h
            pflags = current->flags;
            if (pflags & PF_MEMALLOC_NOIO)
                flags &= ~__GFP_FS;

    fsnotify_group_assert_locked //linux/fsnotify_backend.h
        if (group->flags & FSNOTIFY_GROUP_NOFS)
            WARN_ON_ONCE(!(current->flags & PF_MEMALLOC_NOFS));

    ext4_xattr_inode_lookup_create //ext4/xattr.c
        ext4_xattr_inode_cache_find //ext4/xattr.c
            WARN_ON_ONCE(ext4_handle_valid(journal_current_handle()) && !(current->flags & PF_MEMALLOC_NOFS));

可以看到主要是在文件系统路径下使用,作用是分配内存时不允许IO.

参考: /kernel-6.1/Documentation/core-api/gfp_mask-from-fs-io.rst


五、PF_MEMALLOC_PIN

基于 Linux-6.1

禁止返回可迁移页(清掉 __GFP_MOVABLE)####, 详见 current_gfp_context()。用来告诉页分配器"这次分配出的页很可能会被长期 pin 住",因此要避免分配可迁移页。

禁止返回可迁移页的原因:
(1) 被长期 pin 的页几乎不能迁移,长期 pin(典型是 FOLL_LONGTERM)会把页长期钉住,迁移/压缩/回收很难对其生效。
(2) 可迁移页的设计目标会被破坏。 __GFP_MOVABLE 本来是给"可搬迁"的页,用于内存压缩、CMA 连续内存、内存热插拔隔离等, 如果长期 pin 落在这类页上,会造成碎片和失败重试。
(3) 系统代价会被放大。内核会反复尝试迁移/回收却发现页被 pin,导致 compaction 效率下降、CMA 成功率下降、延迟抖动增大。

什么时候设置:
(1) 在执行"长期 pin 用户页"相关路径时。最典型就是 pin_user_pages 系列触发的 FOLL_LONGTERM 语义,对应 gup 代码已内置处理,见 __gup_longterm_locked().
(2) 预期分配出的页会被设备或子系统长期持有且不可迁移时。例如部分 RDMA/VFIO/长期零拷贝映射场景的底层实现路径(具体是否由你手动置位取决于调用链,很多主线路径已在 MM 内部处理)。

1. 赋值路径

    __gup_longterm_locked //gup.c
        memalloc_pin_save(void) //sched/mm.h
            current->flags |= PF_MEMALLOC_PIN;

    __gup_longterm_locked //gup.c
        memalloc_pin_restore(flags) //sched/mm.h
            current->flags = (current->flags & ~PF_MEMALLOC_PIN) | flags;


2. 生效路径

    gfp_t current_gfp_context(flags)
        pflags = READ_ONCE(current->flags
        if (pflags & PF_MEMALLOC_PIN)
            flags &= ~__GFP_MOVABLE;

    dequeue_huge_page_node_exact //mm/hugetlb.c 默认不使能 CONFIG_HUGETLBFS, 可忽略
        bool pin = !!(current->flags & PF_MEMALLOC_PIN);

使用场景比较单一,只有 gpu.c 中有使用。

 

posted on 2023-02-16 22:45  Hello-World3  阅读(115)  评论(0)    收藏  举报

导航