C++编程实践——Linux下的CPU控制

一、Linux下资源的处理

与Windows操作系统相比,在Linux下,可能由于是开源的原因,对相关资源的控制更加细致全面。比如开发者都知道,其更新升级的源地址可以进行直接编辑应用;网络支持的文件句柄数量也可以编辑相关的配置文件进行处理。而对进程、内存等的监控和管理也可以在 /proc文件中的相关的配置文件中进行处理。比如下面的方法可以对交换内存进行设置:

echo 100 > /proc/sys/vm/swappiness

不过,大家还是要注意,在Linux下,各个方面的资源管理控制往往分为读和写以及对权限的需要。这个非常重要,往往这些细节被忽略导致一些意外的问题。举一个简单的例子,有些命令或文件如果不使用 root权限操作,则会显示没有这些命令或文件。这对于一些新手,可能会觉得是没有安装相关库,从而让操作者做一些额外的操作。

二、CPU的控制

而在实际的开发中,CPU的控制是一个相当重要的场景,毕竟从有计算机以来,几乎所有的工作和任务都是围绕着CPU进行的。特别是随着CPU的功能不断增强,从单核到多核,以及更高的工作频率,更大的缓存等等,这都产生了在不同粒度上对CPU资源进行管控的必要。要了解CPU的控制,就需要开发者熟悉操作系统相关的CPU的调度和控制原理,最好能与相对应的内核中的源码对应起来。来看一下调度器的主要数据结构:

struct sched_entity {
    //整体权重
    struct load_weight load;         
    struct rb_node run_node;         
    u64 exec_start;                 
    u64 sum_exec_runtime;
    // 虚拟运行时间
    u64 vruntime;                   
    u64 prev_sum_exec_runtime;     
};

当然,在不同的版本的内核中可能细节会有不同,大家可根据自己的实际版本来分析即可。
对CPU的控制,肯定是在内核源码控制更方便,但为了安全和方便,本文只介绍在应用层次上的CPU控制,这样会更适合广大开发者来学习和应用相关的技术。要想对CPU进行管理 ,首先需要可以查看相关的CPU信息的命令和工具,这样才能在些基础上进行CPU的管理,常见的CPU查看相关命令和工具有:

  1. 命令
#查看CPU的详细信息
lscpu
cat /proc/cpuinfo

# 查看CPU频率
cpupower frequency-info
cat /proc/cpuinfo | grep "cpu MHz"
  1. 使用工具
    可以安装下面的工具:
# Ubuntu/Debian
sudo apt install cpufrequtils linux-tools-common

当然,相关的工具有很多,同时由于云技术的发展,更多的云相关的工具和软件也有很多,大家可以根据自己的情况来进行安装和应用

当知晓了当前的CPU应用情况又安装了相关管理工具后,就可以在当前实际项目中通过代码或命令等操作根据CPU的控制粒度进行细节上的管控,从而让其更友好的实现设计的目的。一般来说,对CPU的管控主要有以下几个方面:

  1. 设置频率
    这可以算是CPU应用的最基本、最粗暴的应用。特别是在某些可以超频应用的CPU上,更有效果
  2. 设置核心的应用
    即对多核心的CPU出于某种场景的应用,禁用或启用一些核心
  3. 设置CPU的亲和性和中断的亲和性
    这个在前面的DPDK等分析中介绍过,让某个CPU与某个进程或线程绑定,防止出现切换出现的效率问题
  4. 设置优先级
    这个相对来说要有些鸡肋,因为虽然在不同的操作系统上都有大量的优先级设置,但实际开放给上层应用的,却非常有限

在不同的场景下,对CPU的控制有很多种方式,这就需要开发者对实际的CPU的信息有着详细的掌握,这样才能根据其可控制的粒度和相关方法实现对CPU资源利用的最优化。知己知彼,百战百胜。

三、解决方案及示例

针对上面的应用,实现的方式主要有以下几种:

  1. 命令处理
    相关的操作命令如下:
# 查看信息
cpufreq-info
# 设置频率
sudo cpupower frequency-set -d 1.2GHz    # 最小频率
sudo cpupower frequency-set -u 3.5GHz    # 最大频率

# 设置频率-需userspace
sudo cpupower frequency-set -f 2.4GHz

# 禁用CPU核心服务
sudo systemctl stop cpuset

