在一个计算机系统中,CPU是一个有限的核心的资源,有时为了充分利用CPU的资源,我们会把业务混合部署到一台机器上,这些业务有可能包括核心业务和非核心业务,为了不影响核心业务,我们需要限制非核心业务CPU资源的利用率。
背后原理
如果一个进程的CPU利用率是40%,表示这个进程占用了40%的CPU时间。那么为了限制进程的CPU不超过40%,我们可以让这个进程运行40%的时间,然后停止60%的时间。通过给进程发送SIGCONT和SIGSTOP信号可以控制进程的启停。
代码示例
根据上面的原理,代码如下:
#include <errno.h> #include <string.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <signal.h> #include <vector> #include <getopt.h> static int g_process_count = 8; static int g_cpu_usage = 80; static std::vector<pid_t> g_child_processes; static int g_show_usage = 0; static void show_usage() { fprintf(stdout, "Usage: cpu [--process-count=N] [--cpu-usage=N]\n" "\n" " -c, --process-count=N process count\n" " -u, --cpu-usage=N cpu usage\n" "\n" "Examples:\n" " cpu --process-count=8 --cpu-usage=60\n"); } int child_main() { for (;;) { } return 0; } int main(int argc, char* argv[]) { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"process-count", 1, 0, 'c' }, {"cpu-usage", 1, 0, 'u' }, {"help", 0, 0, 'h' }, {0, 0, 0, 0 } }; c = getopt_long(argc, argv, "c:u:?h", long_options, &option_index); if (c == -1) break; switch (c) { case 0: break; case 'c': g_process_count = atoi(optarg); break; case 'u': g_cpu_usage = atoi(optarg); break; case 'h': g_show_usage = 1; break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind != argc) { show_usage(); return -1; } if (g_show_usage) { show_usage(); return 0; } fprintf(stdout, "process count %d cpu usage %d\n", g_process_count, g_cpu_usage); for (int i = 0; i < g_process_count; ++i) { pid_t pid = fork(); if (pid == -1) { fprintf(stderr, "fork failed %d:%s\n", errno, strerror(errno)); return -1; } if (pid == 0) { // child child_main(); exit(0); } g_child_processes.push_back(pid); } for (;;) { for (std::vector<pid_t>::const_iterator iter = g_child_processes.begin(); iter != g_child_processes.end(); ++iter) { pid_t child_pid = *iter; kill(child_pid, SIGSTOP); } usleep((100 - g_cpu_usage) * 1000); for (std::vector<pid_t>::const_iterator iter = g_child_processes.begin(); iter != g_child_processes.end(); ++iter) { pid_t child_pid = *iter; kill(child_pid, SIGCONT); } usleep(g_cpu_usage * 1000); } return 0; }
使用g++ -o ./cpu_limit_test ./cpu_limit_test.cpp编译,运行./cpu_limit_test --process-count=4 --cpu-usage=30的效果如下:
process-count参数指定创建的子进程的个数,cpu-usage参数表示cpu的上线。上面的代码,首先创建process-count个子进程,每个子进程都是一个死循环;父进程每隔一段时间给所有的子进程发送SIGCONT和SIGSTOP信号来控制子进程的运行,进而达到控制CPU资源利用率的目的。
总结
通过上面的示例,我们达到了控制CPU资源利用率的目的,细心的读者可能已经发现,上面示例的子进程的CPU利用率曲线实际上是一条直线。同样的原理,我们修改下代码就可以进而达到让子进程的CPU曲线成正弦或余弦函数,这里我就不展开了。开源项目cpulimit用的也是同样的原理,大家也可以参考I下它的实现。感谢您的阅读,欢迎评论。