cpulimit设计理念及其思考

背景

前几天,同事咨询了我一个问题:IO占用能和cpu使用率那样,有方法来控制吗?这个问题的背景是因为客户提了两个需求,如下:

说实话,针对这两点需求,我的第一反应是有一点思路,但是并没有具体的方案。比如可以通过sleep的方式减少对CPU、IO的占用。但是如何去设计呢?令我比较有兴趣。

经讨论,进程CPU使用率可以通过开源工具cpulimit进行控制,但是进程的IO占用目前并没有好的工具可以实现。好奇心爆棚的我,比较想了解cpulimit的实现原理,以及在这之上是否能够对进程IO占用有借鉴意义。

开源工具cpulimit体积很小,就2千行代码左右,两个小时基本就能看完。了解其大致原理后,感觉对实现控制进程IO占用的功能也有一定的参考作用。通过本篇,希望能够和大家分享一下我的思路。若由任何不妥的地方或问题,欢迎评论区讨论。

cpulimit 使用及设计分析

开源工具cpulimit可通过git下载,本地编译,验证。

下载:

git clone https://github.com/opsengine/cpulimit.git

编译:

yihua@ubuntu:~/cpulimit$ make
cd src && make all
make[1]: Entering directory '/home/yihua/cpulimit/src'
cc -c list.c -Wall -g -D_GNU_SOURCE
cc -c process_iterator.c -Wall -g -D_GNU_SOURCE
cc -c process_group.c -Wall -g -D_GNU_SOURCE
cc -o cpulimit cpulimit.c list.o process_iterator.o process_group.o -Wall -g -D_GNU_SOURCE
cpulimit.c:46:18: warning: extra tokens at end of #ifdef directive#ifdef __APPLE__ || __FREEBSD__make[1]: Leaving directory '/home/yihua/cpulimit/src'
cd tests && make all
make[1]: Entering directory '/home/yihua/cpulimit/tests'
cc -o busy busy.c -lpthread -Wall -g
cc -I../src -o process_iterator_test process_iterator_test.c ../src/list.o ../src/process_iterator.o ../src/process_group.o -lpthread -Wall -g
process_iterator_test.c:31:18: warning: extra tokens at end of #ifdef directive#ifdef __APPLE__ || __FREEBSD__make[1]: Leaving directory '/home/yihua/cpulimit/tests'
yihua@ubuntu:~/cpulimit$

验证:

  1. 通过stress工具模拟CPU密集型场景。stress -c 4,4表示创建4个子进程,因为我的环境是4核,故创建4个进程。
  2. 通过mpstat查看系统的cpu使用率。mpstat -P ALL 1,显示更新频率为1s。
yihua@ubuntu:~/cpulimit$ mpstat -P ALL 1
Linux 4.15.0-213-generic (ubuntu)       12/20/2023      _x86_64_        (4 CPU)12:57:49 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
12:57:50 AM  all   99.25    0.00    0.75    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:50 AM    0  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:50 AM    1   97.00    0.00    3.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:50 AM    2  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:50 AM    3  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.0012:57:50 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
12:57:51 AM  all   99.50    0.00    0.50    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:51 AM    0  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:51 AM    1   98.00    0.00    2.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:51 AM    2  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:51 AM    3  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.0012:57:51 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
12:57:52 AM  all   99.00    0.00    1.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:52 AM    0   99.00    0.00    1.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:52 AM    1   97.98    0.00    2.02    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:52 AM    2  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
12:57:52 AM    3   99.00    0.00    1.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00

由上可知,CPU占用率约100%。

  1. 通过cpulimit控制stress进程的cpu使用率。./src/cpulimit -p pid -l 50 -i
  2. 再观察系统的CPU使用率,如下:
yihua@ubuntu:~$ mpstat -P ALL 1
Linux 4.15.0-213-generic (ubuntu)       12/20/2023      _x86_64_        (4 CPU)01:10:45 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
01:10:46 AM  all   13.40    0.00    1.99    0.00    0.00    0.00    0.00    0.00    0.00   84.62
01:10:46 AM    0   11.11    0.00    2.02    0.00    0.00    0.00    0.00    0.00    0.00   86.87
01:10:46 AM    1   15.84    0.00    3.96    0.00    0.00    0.00    0.00    0.00    0.00   80.20
01:10:46 AM    2   12.87    0.00    0.99    0.00    0.00    0.00    0.00    0.00    0.00   86.14
01:10:46 AM    3   13.13    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   86.8701:10:46 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
01:10:47 AM  all   12.00    0.00    1.50    0.00    0.00    0.25    0.00    0.00    0.00   86.25
01:10:47 AM    0   10.00    0.00    2.00    0.00    0.00    0.00    0.00    0.00    0.00   88.00
01:10:47 AM    1   15.15    0.00    3.03    0.00    0.00    0.00    0.00    0.00    0.00   81.82
01:10:47 AM    2   12.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   88.00
01:10:47 AM    3   12.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   88.0001:10:47 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
01:10:48 AM  all   12.78    0.00    0.75    0.00    0.00    0.00    0.00    0.00    0.00   86.47
01:10:48 AM    0   15.00    0.00    3.00    0.00    0.00    0.00    0.00    0.00    0.00   82.00
01:10:48 AM    1   12.87    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   87.13
01:10:48 AM    2   12.12    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   87.88
01:10:48 AM    3   11.88    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   88.12

