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

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

有时,或许这样的告警是因为一些恶意行为或者bug导致。但是有时,我们希望我们编写的程序能够尽可能压榨性能去尽快处理一些工作,此时CPU占满或许是一个很正常的行为。可如此就会触发告警,加之一些同道告警强迫症发作,此时就会很为难。如果增加一些sleep操作,莫名其妙的睡眠似乎总是不够优雅与完美。
CPU告警不用愁,用C语言编写CPU使用率限制程序_第1张图片那么,有没有什么好的解决方案呢?

今天码哥就给大家提供一种解决方案。
CPU告警不用愁,用C语言编写CPU使用率限制程序_第2张图片
或许有些人听过用过docker,docker容器就是可以做到资源隔离与资源配额的。似乎是我们想要的,那么是否可以借鉴一下呢?

答案是肯定的。

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

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

为了应对上面的那种资源限制需求,码哥展示一个用C语言编写的启动程序,帮你轻松解决这类问题。
CPU告警不用愁,用C语言编写CPU使用率限制程序_第3张图片
限于篇幅,我们只展示CPU限制的方式,内存和IO相关的限制方法与其类似,可参阅网上一些人的文章自行扩展。

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

/*
 * Author: 码哥比特
 */
#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#include 

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 COMMAND\n", name);
    printf("\t-c\t\tset cpu rate\n");
    printf("\t-g\t\tgroup name\n");
    printf("\t-d\t\tdelete group\n");
}

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. %s\n", strerror(errno));
            exit(1);
        }
        memset(tmp, 0, sizeof(tmp));
        n = snprintf(tmp, sizeof(tmp)-1, "%s\n", 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. %s\n", strerror(errno));
            exit(1);
        }
        memset(tmp, 0, sizeof(tmp));
        n = snprintf(tmp, sizeof(tmp)-1, "%d\n", 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 given\n");
        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 

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

正常情况下100%无疑,如图:
在这里插入图片描述
下面我们用我们的程序启动器来启动a.out并限制其CPU为89%,如图:
在这里插入图片描述
我们可以看到,其CPU占比会在89%上下小幅浮动,大家可自行尝试。

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

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

你可能感兴趣的:(c语言,经验分享,linux,学习,容器,后端,微服务)