您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

用C语言编写CPU使用率限制程序

时间:2020-01-17 14:00:42  来源:  作者:

现在云服务已经深入千家万户了,不仅商用,私用也很多。很多云服务厂商也都有配套的服务器安全模块,可以检测网络流量异常、内存占用量和CPU占用率,并且允许人工设置告警阈值。例如,CPU持续大于90%10分钟,那么你可能就会收到一条告警通知。

有时,或许这样的告警是因为一些恶意行为或者bug导致。但是有时,我们希望我们编写的程序能够尽可能压榨性能去尽快处理一些工作,此时CPU占满或许是一个很正常的行为。可如此就会触发告警,加之一些同道告警强迫症发作,此时就会很为难。如果增加一些sleep操作,莫名其妙的睡眠似乎总是不够优雅与完美。

那么,有没有什么好的解决方案呢?

今天码哥就给大家提供一种解决方案。

CPU告警不用愁,用C语言编写CPU使用率限制程序

 

或许有些人听过用过Docker,docker容器就是可以做到资源隔离与资源配额的。似乎是我们想要的,那么是否可以借鉴一下呢?

答案是肯定的。

很多时候,我们的程序只是一次性的任务,且有些任务还依赖一些框架,如果将这种任务装入docker容器运行,显然成本和收益的问题让我们内心不太通达。

linux中,这样的资源配额限制是通过cgroups来实现的,它能够限制CPU、内存、IO等资源的使用程度。当然cgroups还有一些其他的使用功能,这里就不额外延展啦。

为了应对上面的那种资源限制需求,码哥展示一个用C语言编写的启动程序,帮你轻松解决这类问题。

CPU告警不用愁,用C语言编写CPU使用率限制程序

 

限于篇幅,我们只展示CPU限制的方式,内存和IO相关的限制方法与其类似,可参阅网上一些人的文章自行扩展。

下面直接上代码(受限于手机屏幕尺寸,建议大家PC端查看):

/*
 * Author: 码哥比特
 */
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <dirent.h> 
#include <stdlib.h>
#include <sys/stat.h>

char *cpu = NULL;
char *group_name = NULL;
char cpu_basedir[256];
int deleted = 0;

static void print_help(char *name)
{
    printf("Usage:n");
    printf("%s [OPTIONS] SHELL COMMANDn", name);
    printf("t-cttset cpu raten");
    printf("t-gttgroup namen");
    printf("t-dttdelete groupn");
}

static int parse_args(int argc, char *argv[])
{
    //...掠过一些参数解析部分
}

static void set_limit(void)
{
    int fd, n, pid = getpid();
    char tmp[1024];

    if (cpu != NULL) {
        if (mkdir(cpu_basedir, S_IRWXU) < 0) {
            if (errno != EEXIST) {
                fprintf(stderr, "create cpu group failed");
                exit(1);
            }
        }
        memset(tmp, 0, sizeof(tmp));
        snprintf(tmp, sizeof(tmp)-1, "%s/cpu.cfs_quota_us", cpu_basedir);
        if ((fd = open(tmp, O_WRONLY)) < 0) {
            fprintf(stderr, "open cpu file failed. %sn", strerror(errno));
            exit(1);
        }
        memset(tmp, 0, sizeof(tmp));
        n = snprintf(tmp, sizeof(tmp)-1, "%sn", cpu);
        write(fd, tmp, n);
        close(fd);

        memset(tmp, 0, sizeof(tmp));
        snprintf(tmp, sizeof(tmp)-1, "%s/tasks", cpu_basedir);
        if ((fd = open(tmp, O_WRONLY|O_AppEND)) < 0) {
            fprintf(stderr, "open cpu tasks failed. %sn", strerror(errno));
            exit(1);
        }
        memset(tmp, 0, sizeof(tmp));
        n = snprintf(tmp, sizeof(tmp)-1, "%dn", pid);
        write(fd, tmp, n);
        close(fd);
    }
}

