zoukankan      html  css  js  c++  java
  • 利用 cgroup 的 cpuset 控制器限制进程的 CPU 使用

    最近在做一些性能测试的事情,首要前提是控制住 CPU 的使用量。最直观的方法无疑是安装 Docker,在每个配置了参数的容器里运行基准程序。

    对于计算密集型任务,在只限制 CPU 的需求下,直接用 Linux 原生的 cgroup 功能来限制 CPU 使用无疑是最方便的。

    本文简要说明如何使用 cgroup 的 cpuset 控制器限制进程只使用某几个 CPU,更准确的说是某个几个逻辑核。

    1. 查看 CPU 配置

    常用的配置可以用如下 BASH 命令查看。

    cat /proc/cpuinfo | grep "physical id" | sort | uniq # 查看物理 CPU 数量
    cat /proc/cpuinfo | grep "cores" | uniq # 查看每块 CPU 的核心数
    cat /proc/cpuinfo | grep "processor" | wc -l # 查看主机总的逻辑线程数

    特别地,启用了超线程的话,每个 CPU 物理核心会模拟出 2 个线程,也叫逻辑核。判断方式如下:

    是否开启超线程 = 物理 CPU 数量 * 每块 CPU 核心数 / 总逻辑线程数 == 2

    2. 什么是 NUMA

    这里提到一个概念叫 NUMA,主机板上如果插有多块 CPU 的话,那么就是 NUMA 架构。每块 CPU 独占一块面积,一般都有独立风扇。

    一个 NUMA 节点包含了直连在该区域的 CPU、内存等硬件设备,通信总线一般是 PCI-E。由此也引入了 CPU 亲和性的概念,即 CPU 访问同一个 NUMA 节点上的内存的速度大于访问另一个节点的。

    执行以下命令,以查看本机的 NUMA 结构。

    numactl --harware
    # available: 2 nodes (0-1)
    # node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    # node 0 size: 127834 MB
    # node 0 free: 72415 MB
    # node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    # node 1 size: 128990 MB
    # node 1 free: 68208 MB
    # node distances:
    # node   0   1
    #   0:  10  21
    #   1:  21  10

    一个 NUMA 节点包括一个内存节点和属于同一块 CPU 的若干个逻辑核,请记住它们的编号,将在配置 cpuset 中用到。

    在此解释下“node distance”,访问本节点的内存的通信成本是常量值 10,操作系统以此基准来量化访问其他 NUMA 节点上内存的代价。

    3. 创建 cgroup 并配置资源使用

     内核版本较高(>=2.6.24)的 Linux 发行版都内置了 cgroup,可以执行以下命令验证一下。

    mount | grep cgroup
    # tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
    # cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
    # cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
    # cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
    # cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
    # cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
    # cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
    # cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
    # cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
    # cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
    # cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
    

    如果没有的话,也可以执行一段简单的脚本呢,来一次性挂载齐全。

    cgroot="${1:-/sys/fs/cgroup}"
    subsys="${2:-blkio cpu cpuacct cpuset devices freezer memory net_cls net_prio ns perf_event}"
    mount -t tmpfs cgroup_root "${cgroot}"
    for ss in $subsys; do
      mkdir -p "$cgroot/$ss"
      mount -t cgroup -o "$ss" "$ss" "$cgroot/$ss"
    done
    

    cgroup 针对每一种资源都提供了相应的控制器来进行配置,在 Linux 中以文件系统的形式呈现。本文只涉及进程在物理核上的放置,因此来看一下 cpuset 目录下有什么。

    mkdir /sys/fs/cgroup/cpuset/tiger # 创建一个控制组,删除用 rmdir 命令
    ls /sys/fs/cgroup/cpuset/tiger
    # cgroup.clone_children  cpuset.memory_pressure
    # cgroup.procs           cpuset.memory_spread_page
    # cpuset.cpu_exclusive   cpuset.memory_spread_slab
    # cpuset.cpus            cpuset.mems
    # cpuset.effective_cpus  cpuset.sched_load_balance
    # cpuset.effective_mems  cpuset.sched_relax_domain_level
    # cpuset.mem_exclusive   notify_on_release
    # cpuset.mem_hardwall    tasks
    # cpuset.memory_migrate

    如果要使用 cpuset 控制器,需要同时配置 cpuset.cpus 和 cpuset.mems 两个文件(参数)。这两个文件接受用短横线和逗号表示的区间,如“0-7,16-23”。如果对应的资源不存在,那么写入的时候会报错。

    不建议直接在控制器的根目录下配置,通过创建子目录的形式可以同时维持多个控制器。执行如下命令,限制 tiger 控制组下所有进程只能使用逻辑核0和1。

    echo "0-1" > /sys/fs/cgroup/cpuset/tiger/cpuset.cpus
    echo 0 > /sys/fs/cgroup/cpuset/tiger/cpuset.mems
    

    对于 cpuset.mems 参数而言,每个内存节点和 NUMA 节点一一对应。如果进程的内存需求量较大,可以把所有的 NUMA 节点都配置进去。这里就用到了 NUMA 的概念。出于性能的考虑,配置的逻辑核和内存节点一般属于同一个 NUMA 节点,可用“numactl --hardware”命令获知它们的映射关系。

    4. 验证效果

    在 cpuset 的所有配置文件中,tasks 和 cgroups.procs 是用来管理控制组中的进程的。执行以下命令,把当前会话加入刚刚创建的控制组里,本会话发起的所有命令(子进程)都会收到 cpu 使用的约束。

    echo $$ > /sys/fs/cgroup/cpuset/tiger/cgroup.procs # 写入当前进程编号
    

    两个配置项基本是等价的,但有一小点不同。操作系统以线程为调度单位,将一个一般的 pid 写入到 tasks 中,只有这个 pid 对应的线程,以及由它产生的其他进程、线程会属于这个控制组。而把 pid 写入 cgroups.procs,操作系统则会把找到其所属进程的所有线程,把它们统统加入到当前控制组。

    进程在加入一个控制组后,控制组所对应的限制会即时生效。启动一个计算密集型的任务,申请用 4 个逻辑核。

    stress -c 4 &
    # [1] 2958521
    # stress: info: [2958521] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
    

    观察 CPU 的使用情况来验证效果,只有编号为 0 和 1 的两个逻辑核在工作,用户态的比例高达 100%。

    top # 在列表页按数字 1 键,切换到 CPU 看板
    # Tasks: 706 total,   3 running, 702 sleeping,   0 stopped,   1 zombie
    # %Cpu0  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    # %Cpu1  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    # %Cpu2  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    # %Cpu3  :  0.0 us,  1.7 sy,  0.0 ni, 98.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
  • 相关阅读:
    ASP.NET MVC 重点教程一周年版 第二回 UrlRouting
    ASP.NET MVC 重点教程一周年版 第三回 Controller与View
    DynamicData for Asp.net Mvc留言本实例 下篇 更新
    Asp.net MVC视频教程 18 单选与复选框
    使用ASP.NET MVC Futures 中的异步Action
    ASP.NET MVC RC 升级要注意的几点
    ATL、MFC、WTL CString 的今生前世
    msvcprt.lib(MSVCP90.dll) : error LNK2005:已经在libcpmtd.lib(xmutex.obj) 中定义
    关于Windows内存的一些参考文章
    Windows访问令牌相关使用方法
  • 原文地址:https://www.cnblogs.com/shishaochen/p/9735114.html
Copyright © 2011-2022 走看看