linux 利用cgroups 手搓简易资源控制库
上次写了C语言实现的log模块,本次使用c++结合log,利用Linux下cgroup资源控制手写一个资源控制库,首先了解cgroup,方便理解后面代码
1. 目录结构
2. 资源控制模块代码
1) setSource.cpp
#include "setSource.h" #include <fcntl.h> #include <cstdio> #include <cstdlib> #include <fstream> #include <cerrno> #include <string> extern "C" { #include <unistd.h> #include <sys/stat.h> #include <string.h> #include "log.h" } /* 各级别乘的系数 */ #define HIGH (float)-1 #define MEDIUM (float)0.5 #define LOW (float)0.2 /* 各个子系统的路径,方便后续创建文件夹等操作 */ #define CGROUP_CPU_PATH "/sys/fs/cgroup/cpu/" #define CGROUP_MEM_PATH "/sys/fs/cgroup/memory/" #define CGROUP_BIO_PATH "/sys/fs/cgroup/blkio/" #define CGROUP_NET_PATH "/sys/fs/cgroup/net_cls/" /* 各个子系统默认值文件的路径 */ #define DEFAULT_CPU_CONF "/sys/fs/cgroup/cpu/cpu.cfs_period_us" #define DEFAULT_MEM_CONF "/sys/fs/cgroup/memory/memory.limit_in_bytes" #define DEFAULT_BIOR_CONF "" #define DEFAULT_BIOW_CONF "" #define DEFAULT_NET_CONF "" /* 资源限制时需要修改的配置文件名 */ #define PIDCONF "/cgroup.procs" #define CPUCONF "/cpu.cfs_quota_us" #define MEMCONF "/memory.limit_in_bytes" #define BIORCONF "" #define BIOWCONF "" #define NETCONF "" /***************************************** * * 读取配置文件的默认值 * ****************************************/ int setSource::readConfigFileToint(string config_name) { string buf; int res = -1; ifstream ifs(config_name); if (ifs.is_open()) { getline(ifs, buf); if (!buf.empty()) { res = stoi(buf); } ifs.close(); } else { LogWrite(ERROR, "%s%s%s", "readConfigFileToint: read value from ",config_name.c_str()," error"); } return res; } /***************************************** * * 将修改值写入配置文件 * ****************************************/ int setSource::writeConfigFile(string config_name, int set_value) { int res = 0; ofstream ofs(config_name); if (ofs.is_open()) { ofs << set_value; ofs.close(); } else { res = -1; LogWrite(ERROR, "%s%d%s%s%s","writeConfigFile: Set value ", set_value, " to ", config_name.c_str()," error"); } return res; } /***************************************** * * 根据级别获取对应的系数 * ****************************************/ float setSource::sourceLevel(int level_value) { switch (level_value) { case 3: return HIGH; break; case 2: return MEDIUM; break; case 1: return LOW; break; default: return HIGH; break; } } /***************************************** * * 资源限制操作接口 * ****************************************/ void setSource::setSourceValue(string dir_path, string pName, string confName, string defauleName, int pid) { if (dir_path.empty() || pName.empty() || confName.empty() || defauleName.empty()) { LogWrite(ERROR, "%s", "string is NULL"); } string dir_name = dir_path + pName; string pid_conf = dir_name + PIDCONF; string change_conf = dir_name + confName; int value = (int)(readConfigFileToint(defauleName) * sourceLevel(source_level)); if (access(dir_name.c_str(), F_OK) == -1) { if (mkdir(dir_name.c_str(), 0777) < 0 && errno != EEXIST) { LogWrite(ERROR,"%s%s%s","setSourceValue:mkdir ",dir_name.c_str()," error"); return; } } if (writeConfigFile(pid_conf, pid) != 0 || writeConfigFile(change_conf, value) != 0) { LogWrite(ERROR,"%s%s%s%s%s","setSourceValue: set value to ", confName.c_str(), " or ", pid_conf.c_str()," error"); } } /***************************************** * * 外部调用接口入口,处理设置各类资源限制 * ****************************************/ void setSource::dealWtithSetSource(string name, int pid, int mode) { LogWrite(INFO, "%s", "dealWtithSetSource begin"); if (source_level == 3) { LogWrite(INFO, "%s", " no limited"); return; } if (mode & T_CPU) { LogWrite(DEBUG, "%s", "set cpu"); setSourceValue(CGROUP_CPU_PATH, name, CPUCONF, DEFAULT_CPU_CONF, pid); } if (mode & T_MEM) { LogWrite(DEBUG, "%s", "set memory"); setSourceValue(CGROUP_MEM_PATH, name, MEMCONF, DEFAULT_MEM_CONF, pid); } if (mode & T_BIOREAD) { LogWrite(DEBUG, "%s", "set bio read"); setSourceValue(CGROUP_BIO_PATH, name, BIORCONF, DEFAULT_BIOR_CONF, pid); } if (mode & T_BIOWRITE) { LogWrite(DEBUG, "%s", "set bio write"); setSourceValue(CGROUP_BIO_PATH, name, BIOWCONF, DEFAULT_BIOW_CONF, pid); } if (mode & T_NET) { LogWrite(DEBUG, "%s", "set net"); setSourceValue(CGROUP_NET_PATH, name, NETCONF, DEFAULT_NET_CONF, pid); } LogWrite(INFO, "%s", "dealWtithSetSource end"); } /***************************************** * * 清理资源设置的文件夹 * ****************************************/ void setSource::clearSource(string name) { LogWrite(INFO,"%s", "clearSource begin"); rmdir(name.c_str()); LogWrite(INFO,"%s", "clearSource end"); } /***************************************** * * 构造与析构 * ****************************************/ setSource::setSource(int mod) { source_level = mod; } setSource::~setSource() { }
2) setSource.h
#ifndef _SET_SOURCE_H_ #define _SET_SOURCE_H_ #include <string> #include <iostream> using namespace std; #define T_CPU 0x01 #define T_MEM 0x02 #define T_BIOREAD 0x04 #define T_BIOWRITE 0x08 #define T_NET 0x10 #define T_ALL (T_CPU | T_MEM | T_BIOREAD | T_BIOWRITE | T_NET) class setSource { public: setSource(int mod); ~setSource(); void dealWtithSetSource(string name, int pid, int mode); void clearSource(string name); private: float sourceLevel(int level_value); void setSourceValue(string dir_path, string pName, string confName, string defauleName, int pid); int readConfigFileToint(string config_name); int writeConfigFile(string config_name, int set_value); int source_level; }; #endif /* _SET_SOURCE_H_ */
3)Makefile
# # MakeFile # # 编译器 gcc CC=g++ -std=c++11 # log库与头文件目录 LIBDIR=$(PWD)/lib INCLUDEDIR=$(PWD)/include # 编译参数 CFLAGS=-Wall -std=c++11 -g -O0 LIBFLAGS=-llog -lsetsource -L $(LIBDIR) # 日志模块文件 LOGSRC=setSource.cpp LOGOBJ=$(LOGSRC:.cpp=.o) # 编出动态库,默认使用动态库编译 LIB=libsetsource.so # 测试程序 OBJS=test # 测试程序文件 SOURCEFILE=test.cpp .PHONY:all all:$(OBJS) $(OBJS):$(LIB) $(SOURCEFILE) $(CC) $(SOURCEFILE) $(LIBFLAGS) $(CFLAGS) -I $(INCLUDEDIR) -o $@ @echo "compile finish" $(LIB):$(LOGOBJ) $(CC) -shared $(CFLAGS) -o $@ $^ @cp -r libsetsource.so ./lib $(LOGOBJ):$(LOGSRC) $(CC) -g -O0 -c -fPIC $^ -llog $(LIBDIR) -I $(INCLUDEDIR) .PHONY:clean clean: rm -fr $(LOGOBJ) $(LIB) $(OBJS)
3. demo与控制效果
1) test.cpp
#include "setSource.h" #include <iostream> #include <unistd.h> #include <cstdlib> int main(int argc, char const *argv[]) { int pid = fork(); if (pid == 0) { sleep(5); while (1) { /* code */ } } else { setSource test(2); test.dealWtithSetSource("test", pid, T_CPU); } return 0; }
2) 效果
4. TODO LIST
目前只做了CPU和内存的并且是固定比例,bio的读写与网络带宽还没做,思路是一样的。
ps:
bio 只会限制direct io 并不会限制 buffer io,如果要限制进程读写裸设备或直接在块设备上操作等情况才适合。
本文作者:BaldButStrong
本文链接:https://www.cnblogs.com/supersimple/p/18701271
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步