int main(int argc, char *argv[])
{
    int idx = parse_args(argc, argv);
    if (group_name == NULL) {
        fprintf(stderr, "group name must be givenn");
        print_help(argv[0]);
        exit(1);
    }
    memset(cpu_basedir, 0, sizeof(cpu_basedir));
    snprintf(cpu_basedir, sizeof(cpu_basedir)-1, "/sys/fs/cgroup/cpu/%s", group_name);
    if (deleted) {
        remove(cpu_basedir);
    }
    if (idx) {
        set_limit();
        execv(argv[idx], argv+idx);
    }
    return 0;
}

为了节省篇幅,代码中略过了一些参数解析部分。从帮助信息中,我们也可大致看到程序的使用方式。

# ./cpuctl -c=89000 -g=test_group a.out  #姑且先叫cpuctl吧

这里,-g是用来设置资源组名的,避免和其他资源组冲突。-c是这个资源组下的所有进程CPU占用总和的上限数值,89000是89%的含义。这里要注意,如果你有两个进程在这个资源组下,那么两个进程是平分89%的,也就是每个进程44.5%。a.out则是我们要执行的程序。

我们用一个简单的死循环来测试一下:

#include <stdio.h>

int main(void)
{
    while (1) {}
    return 0;
}

正常情况下100%无疑,如图:

CPU告警不用愁,用C语言编写CPU使用率限制程序

CPU100%

下面我们用我们的程序启动器来启动a.out并限制其CPU为89%,如图:

CPU告警不用愁,用C语言编写CPU使用率限制程序

CPU 89%

我们可以看到,其CPU占比会在89%上下小幅浮动,大家可自行尝试。

从代码中,我们可以看到,其实限制的方法是在/sys/fs/cgroup/cpu下建立一个test_group目录,然后向其内的cfs_quota_us文件写入限制额度,再向其内的tasks写入希望限制的进程ID。目前从码哥的使用情况来看,阿里和腾讯的cgroups配置都是在/sys/fs/cgroup中的,可能其他的操作系统有不同的路径。

 

喜欢的朋友可以关注码哥,也可以在评论区给码哥留言交流,谢谢观看!



