Linux内存管理(七十二):Linux PSI 原理更新(v5.15)

源码基于:Linux 5.15

约定:

  • 芯片架构:ARM64
  • 内存架构:UMA
  • CONFIG_ARM64_VA_BITS:39
  • CONFIG_ARM64_PAGE_SHIFT:12
  • CONFIG_PGTABLE_LEVELS :3

0. 前言

本文是在之前《PSI 详解 v5.4》一文基础上,整理一下PSI 原理中的细节,包含 cgroup v2 关于PSI 的原理和使用。

参考:

PSI 详解 v5.4

PSI 指标

PSI 功能依赖 CONFIG_PSI,当该 config 没有使能,psi.c 是不会被编译到 image的。

1. PSI 中涉及的重要属性

本节主要梳理 PSI 中一些重要的变量、属性值,笔者习惯把重要的属性总结到最前文,阅读PSI 原理可以暂跳过此节。

1.1 psi_enable

kernel/sched/psi.c#ifdef CONFIG_PSI_DEFAULT_DISABLED
static bool psi_enable;
#else
static bool psi_enable = true;
#endif

静态全局变量 psi_enable 受限于 CONFIG_PSI_DEFAULT_DISABLED,默认该 config 不会使能,即 psi_enable 为 true。

另外,可以通过 cmdline 中属性 "psi=" 修改该值:

kernel/sched/psi.cstatic int __init setup_psi(char *str)
{return kstrtobool(str, &psi_enable) == 0;
}
__setup("psi=", setup_psi);

当 psi_enable 为false 时,PSI 全局控制变量 psi_disabled (static key) 会被置成 true:

kernel/sched/psi.cvoid __init psi_init(void)
{if (!psi_enable) {static_branch_enable(&psi_disabled);return;}...
}

1.2 psi_period

kernel/sched/psi.c/* Sampling frequency in nanoseconds */
static u64 psi_period __read_mostly;

1.3 psi_cgroups_enabled

kernel/sched/psi.cDEFINE_STATIC_KEY_TRUE(psi_cgroups_enabled);

该变量主要确认 cgroup 中是否启动了 PSI 功能,默认值为 true。

cgroup 模块通过在 cmdline 中设定 "cgroup_disable=" 属性来禁用功能,例如:

  • 设定 "cgroup_disable=cpuset" 来禁用 cpuset;
  • 设定 "cgroup_disable=pressure" 来禁用 PSI;

在 PSI 初始化 psi_init() 函数调用时,会确定 cgroup 模块是否禁用了 pressure,如果 cgroup 中禁用了 PSI,则 psi_cgroups_enabled 的值会被置为 false。

另外,与 Linux6.6 不同之处是,该变量在 Linux6.6 中定义时加上了 static:

static DEFINE_STATIC_KEY_TRUE(psi_cgroups_enabled);

1.4 psi_system 

正常情况下,PSI 中通过 struct psi_group 结构体管理 PSI 中所有状态、数据、work 等信息。

psi_system 是 struct psi_group 结构体变量,用以管理系统 PSI 状态、数据、work 等信息。

kernel/sched/psi.cstatic DEFINE_PER_CPU(struct psi_group_cpu, system_group_pcpu);
struct psi_group psi_system = {.pcpu = &system_group_pcpu,
};

但,在 cgroup 中也可以实现PSI 局部管理,通过对 cpu.pressurememory.pressureio.pressure 等文件 write 操作注册 psi_trigger,实现对特定 cgroup 的资源瓶颈状态的监听和跟踪。

1.4 struct psi_group

include/linux/psi_types.hstruct psi_group {//avgs_work 数据同步锁struct mutex avgs_lock;/* Per-cpu task state & time tracking */struct psi_group_cpu __percpu *pcpu;/* Running pressure averages */u64 avg_total[NR_PSI_STATES - 1];//avgs_work的时间点u64 avg_last_update;u64 avg_next_update;//带有定时器的avgs_workstruct delayed_work avgs_work;/* Total stall times and sampled pressure averages */u64 total[NR_PSI_AGGREGATORS][NR_PSI_STATES - 1];unsigned long avg[NR_PSI_STATES - 1][3];//psimon 线程struct task_struct __rcu *poll_task;struct timer_list poll_timer;wait_queue_head_t poll_wait;atomic_t poll_wakeup;/* Protects data used by the monitor */struct mutex trigger_lock;//所有psi_trigger都存在链表中struct list_head triggers;//每个psi_states类型的psi_trigger 数量u32 nr_triggers[NR_PSI_STATES - 1];//统计poll_work 需要处理哪些psi_states类型的 triggeru32 poll_states;//记录poll_work 最小的周期u64 poll_min_period;/* Total stall times at the start of monitor activation */u64 polling_total[NR_PSI_STATES - 1];u64 polling_next_update;u64 polling_until;
};