由上可知,CPU0 + CPU1 + CPU2 + CPU3约等于50。

cpulimt设计流程分析

cpulimit真的很小,建议小伙伴们自己走读一遍,加强理解。cpulimit -p pid -l 50 -i其大致流程如下:

  1. 设定一个应用时间片TIME_SLOT=100ms。计算得到 T w o r k T_{work} Twork时间和 T s l e e p T_{sleep} Tsleep时间。即 T w o r k T_{work} Twork=TIME_SLOT*limit,$T_{sleep}=TIME_SLOT*(1-limit)
  2. 通过pid,在proc文件系统中,找到进程及其子进程的信息目录。
  3. 通过获取/proc/pid/stat文件中的utimestime的时长,utime+stime得到该进程已消耗CPU的节拍数。
  4. 将进程及其子进程的节拍数相加,得到该进程消耗的总的节拍数。即为实际运行时长 T r e l w o r k T_{rel_work} Trelwork
    1. T r e l w o r k T_{rel_work} Trelwork > T w o r k T_{work} Twork,说明超过了限定值。需要释放CPU使用权。cpulimit则会想所有的进程发送信号SIGSTOP,并自身nanosleep TIME_SLOT - T r e l w o r k T_{rel_work} Trelwork时长。
    2. T r e l w o r k T_{rel_work} Trelwork < T w o r k T_{work} Twork,说明为达到限定值,可以继续占用CPU使用权。cpulimit则会想所有的进程发送信号SIGCONT,并自身nanosleep T w o r k T_{work} Twork - T r e l w o r k T_{rel_work} Trelwork 时长。
  5. 重复1~3步骤。

伪代码大致流程如下:

while(true)
{//1. 更新进程组的资源消耗update_process_group(&pgroup);...for (node = pgroup.proclist->first; node != NULL; node = node->next) {struct process *proc = (struct process*)(node->data);if (proc->cpu_usage < 0) {continue;}if (pcpu < 0) pcpu = 0;pcpu += proc->cpu_usage;}...//2. 计算sleeptime、worktimeworkingrate = MIN(workingrate / pcpu * limit, 1);tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec;//3. 唤醒进程组while (node != NULL){struct list_node *next_node = node->next;struct process *proc = (struct process*)(node->data);kill(proc->pid,SIGCONT);node = next_node;}//3.1 让进程组运行twork时长gettimeofday(&startwork, NULL);nanosleep(&twork, NULL);gettimeofday(&endwork, NULL);//3.2 让进程组sleep twork时长if (tsleep.tv_nsec>0) {node = pgroup.proclist->first;while (node != NULL){struct list_node *next_node = node->next;struct process *proc = (struct process*)(node->data);kill(proc->pid,SIGSTOP);node = next_node;}nanosleep(&tsleep,NULL);
}

思考

进程IO占用是否能够采用上述的方式:通过监控进程发送SIGSTOPSIGCONT控制目标进程的运行和休眠呢?

其实略加思考,就知道是不可行的。我们可以思考一下这个问题:客户提出IO占用可控的目的是什么?

答:无非就是防止该进程一直占用IO资源,其它业务进程获取不到资源,影响正常业务执行。如果采用cpulimit的方案,那么就会出现一种情况:若目标进程正在持有该IO资源,即使操作系统通过SIGSTOP信号,将目标进程挂起,那么其它进程也无法获取该IO资源,并不能达到客户的预期

脑洞大开(仅供参考)

通过cpulimit的设计思路,以及我们程序的应用场景,于是乎我想到了一种解决方案,似乎也能达到IO占用控制的效果。如有任何不对的地方,还请不吝指教。

我们的程序IO主要就是read/write 分区。我可以封装自己的read/write接口。其逻辑大致如下:

  1. 设定一个应用时间片TIME_SLOT=100ms。计算得到 T w o r k T_{work} Twork时间和 T s l e e p T_{sleep} Tsleep时间。即 T w o r k T_{work} Twork=TIME_SLOT*limit T s l e e p T_{sleep} Tsleep=TIME_SLOT*(1-limit)
  2. 通过proc文件系统,获取进程当前已经消耗的cpu时间 T i o 1 T_{io1} Tio1。并记录当前时间系统时间 T 总 1 T_{总1} T1
  3. 执行read/write系统调用。
  4. 通过proc文件系统,获取进程当前已经消耗的cpu时间 T i o 2 T_{io2} Tio2。并记录当前时间系统时间 T 总 2 T_{总2} T2
    1. T i o 2 T_{io2} Tio2 - T i o 1 T_{io1} Tio1 < T w o r k T_{work} Twork T 总 2 T_{总2} T2- T 总 1 T_{总1} T1 < T w o r k T_{work} Twork ,说明还未达到限定值,可以继续执行IO。
    2. T i o 2 T_{io2} Tio2 - T i o 1 T_{io1} Tio1 > T w o r k T_{work} Twork,说明单位时间内,已达到占用比,执行下一步骤休眠。
    3. T 总 2 T_{总2} T2- T 总 1 T_{总1} T1 > T w o r k T_{work} Twork,说明单位时间内,IO被其它进程抢占,未达到限定值。回到步骤2。
  5. nanosleep T w o r k T_{work} Twork - ( T 总 2 T_{总2} T2- T 总 1 T_{总1} T1) 。

