linux 利用cgroups 手搓简易资源控制库

上次写了C语言实现的log模块,本次使用c++结合log,利用Linux下cgroup资源控制手写一个资源控制库,首先了解cgroup,方便理解后面代码

log参考链接

1. 目录结构

image

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) 效果

image

4. TODO LIST

目前只做了CPU和内存的并且是固定比例,bio的读写与网络带宽还没做,思路是一样的。

ps:

bio 只会限制direct io 并不会限制 buffer io,如果要限制进程读写裸设备或直接在块设备上操作等情况才适合。

posted @ 2025-02-06 16:55  BaldButStrong  阅读(47)  评论(0)    收藏  举报