2. PSI module 初始化

在module init 时调用 psi_proc_init() 进行 PSI 的初始化,创建了proc 虚拟文件目录 pressure,并在其下面创建了三个子文件:pressure/io、pressure/memory、pressure/cpu

与 Linux5.4 版本不同之处是,这些节点的创建是在  psi_enable 为true 条件下:

kernel/sched/psi.cstatic int __init psi_proc_init(void)
{if (psi_enable) {proc_mkdir("pressure", NULL);proc_create("pressure/io", 0, NULL, &psi_io_proc_ops);proc_create("pressure/memory", 0, NULL, &psi_memory_proc_ops);proc_create("pressure/cpu", 0, NULL, &psi_cpu_proc_ops);}return 0;
}
module_init(psi_proc_init);

更多关于 psi_enable  变量信息可以查看上文第 1.1 节。 

3. PSI 初始化 psi_init()

start_kernel()---->sched_init()---->psi_init()

与 Linux5.4 不同之处是,这里开始多了 cgroup 的控制,主要是通过控制变量 psi_cgroups_enabled (static key),默认为true。

void __init psi_init(void)
{if (!psi_enable) {static_branch_enable(&psi_disabled);return;}if (!cgroup_psi_enabled())static_branch_disable(&psi_cgroups_enabled);psi_period = jiffies_to_nsecs(PSI_FREQ);group_init(&psi_system);
}

与 Linux6.6 不同之处是,当 psi_enable 为false 时,也会将 psi_cgroups_enabled 置为 false。

更多关于 psi_cgroups_enabledpsi_periodpsi_system 可以查看上文第 1.2 节。

3.1 group_init()

kernel/sched/psi.cstatic void group_init(struct psi_group *group)
{int cpu;for_each_possible_cpu(cpu)seqcount_init(&per_cpu_ptr(group->pcpu, cpu)->seq);//初始化avgs_work 的两个时间点group->avg_last_update = sched_clock();group->avg_next_update = group->avg_last_update + psi_period;//初始化带定时器的work,并指定处理函数为psi_avgs_work()INIT_DELAYED_WORK(&group->avgs_work, psi_avgs_work);//初始化avgs_work 数据同步的 avgs_lockkmutex_init(&group->avgs_lock);/* Init trigger-related members */atomic_set(&group->poll_wakeup, 0);mutex_init(&group->trigger_lock);//初始化psi_trigger listINIT_LIST_HEAD(&group->triggers);//初始化每个psi_states类型的psi_trigger 数量memset(group->nr_triggers, 0, sizeof(group->nr_triggers));//初始化poll_work 需要处理哪些psi_states类型的 triggergroup->poll_states = 0;//初始化poll_work 的时间间隔,对于psi_system 这个group只记录最小值group->poll_min_period = U32_MAX;memset(group->polling_total, 0, sizeof(group->polling_total));//初始化下一次poll_work触发的时间group->polling_next_update = ULLONG_MAX;group->polling_until = 0;//初始化poll_work的等待队列init_waitqueue_head(&group->poll_wait);timer_setup(&group->poll_timer, poll_timer_fn, 0);rcu_assign_pointer(group->poll_task, NULL);
}

4. psi_trigger_create()