Tags:C语言   点击:()  评论:()
声明:本站部分内容来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除,谢谢。
▌相关评论
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
▌相关推荐
“void”一词的一般含义是“无效或完全空白”。这个术语在计算机编程中起着至关重要的作用。最熟悉的用途是:&middot;虚函数返回类型&middot;虚函数作为功能参数&middot;空指...【详细内容】
2020-09-28   C语言  点击:(10)  评论:(0)  加入收藏
接上文:C语言 printf 格式化输出的详细示例printf() 是一个标准库函数,使用时需要 include 头文件 stdio.h。#include<stdio.h>printf() 函数的调用形式为:printf("格式控制字...【详细内容】
2020-08-31   C语言  点击:(2)  评论:(0)  加入收藏
1 引言举个例子: 在func函数退出后,指针pInt所指的内容*pInt为 12#include <stdio.h>//公众号:C语言与CPP编程int func(int* pRes){ if(pRes == NULL) pRes = new int(1...【详细内容】
2020-08-12   C语言  点击:(7)  评论:(0)  加入收藏
变长数组(VLA)和调用malloc()在功能上有些重合。例如,两者都可用于创建在运行时确定大小的数组:int vlamal(){ int n; int * pi; scanf("%d", &n); pi = (int *) ma...【详细内容】
2020-08-10   C语言  点击:(2)  评论:(0)  加入收藏
The standard library provides a wide variety of functions. This section is a brief synopsis of the most useful. More details and many other functions can be fou...【详细内容】
2020-08-10   C语言  点击:(2)  评论:(0)  加入收藏
整形溢出和提升大部分 C 程序员都以为基本的整形操作都是安全的其实不然,看下面这个例子,你觉得输出结果是什么:int main(int argc, char** argv) { long i = -1; if...【详细内容】
2020-08-05   C语言  点击:(4)  评论:(0)  加入收藏
开始之前,首先来看一个通常我们不会以递归的形式思考的问题。假设我们想计算整数n的阶乘。n的阶乘可写作n!,其结果是1~n之间的各数之积。比如,4!=4&times;3&times;2&times;1。一...【详细内容】
2020-08-05   C语言  点击:(4)  评论:(0)  加入收藏
远程学习和教育的概念。在线教程和视频课程,研究和毕业,科学研讨会,数字网络学习,测试和文学内存用于存储程序中的数据,由存储期、作用域和链接表征。存储期可以是静态的、自...【详细内容】
2020-07-31   C语言  点击:(6)  评论:(0)  加入收藏
体系结构计划外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别有时称为外部存储类别(external storage class),属于该类别的变量称为外部变量(external variabl...【详细内容】
2020-07-30   C语言  点击:(8)  评论:(0)  加入收藏
在一个软件项目中,如果需要在一个文件中包含另一个头文件时,一般有两种包含方式:​​​​​#include <stdio.h>#include “module.h”如果你引用的头文件是标准库的头文件或官...【详细内容】
2020-07-29   C语言  点击:(3)  评论:(0)  加入收藏
函数不一定非要使用C库中的标准函数,如果无法使用这些函数或者不想用它们,完全可以在getchar()和putchar()的基础上自定义所需的函数。假设你需要一个类似puts()但是不会自动...【详细内容】
2020-07-29   C语言  点击:(6)  评论:(0)  加入收藏
C库提供了多个处理字符串的函数,ANSI-C把这些函数的原型放在string.h头文件中。其中最常用的函数有strlen()、strcat()、strcmp()、strncmp()、strcpy()和strncpy()。另外,还...【详细内容】
2020-07-29   C语言  点击:(7)  评论:(0)  加入收藏
指针和多维数组有什么关系?为什么要了解它们的关系?处理多维数组的函数要用到指针,所以在使用这种函数之前,先要更深入地学习指针。至于第1个问题,我们通过几个示例来回答。为简...【详细内容】
2020-07-26   C语言  点击:(6)  评论:(0)  加入收藏
在许多程序中,数组很重要。数组可以作为一种存储多个相关项的便利方式。数组(array)是按顺序存储的一系列类型相同的值,如10个char类型的字符或15个int类型的值。整个数组有一...【详细内容】
2020-07-23   C语言  点击:(3)  评论:(0)  加入收藏
我们尝试用scanf做累加运算:/* summing.c -- sums integers entered interactively */#include <stdio.h>int main(void){ long num; long sum = 0L; /* initial...【详细内容】
2020-07-21   C语言  点击:(2)  评论:(0)  加入收藏
通常,在语句和表达式中应使用类型相同的变量和常量。但是,如果使用混合类型,最好先了解一些基本的类型转换规则。1 类型转换规则1.当类型转换出现在表达式时,无论是unsigned还是...【详细内容】
2020-07-21   C语言  点击:(3)  评论:(0)  加入收藏
char类型用于存储字符(如,字母或标点符号),但是从技术层面看,char是整数类型。因为char类型实际上存储的是整数而不是字符。计算机使用数字编码来处理字符,即用特定的整数表示特定...【详细内容】
2020-07-20   C语言  点击:(11)  评论:(0)  加入收藏
C语言的三个标准目前,有许多C实现可用。在理想情况下,编写C程序时,假设该程序中未使用机器特定的编程技术,那么它的运行情况在任何实现中都应该相同。要在实践中做到这一点,不同...【详细内容】
2020-07-19   C语言  点击:(7)  评论:(0)  加入收藏
C的标准写法为:#include <stdio.h>int main(void) /* a simple program */main是一个极其普通的名称,但是这是唯一的选择。我们可以将其想象为一个容器。C程序一...【详细内容】
2020-07-19   C语言  点击:(4)  评论:(0)  加入收藏
C语言编写程序时,编写的内容被存储在文本文件中,该文件被称为源代码文件(source-code-file)。大部分C系统,包括之前提到的,都要求文件名以.c结尾(如,wordcount.c和budget.c)。在文件...【详细内容】
2020-07-19   C语言  点击:(9)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条