linux内核那些事之mempolicy(2)
2022/2/26 7:27:29
本文主要是介绍linux内核那些事之mempolicy(2),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
mempolicy相关系统调用主要有set_mempolicy/get_mempolicy 、mbind主要配置task/process policy和vma policy,如下图:
set_mempolicy
set_mempolicy()系统调用主要作用用于修改当前进行NUMA mem policy策略以及以后该进程创建的子进程也会继承该特性,API为:
#include <numaif.h> long set_mempolicy(int mode, const unsigned long * nodemask,unsigned long maxnode);
使用该系统调用要链接numa库:-Lnuma.
参数:
- int mode: mem policy配置策略模式,该字段包含两个部分一个是用户设置memplicy mode,另外一个部分为mem policy外部flag部分见,如下图:
- mode 部分主要包括MPOL_DEFAULT、MPOL_PREFERRED、MPOL_BIND、MPOL_INTERLEAVE、MPOL_LOCAL等字段,
- extern flag主要包含MPOL_F_STATIC_NODES和MPOL_F_RELATIVE_NODES字段,并在5.12版本加入MPOL_F_NUMA_BALANCING字段。
- 上述字段具体描述《linux内核那些事之mempolicy(1)》
- const unsigned long * nodemask: 该进程NUMA mem policy所要支持的numa节点 以bit位。
- unsigned long maxnode:该进程支持的最大节点。
kernel_set_mempolicy
kernel_set_mempolicy为set_mempolicy系统调用 kernel层处理函数:
/* Set the process memory policy */ static long kernel_set_mempolicy(int mode, const unsigned long __user *nmask, unsigned long maxnode) { int err; nodemask_t nodes; unsigned short flags; flags = mode & MPOL_MODE_FLAGS; mode &= ~MPOL_MODE_FLAGS; if ((unsigned int)mode >= MPOL_MAX) return -EINVAL; if ((flags & MPOL_F_STATIC_NODES) && (flags & MPOL_F_RELATIVE_NODES)) return -EINVAL; err = get_nodes(&nodes, nmask, maxnode); if (err) return err; return do_set_mempolicy(mode, flags, &nodes); }
- flags = mode & MPOL_MODE_FLAGS: 从mode中取出 extern flags状态标志位。
- mode &= ~MPOL_MODE_FLAGS:从mode参数中取出mempolicy mode。
- 对mode 以及flags进行参数检查。
- get_nodes:将nmask node节点转换成nodemask_t 结构。
- 调用do_set_mempolicy 修改当前进程current->mempolicy mempolicy。
get_mempolicy
get_mempolicy 用于获取当前进程或者具体内存地址mempolicy策略,API为:
#include <numaif.h> long get_mempolicy(int mode, unsigned long * nodemask,unsigned long maxnode,void *addr, unsigned long flags);
参数:
- unsigned long flag: 如果flag为0,表明获取的是当前线程的mempolicy,此时addr参数为NULL.。如果为flags 为MPOL_F_MEMS_ALLOWD,将会忽略mode参数 ,nodemask将会返回配置支持的numa节点。如果flag参数为MPOL_F_ADDR,结果将会返回指定的addr地址对应的mempolicy numa节点。
- int mode:如果mode参数不为0,则结果返回指定mode的 mempolicy 节点。
- unsigned long *nodemask:为unsigned long类似数组,大小为maxnode/sizeof(unsigned long) 。
- unsigned long maxnode:支持的最大nume 节点。
kernel_get_mempolicy
kernel_get_mempolicy为对应get_mempolicy kernel处理函数:
/* Retrieve NUMA policy */ static int kernel_get_mempolicy(int __user *policy, unsigned long __user *nmask, unsigned long maxnode, unsigned long addr, unsigned long flags) { int err; int uninitialized_var(pval); nodemask_t nodes; addr = untagged_addr(addr); if (nmask != NULL && maxnode < nr_node_ids) return -EINVAL; err = do_get_mempolicy(&pval, &nodes, addr, flags); if (err) return err; if (policy && put_user(pval, policy)) return -EFAULT; if (nmask) err = copy_nodes_to_user(nmask, maxnode, &nodes); return err; }
调用do_get_mempolicy 获取memplicy结果。
do_get_mempolicy
do_get_mempolicy为get_mempolicy核心处理函数:
/* Retrieve NUMA policy */ static long do_get_mempolicy(int *policy, nodemask_t *nmask, unsigned long addr, unsigned long flags) { int err; struct mm_struct *mm = current->mm; struct vm_area_struct *vma = NULL; struct mempolicy *pol = current->mempolicy, *pol_refcount = NULL; if (flags & ~(unsigned long)(MPOL_F_NODE|MPOL_F_ADDR|MPOL_F_MEMS_ALLOWED)) return -EINVAL; if (flags & MPOL_F_MEMS_ALLOWED) { if (flags & (MPOL_F_NODE|MPOL_F_ADDR)) return -EINVAL; *policy = 0; /* just so it's initialized */ task_lock(current); *nmask = cpuset_current_mems_allowed; task_unlock(current); return 0; } if (flags & MPOL_F_ADDR) { /* * Do NOT fall back to task policy if the * vma/shared policy at addr is NULL. We * want to return MPOL_DEFAULT in this case. */ mmap_read_lock(mm); vma = find_vma_intersection(mm, addr, addr+1); if (!vma) { mmap_read_unlock(mm); return -EFAULT; } if (vma->vm_ops && vma->vm_ops->get_policy) pol = vma->vm_ops->get_policy(vma, addr); else pol = vma->vm_policy; } else if (addr) return -EINVAL; if (!pol) pol = &default_policy; /* indicates default behavior */ if (flags & MPOL_F_NODE) { if (flags & MPOL_F_ADDR) { /* * Take a refcount on the mpol, lookup_node() * wil drop the mmap_lock, so after calling * lookup_node() only "pol" remains valid, "vma" * is stale. */ pol_refcount = pol; vma = NULL; mpol_get(pol); err = lookup_node(mm, addr); if (err < 0) goto out; *policy = err; } else if (pol == current->mempolicy && pol->mode == MPOL_INTERLEAVE) { *policy = next_node_in(current->il_prev, pol->v.nodes); } else { err = -EINVAL; goto out; } } else { *policy = pol == &default_policy ? MPOL_DEFAULT : pol->mode; /* * Internal mempolicy flags must be masked off before exposing * the policy to userspace. */ *policy |= (pol->flags & MPOL_MODE_FLAGS); } err = 0; if (nmask) { if (mpol_store_user_nodemask(pol)) { *nmask = pol->w.user_nodemask; } else { task_lock(current); get_policy_nodemask(pol, nmask); task_unlock(current); } } out: mpol_cond_put(pol); if (vma) mmap_read_unlock(mm); if (pol_refcount) mpol_put(pol_refcount); return err; }
根据传入的参数不同场景获取numa 节点,不再详细分析。
mbind
mbind系统调用为设置vma mempolicy级别,可以只设置某一个内存区域NUMA节点策略:
#include <numaif.h> long mbind(void *addr, unsigned long len, int mode,const unsigned long *nodemask,unsigned long maxnode,unsignd int flags)
参数
- unsigned int flags:flag支持MPOL_MF_STRICT、MPOL_MF_MOVE、MPOL_MF_MOVE_ALL.
当flags为MPOL_MF_STRICT且mode 不是MPOL_DEFAULT时,意思时要严格遵循设置numa设置,如果已经申请的物理页和 numa 配置节点策略不一致直接返回EIO。
flags为MPOL_MF_MOVE:内核会尝试将已经分配的内存 不符合设置numa节点要求给进行迁移,迁移到符合配置numa节点上去。
flags为MPOL_MF_MOVE_ALL:内核会尝试将已经分配的内存 不符合设置numa节点要求给进行迁移,迁移该物理页时不管其他进程是否也再使用都进行迁移。
- 其他参数与上面API意思相同。
do_mbind
do_mbind函数为mbind系统调用核心处理函数:
static long do_mbind(unsigned long start, unsigned long len, unsigned short mode, unsigned short mode_flags, nodemask_t *nmask, unsigned long flags) { struct mm_struct *mm = current->mm; struct mempolicy *new; unsigned long end; int err; int ret; LIST_HEAD(pagelist); if (flags & ~(unsigned long)MPOL_MF_VALID) return -EINVAL; if ((flags & MPOL_MF_MOVE_ALL) && !capable(CAP_SYS_NICE)) return -EPERM; if (start & ~PAGE_MASK) return -EINVAL; if (mode == MPOL_DEFAULT) flags &= ~MPOL_MF_STRICT; len = (len + PAGE_SIZE - 1) & PAGE_MASK; end = start + len; if (end < start) return -EINVAL; if (end == start) return 0; new = mpol_new(mode, mode_flags, nmask); if (IS_ERR(new)) return PTR_ERR(new); if (flags & MPOL_MF_LAZY) new->flags |= MPOL_F_MOF; /* * If we are using the default policy then operation * on discontinuous address spaces is okay after all */ if (!new) flags |= MPOL_MF_DISCONTIG_OK; pr_debug("mbind %lx-%lx mode:%d flags:%d nodes:%lx\n", start, start + len, mode, mode_flags, nmask ? nodes_addr(*nmask)[0] : NUMA_NO_NODE); if (flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) { err = migrate_prep(); if (err) goto mpol_out; } { NODEMASK_SCRATCH(scratch); if (scratch) { mmap_write_lock(mm); task_lock(current); err = mpol_set_nodemask(new, nmask, scratch); task_unlock(current); if (err) mmap_write_unlock(mm); } else err = -ENOMEM; NODEMASK_SCRATCH_FREE(scratch); } if (err) goto mpol_out; ret = queue_pages_range(mm, start, end, nmask, flags | MPOL_MF_INVERT, &pagelist); if (ret < 0) { err = ret; goto up_out; } err = mbind_range(mm, start, end, new); if (!err) { int nr_failed = 0; if (!list_empty(&pagelist)) { WARN_ON_ONCE(flags & MPOL_MF_LAZY); nr_failed = migrate_pages(&pagelist, new_page, NULL, start, MIGRATE_SYNC, MR_MEMPOLICY_MBIND); if (nr_failed) putback_movable_pages(&pagelist); } if ((ret > 0) || (nr_failed && (flags & MPOL_MF_STRICT))) err = -EIO; } else { up_out: if (!list_empty(&pagelist)) putback_movable_pages(&pagelist); } mmap_write_unlock(mm); mpol_out: mpol_put(new); return err; }
- mbind_range:将指定的内内存区域进行内存策略绑定,如果绑定成功则需要判断是否已经分配物理页。
- 如果已经分配物理内存,则会根据设置的flags要求,是否做出页页迁移动作,如果需要进行页迁移则调用migrate_pages进行页迁移。
这篇关于linux内核那些事之mempolicy(2)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法