该函数用以创建 psi_trigger,系统中两个地方会触发:

  • 写 /proc/pressure/* 文件
  • 写 /sys/fs/cgroup/*.pressure 文件

创建好的 psi_trigger 都会被存在对应的 psi_group 中,或是系统的 psi_system,或是 cgroup 中的psi_group。

另外,在第一次创建 psi_trigger 的时候会创建 task "psimon"与 Linux5.4 不同之处是,这里用 kthread_create() 直接创建 task_struct,而不再是 kthread_worker。

kernel/sched/psi.cstruct psi_trigger *psi_trigger_create(struct psi_group *group,char *buf, size_t nbytes, enum psi_res res)
{struct psi_trigger *t;enum psi_states state;u32 threshold_us;u32 window_us;if (static_branch_likely(&psi_disabled))   //psi 是否enablereturn ERR_PTR(-EOPNOTSUPP);if (sscanf(buf, "some %u %u", &threshold_us, &window_us) == 2)  //解析是否为写 some 数据state = PSI_IO_SOME + res * 2;else if (sscanf(buf, "full %u %u", &threshold_us, &window_us) == 2) //解析是否为写 full 数据state = PSI_IO_FULL + res * 2;elsereturn ERR_PTR(-EINVAL);   //其他数据都认为无效if (state >= PSI_NONIDLE)   //pis_states 不能超过PSI_NONIDLEreturn ERR_PTR(-EINVAL);if (window_us < WINDOW_MIN_US ||  //窗口时长设置在500ms 至 10swindow_us > WINDOW_MAX_US)return ERR_PTR(-EINVAL);/* Check threshold */if (threshold_us == 0 || threshold_us > window_us)  //检测阈值不能为0,也不能大于窗口时长return ERR_PTR(-EINVAL);t = kmalloc(sizeof(*t), GFP_KERNEL);  //创建一个psi_triggerif (!t)return ERR_PTR(-ENOMEM);t->group = group;   //记录psi_trigger的 groupt->state = state;   //记录psi_trigger的 psi_statest->threshold = threshold_us * NSEC_PER_USEC;  //记录psi_trigger的检测时间,us 单位t->win.size = window_us * NSEC_PER_USEC;      //记录psi_trigger的窗口时长,us 单位window_reset(&t->win, 0, 0, 0);t->event = 0;t->last_event_time = 0;init_waitqueue_head(&t->event_wait);mutex_lock(&group->trigger_lock);if (!rcu_access_pointer(group->poll_kworker)) { //psi 系统相当关键的地方,创建psimon 工作线程struct task_struct *task;task = kthread_create(psi_poll_worker, group, "psimon");if (IS_ERR(task)) {kfree(t);mutex_unlock(&group->trigger_lock);return ERR_CAST(task);}atomic_clear_bit(POLL_WAKEUP, &group->poll_wakeup);wake_up_process(task);rcu_assign_pointer(group->poll_task, task); //存放task到poll_task中}list_add(&t->node, &group->triggers);  //将新建的psi_trigger 存入group 中group->poll_min_period = min(group->poll_min_period,div_u64(t->win.size, UPDATES_PER_WINDOW)); //确认最新的poll 周期group->nr_triggers[t->state]++;    //计数当前psi 资源psi_trigger数量group->poll_states |= (1 << t->state);  //更新group 中资源状态mutex_unlock(&group->trigger_lock);return t;
}

 

4.1 psi_poll_worker()

该函数是 psimon 线程的执行函数,在 group->poll_wakup 被置为 POLL_WAKUP 或 线程停止时被唤醒,如果是 POLL_WAKEUP 唤醒则调用核心处理函数 psi_poll_work()