以上便是我暂时的设想,当然真正实现时,需要考虑一些细节。比如步骤3和步骤4.1,若不加处理,大量的时间会消耗在proc文件系统读取上。

总结

以上便是的初步设想,后续有时间会进行代码验证,有兴趣的朋友可以关注哈。
若我的内容对您有所帮助,还请关注我的公众号。不定期分享干活,剖析案例,也可以一起讨论分享。
我的宗旨:
踩完您工作中的所有坑并分享给您,让你的工作无bug,人生尽是坦途。
在这里插入图片描述

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

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

相关文章

基于RocketMQ实现分布式事务

前言 在上一篇文章Spring Boot自动装配原理以及实践我们完成了服务通用日志监控组件的开发&#xff0c;确保每个服务都可以基于一个注解实现业务功能的监控。 而本文我们尝试基于RocketMQ实现下单的分布式的事务。可能会有读者会有疑问&#xff0c;之前我们不是基于Seata完成了…

【linux】解决matplotlib中文显示乱码

一、Debian操作系统版本号 $ cat /etc/debian_version 12.4 二、问题 matplotlib中文显示乱码。 三、解决方法&#xff1a; plt.rcParams["font.sans-serif"] "Noto Sans CJK JP" 四、解决过程 1.问GPT 回答了一些相关问题&#xff0c;但是好像用…

23 聪明的设计

仅用加法的实在是想不出来。。 #include <iostream> using namespace::std; using std::cout; using std::cin; int ljq(int n) {if(n < 1){return n;}else{return (nljq(n-1));} }int main() {int n;cin >> n;std::cout << ljq(n);return 0; }

【MATLAB第85期】基于MATLAB的2023年智能进化算法/元启发式算法合集(持续更新)

【MATLAB第85期】基于MATLAB的2023年智能进化算法/元启发式算法合集&#xff08;持续更新&#xff09; 1.海象进化算法&#xff08;Walrus Optimization Algorithm&#xff09; 作者&#xff1a;Pavel Trojovsk and Mohammad Dehghani 2.暴龙优化算法&#xff08;Tyrannosa…

Go 语言中并发的威力

发挥效率和响应能力 并发是现代软件开发中的一个基本概念&#xff0c;它使程序能够同时执行多个任务&#xff0c;提高效率和响应能力。在本文中&#xff0c;我们将探讨并发在现代软件开发中的重要性&#xff0c;并深入了解 Go 处理并发任务的独特方法。 在现代软件开发中并发…

基于LSTM的情感分析

本文以情感分析为主题&#xff0c;介绍了其在自然语言处理中的重要性以及应用场景。传统的循环神经网络&#xff08;RNN&#xff09;在处理长序列时存在问题&#xff0c;而引入了记忆细胞、输入门、输出门和遗忘门的LSTM模型能够有效解决这一问题。数据集和预处理在机器学习和自…

appium工具相关

一、appium基本介绍 1、appium 基本介绍 定义&#xff1a;appium 就是一款非常流行和好用的第三方工具&#xff0c;通过该工具我们可以配合 python 脚本实现 IOS / Android 多平台的APP 自动化测试。作用&#xff1a;在编写测试脚本的PC机和运行 APP 的真机或设备之前充当一个…

【matlab】绘制横状双组渐变柱状图

【matlab】绘制横状双组渐变柱状图

Open3D 最小二乘拟合平面(直接求解法)

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。爬虫自重。 一、算法原理 平面方程的一般表达式为: A x + B y + C z

JDK bug:ciObjectFactory::create_new_metadata:原因完全解析

文章目录 1、问题2.详细日志2.关键日志3.结论4.JDK&#xff1a;bug最终bug链接&#xff1a; 京东遇到过类似bug各位大佬如果有更详细的解答可以留言。 1、问题 服务不通&#xff0c;接口404&#xff0c;查看日志有一下截图&#xff0c;还有一个更详细的日志 2.详细日志 # #…

最详细手把手教你安装 Vivado2017.4

软件下载 官网可下载各个版本 百度网盘链接 Vivado2017.4 License 软件安装 解压缩安装包&#xff0c;双击运行安装程序 xsetup.exe&#xff1a; 忽略软件更新&#xff0c;点击 Continue&#xff1a; 点击 Next&#xff1a; 全部勾选 I Agree&#xff0c;点击 Next&#x…

【软件工程】软件工程复习题库2023

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; SpringCloud MybatisPlus JVM 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 软件工程复习题库 一、选择题二、填空题三、判断题四…