# 查看进程的CPU亲和性
taskset -p <PID>

# 启动程序时绑定到特定CPU
taskset -c 0,1 ./app

# 修改运行中进程的CPU亲和性
taskset -cp 0,2,4 <PID>
  1. 配置文件
    具体的配置文件如下控制:
# 查看CPU核心状态
cat /sys/devices/system/cpu/online
ls /sys/devices/system/cpu/cpu*/online
# 禁用CPU核心(禁用CPU1)
echo 0 | sudo tee /sys/devices/system/cpu/cpu1/online
# 启用CPU核心
echo 1 | sudo tee /sys/devices/system/cpu/cpu1/online

# 创建CPU控制组
sudo mkdir /sys/fs/cgroup/testgroup

# 设置CPU权重(默认100)
echo 300 > /sys/fs/cgroup/testgroup/cpu.weight

# 设置CPU使用上限(单位:微秒)
echo "200000 200000" > /sys/fs/cgroup/testgroup/cpu.max

# 添加进程到控制组
echo <PID> > /sys/fs/cgroup/testgroup/cgroup.procs

# 编辑服务文件
sudo systemctl edit myservice

# 添加以下内容:
[Service]
CPUWeight=300
CPUQuota=85%
CPUShares=1024

# 编辑配置文件
sudo vim /etc/sysctl.d/99-cpu.conf

# 设置参数
kernel.sched_min_granularity_ns = 12000000
kernel.sched_wakeup_granularity_ns = 16000000
kernel.sched_migration_cost_ns = 6000000

# 使能配置
sudo sysctl -p /etc/sysctl.d/99-cpu.conf
  1. 代码控制
    cgroup有V1和V2版本的不同,一般来说推荐使用V2版本,特别是在云环境和虚拟环境下。判断系统使用哪个版本的方法可以使用下面的命令:
stat -fc %T /sys/fs/cgroup/
#输出 cgroup2fs,说明系统使用 cgroup v2;输出 tmpfs,说明使用 cgroup v1

注意:cgroup V2不兼容V1
下面看一下其具体的一个代码:

#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <sys/stat.h>
#include <unistd.h>

void createCgroup(const std::string &cgroupPath) {
  if (mkdir(cgroupPath.c_str(), 0755) == -1) {
    exit(EXIT_FAILURE);
  }
}

// 设置CPU限制:微秒,100000=100ms
void setCpuLimit(const std::string &cgroupPath, int cpuQuota) {
  std::string cpuMaxPath = cgroupPath + "/cpu.max";
  std::ofstream cpuMax(cpuMaxPath);
  if (!cpuMax.is_open()) {
    exit(EXIT_FAILURE);
  }
  cpuMax << cpuQuota << " 100000";
  cpuMax.close();
}

// add cgroup
void addProcess(const std::string &cgroupPath, pid_t pid) {
  std::string procsPath = cgroupPath + "/cgroup.procs";
  std::ofstream procs(procsPath);
  if (!procs.is_open()) {
    exit(EXIT_FAILURE);
  }
  procs << pid;
  procs.close();
}

int main() {
  //  root
  if (getuid() != 0) {
    return EXIT_FAILURE;
  }

  const std::string cgroupName = "test_cgroup";
  const std::string cgroupPath = "/sys/fs/cgroup/" + cgroupName;

  createCgroup(cgroupPath);

  // limit
  setCpuLimit(cgroupPath, 50000); // 50ms/100ms

  // add cgroup
  pid_t pid = getpid();
  addProcess(cgroupPath, pid);

  // test
  volatile int i = 0;
  while (i < 1000000000) {
    i++;
    if (i % 10000000 == 0) {
      std::cout << "run task... " << i << std::endl;
    }
  }

  // rm cgroup
  rmdir(cgroupPath.c_str());
  return EXIT_SUCCESS;
}

代码非常简单,创建cgroup目录,添加当前进程到组并限制CPU资源,最后弄了一个测试的负载。

四、总结

开发者不但应该把代码掌握的深入,对应用环境,包括各种平台的实际情况也要相当清楚。这就和打仗一样,再好的军事技术如果无法与实践技术结合,结果可想而知。赵括其实就是一个活生生的例子。项目的实践是一个系统的工程,开发者切记不能只看到其中部分,而忽略全局。与大家共勉!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值