kernel/sched/psi.cstatic int psi_poll_worker(void *data)
{struct psi_group *group = (struct psi_group *)data;//设置psimon 的优先级sched_set_fifo_low(current);//等待唤醒,并执行psi_poll_work()while (true) {wait_event_interruptible(group->poll_wait,atomic_fetch_and_clear_bit(POLL_WAKEUP, &group->poll_wakeup) ||kthread_should_stop());if (kthread_should_stop())break;//psi poll 的核心处理函数psi_poll_work(group);}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/18995.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何下载b站(哔哩哔哩bilibili)的学习视频教程

方法1&#xff1a; 打开粘贴视频链接下载即可哔哩哔哩(bilibili)视频解析下载 - 保存B站视频到手机、电脑哔哩哔哩高清视频解析下载工具是一个免费的B站视频在线解析提取工具,支持提取B站APP和bilibili网站上的任何视频,提取出来的视频无水印.我们可以借助此下载器方便地将视频…

最大公约数和最小公倍数(函数)(C语言)

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//声明函数&#xff1b; //最大公约数&#xff1b; int greatdivisor(int x, int y);//最小公倍数&#xff1b; int leastmultiple(int x, int y);int main() {/…

operator <=> (spaceship operator)

1. C20 前如何定义比较运算符 C20 之前&#xff0c;必须为一个类型定义六个操作符&#xff0c;以提供对象所有比较的支持。 例如&#xff0c;若要比较Value 类型的对象(具有整型ID)&#xff0c;则须实现以下操作: class Value { private:long id;...public:...// equality op…

618精选编程书单:学好代码是用好大模型的基础

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…

如何选择适合自己需求的云服务器

最近明月接了一个跨境电商的代维业务&#xff0c;发现他们的云服务器很有代表性&#xff0c;今天就以此为例给大家分享一下应该如何选择适合自己需求的云服务器。像明月这样专做代维业务的可以说什么云服务器都体验过了&#xff0c;也发现大家在选择自己的云服务器的时候有很大…

媒体查询检查设备特性的条件表达式的简单应用

一、media (max-width: 768px) 当屏幕宽度小于或等于768像素时&#xff0c;设置指定的CSS样式。 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-wi…

加密资产私钥安全完整手册(一) ,bitget钱包为例

比特币和以太坊等加密货币的兴起开创了数字金融的新时代&#xff0c;但也带来了独特的安全挑战。这些代表现实世界价值的数字资产已成为黑客和窃贼的主要目标。为了安全地应对这种情况&#xff0c;了解私钥的基本概念至关重要。 私钥是加密货币所有权和安全性的基石。它们相当于…

VSCode小技巧,忽略不想格式化的代码行

零&#xff0e;格式化工具文档 1 . Black Ignoring sections功能 2 . autopep8 disabling-line-by-line功能&#xff1b;&#xff1b;–line-range选项 3 . Prettier prettier-ignore功能(例&#xff1a;适用于JS的// prettier-ignore&#xff0c;适用于CSS的/* prettier-igno…

HTML新春烟花盛宴

目录 写在前面 烟花盛宴 完整代码 修改文字

三步问题 | 动态规划

1.三步问题 题目连接&#xff1a;面试题 08.01. 三步问题 三步问题。有个小孩正在上楼梯&#xff0c;楼梯有n阶台阶&#xff0c;小孩一次可以上1阶、2阶或3阶。实现一种方法&#xff0c;计算小孩有多少种上楼梯的方式。结果可能很大&#xff0c;你需要对结果模1000000007。 2…

如何使用群联AI智能云防护来防御数据链路层

首先&#xff0c;需要明确的是&#xff0c;数据链路层的安全威胁和防护措施主要涉及到MAC地址管理、差错检测、加密和认证等方面。群联科技虽然可能没有在其官方文档中直接提到针对数据链路层的特定解决方案&#xff0c;但可以从其AI云防护技术的角度来推测一些可能的策略。 智…

C语言 指针——指针变量的定义、初始化及解引用

目录 指针 内存如何编址&#xff1f; 如何对变量进行寻址&#xff1f; 用什么类型的变量来存放变量的地址? 如何显示变量的地址?​编辑 使用未初始化的指针会怎样&#xff1f; NULL是什么&#xff1f; 如何访问指针变量指向的存储单元中的数据&#xff1f; 指针变量的…

APP原生开发与框架开发的优劣势

电话管家APP商用也有几年时间了&#xff0c;但是客户一直都有遇到一些问题。 为什么我们的APP老是要升级&#xff1f; 为什么有些手机使用体验不好&#xff1f; 为什么有些公司的APP几天就开发出来上线了&#xff1f; 为什么有些公司的APP那么便宜&#xff1f; 今天就来从…

家政预约小程序08服务详情

目录 1 创建页面2 创建URL参数3 配置数据详情组件4 从分类页跳转到详情页5 搭建详情页总结 现在我们的小程序已经在首页和分类页展示了服务的列表信息&#xff0c;当用户点击具体的内容的时候需要打开详情页&#xff0c;本篇介绍一下详情页的开发。 1 创建页面 打开应用编辑器…

中学生学人工智能系列:如何用AI学英语

经常有读者朋友给公众号《人工智能怎么学》留言咨询如何使用人工智能学习语文、数学、英语等科目。这些都是中学教师、中学生朋友及其家长们普遍关注的问题。仅仅使用留言回复的方式&#xff0c;不可能对这些问题做出具体和透彻的解答&#xff0c;因此本公众号近期将推出中学生…

如何在phpMy管理对Joomla后台的登录密码进行重置

本周有一个客户&#xff0c;购买Hostease的虚拟主机&#xff0c;询问我们的在线客服&#xff0c;如何在phpMy管理对Joomla后台的登录密码进行重置&#xff1f;我们为用户提供相关教程&#xff0c;用户很快解决了遇到的问题。在此&#xff0c;我们分享这个操作教程&#xff0c;希…

别再 pip install 了!一个绝佳的包管理器:pipx

在Python开发过程中&#xff0c;我们常常需要安装各种各样的工具库。有些库是项目级别的&#xff0c;比如Django或者Flask&#xff0c;而有些库是我们在整个系统中都可能用到的命令行工具&#xff0c;比如black、flake8、httpie等。对于后者&#xff0c;传统的pip安装方式可能会…

jenkins集成

jenkins是一款广泛使用的开源持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&#xff09;工具&#xff0c;主要用于自动化构建、测试和部署软件。以下是关于如何集成Jenkins的详细介绍&#xff1a; 安装Jenkins&#xff1a; 要安装Jenkins&#xff0c;您需要按照…

DependencyCheck工具使用

1、工具下载地址 Releases jeremylong/DependencyCheck GitHub 2、工具使用 ./dependency-check.sh --disableRetireJS --disableNodeJS --project test -s /test/ -o /home/clog/test/report10 --noupdate

LeetCode题练习与总结:平衡二叉树--110

一、题目描述 给定一个二叉树&#xff0c;判断它是否是平衡二叉树。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&#xff1a;false示例 3&#xff1a…