文章目录

  • 1.Cgroups 与 Systemd之间的关系
    • (1)创建临时的 cgroup
    • (2)通过配置文件修改 cgroup
    • (3)通过 systemctl 命令修改 cgroup
  • 2.cpuset
    • (1)让系统默认情况下不使用逻辑核心 2,3 和 4
    • (2)将程序绑到特定的 CPU 核心

1.Cgroups 与 Systemd之间的关系

Linux 的 init 系统发展到 systemd 之后,systemd 与 cgroups 发生了融合(或者说 systemd 提供了 cgroups 的使用和管理接口

  • 要理解 systemd 与 cgroups 的关系,我们需要先区分 cgroups 的两个方面:层级结构(A)和资源控制(B)。
  • 首先 cgroups 是以层级结构组织并标识进程的一种方式,同时它也是在该层级结构上执行资源限制的一种方式。
  • 我们简单的把 cgroups 的层级结构称为 A,把 cgroups 的资源控制能力称为 B。
    对于 systemd 来说,A 是必须的,如果没有 A,systemd 将不能很好的工作。
    而 B 则是可选的,如果你不需要对资源进行控制,那么在编译 Linux 内核时完全可以去掉 B 相关的编译选项。

Systemd 默认挂载的 cgroups 系统

  • 在系统的开机阶段,systemd 会把支持的 controllers (subsystem 子系统)挂载到默认的 /sys/fs/cgroup/ 目录下面
  • 除了 systemd 目录外,其它目录都是对应的 subsystem。
  • /sys/fs/cgroup/systemd 目录是 systemd 维护的自己使用的非 subsystem 的 cgroups 层级结构。
    这玩意儿是systemd 自己使用的,换句话说就是,并不允许其它的程序动这个目录下的内容。
    其实 /sys/fs/cgroup/systemd 目录对应的 cgroups 层级结构就是 systemd 用来使用 cgoups 中 feature A 的。

Cgroup 的默认层级

  • 通过将 cgroup 层级系统与 systemd树绑定,systemd 可以把资源管理的设置从进程级别移至应用程序级别。因此,我们可以使用 systemctl 指令,或者通过修改 systemd unit 的配置文件来管理 unit 相关的资源。

默认情况下,systemd 会自动创建 slice、scope 和 service unit 的层级(slice、scope 和 service 都是 systemd 的 unit 类型),来为 cgroup 树提供统一的层级结构。

  • 系统中运行的所有进程,都是 systemd init 进程的子进程。在资源管控方面,systemd 提供了三种 unit 类型:

service: 一个或一组进程,由 systemd 依据 unit 配置文件启动。service 对指定进程进行封装,这样进程可以作为一个整体被启动或终止。

scope:一组外部创建的进程。由进程通过 fork() 函数启动和终止、之后被 systemd 在运行时注册的进程,scope 会将其封装。例如:用户会话、 容器和虚拟机被认为是 scope。

slice: 一组按层级排列的 unit。slice 并不包含进程,但会组建一个层级,并将 scope 和 service 都放置其中。
真正的进程包含在 scope 或 service 中。
在这一被划分层级的树中,每一个 slice 单位的名字对应通向层级中一个位置的路径。

  • 我们可以通过 systemd-cgls --no-pager命令来查看 cgroups 的层级结构

    service、scope 和 slice unit 被直接映射到 cgroup 树中的对象。
    当这些 unit 被激活时,它们会直接一一映射到由 unit 名建立的 cgroup 路径中。
    例如,cron.service 属于 system.slice,会直接映射到 cgroup system.slice/cron.service/ 中。

默认情况下,系统会创建四种 slice:

  • slice:根 slice
  • system.slice:所有系统 service 的默认位置
  • user.slice:所有用户会话的默认位置
  • machine.slice:所有虚拟机和 Linux 容器的默认位置

(1)创建临时的 cgroup

需要使用 systemd-run 命令创建临时的 cgroup,它可以创建并启动临时的 service 或 scope unit,并在此 unit 中运行程序。

  • systemd-run 命令默认创建 service 类型的 unit,比如我们创建名称为 toptest 的 service 运行 top 命令:
$ sudo systemd-run --unit=toptest --slice=test top -b

  • 然后查看一下 test.slice 的状态:

  • 再看看 toptest.service 的状态:
    创建了一个 test.slice/toptest.service cgroup 层级关系。

top 命令被包装成一个 service 运行在后台了!

  • 接下来我们就可以通过 systemctl 命令来限制 toptest.service 的资源了。
  • 在限制前让我们先来看一看 top 进程的 cgroup 信息:
$ vim /proc/30185/cgroup           # 2850 为 top 进程的 PID

限制 toptest.service 的 CPUShares 为 600,可用内存的上限为 550M:

sudo systemctl set-property toptest.service CPUShares=600 MemoryLimit=500M
  • 再次检查 top 进程的 cgroup 信息:

    在 CPU 和 memory 子系统中都出现了 toptest.service 的名字。
  • 同时去查看 /sys/fs/cgroup/memory/test.slice 和 /sys/fs/cgroup/cpu/test.slice 目录(是/sys/fs/cgroup/ cpu,cpuacct/软链接),这两个目录下都多出了一个 toptest.service 目录。
  • 我们设置的 CPUShares=600 MemoryLimit=500M 被分别写入了这些目录下的对应文件中。

临时 cgroup 的特征是,所包含的进程一旦结束,临时 cgroup 就会被自动释放。

  • 比如我们 kill 掉 top 进程,然后再查看 /sys/fs/cgroup/memory/test.slice 和 /sys/fs/cgroup/cpu/test.slice 目录,刚才的 toptest.service 目录已经不见了。

(2)通过配置文件修改 cgroup

所有被 systemd 监管的 persistent cgroup(持久的 cgroup)都在 /usr/lib/systemd/system/ 目录中有一个 unit 配置文件。

  • 比如我们常见的 service 类型 unit 的配置文件。我们可以通过设置 unit 配置文件来控制应用程序的资源,persistent cgroup 的特点是即便系统重启,相关配置也会被保留。
  • 需要注意的是,scope unit 不能以此方式创建。
  • 下面让我们为 cron.service 添加 CPU 和内存相关的一些限制,编辑 /lib/systemd/system/cron.service 文件:
$ sudo vim  /lib/systemd/system/cron.service

  • 添加红框中的行,然后重新加载配置文件并重启 cron.service:
$ sudo systemctl daemon-reload
$ sudo systemctl restart cron.service
  • 现在去查看 /sys/fs/cgroup/memory/system.slice/cron.service/memory.limit_in_bytes 和 /sys/fs/cgroup/cpu/system.slice/cron.service/cpu.shares 文件,是不是已经包含我们配置的内容了!

(3)通过 systemctl 命令修改 cgroup

除了编辑 unit 的配置文件,还可以通过 systemctl set-property 命令来修改 cgroup,这种方式修该的配置也会在重启系统时保存下来。

  • 现在我们把 cron.service 的 CPUShares 改为 700:
$ sudo systemctl set-property cron.service CPUShares=700
  • 查看 /sys/fs/cgroup/cpu/system.slice/cron.service/cpu.shares 文件的内容应该是 700,重启系统后该文件的内容还是 700。

2.cpuset

查看 CPU 配置

  • CPU 核心的编号一般是从 0 开始的,4 个核心的编号范围是 0-3。
  • 我们可以通过查看 /proc/cpuinfo 的内容来确定 CPU 的某些信息:
$ cat /proc/cpuinfo
...
processor    : 3
vendor_id    : GenuineIntel
cpu family    : 6
model        : 26
model name    : Intel(R) Xeon(R) CPU           X5650  @ 2.67GHz
stepping    : 4
microcode    : 0x1f
cpu MHz        : 2666.761
cache size    : 12288 KB
physical id    : 6
siblings    : 1
core id        : 0processor : 表示核心的编号,但这不是物理 CPU 的核心,更确切地可以称之为**逻辑核编号。
physical id : 表示当前逻辑核所在的物理 CPU 的核心,也是从 0 开始编号,这里表示这个逻辑核在第 7 个 物理 CPU 上。core id : 如果这个值大于 0,你就要注意了,你的服务器可能开启了超线程。
如果启用了超线程,每个物理 CPU 核心会模拟出 2 个线程,也叫逻辑核(和上面的逻辑核是两回事,只是名字相同而已)。
如果你想确认服务器有没有开启超线程,可以通过下面的命令查看:
$ cat /proc/cpuinfo | grep -e "core id" -e "physical id"
physical id    : 0
core id        : 0
physical id    : 2
core id        : 0
physical id    : 4
core id        : 0
physical id    : 6
core id        : 0
如果 physical id 和 core id 皆相同的 processor 出现了两次,就可以断定开启了超线程。显然我的服务器没有开启。

NUMA 架构

  • 如果主机板上插有多块 CPU,那么就是 NUMA 架构。每块 CPU 独占一块面积,一般都有独立风扇。
  • 一个 NUMA 节点包含了直连在该区域的 CPU、内存等硬件设备,通信总线一般是 PCI-E。
    由此也引入了 CPU 亲和性的概念,即 CPU 访问同一个 NUMA 节点上的内存的速度大于访问另一个节点的。
  • 查看本机的 NUMA 架构:
$ numactl --hardwareavailable: 1 nodes (0)
node 0 cpus: 0 1 2 3
node 0 size: 2047 MB
node 0 free: 1335 MB
node distances:
node   00:  10

isolcpus

  • Linux 最重要的职责之一就是调度进程,而进程只是程序运行过程的一种抽象,它会执行一系列指令,计算机会按照这些指令来完成实际工作。
  • 从硬件的角度来看,真正执行这些指令的是中央处理单元,即 CPU。
  • 默认情况下,进程调度器可能会将进程调度到任何一个 CPU 核心上,因为它要根据负载来均衡计算资源的分配。

(1)让系统默认情况下不使用逻辑核心 2,3 和 4

为了增加实验的明显效果,可以隔离某些逻辑核心,让系统默认情况下永远不会使用这些核心,除非我指定某些进程使用这些核心。

  • 要想做到这一点,就要使用到内核参数 isolcpus 了,例如:如果想让系统默认情况下不使用逻辑核心 2,3 和 4,可以将以下内容添加到内核参数列表中:
isolcpus=1,2,3
# 或者
isolcpus=1-3
  • 对于 CnetOS 7 来说,可以直接修改 /etc/default/grub:
$ cat /etc/default/grubGRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet isolcpus=1,2,3"
GRUB_DISABLE_RECOVERY="true"
  • 然后重新构建 grub.conf:
$ grub2-mkconfig -o /boot/grub2/grub.cfg
  • 重启系统之后,系统将不再使用逻辑核心 2,3 和 4,只会使用核心 1。找个程序把 CPU 跑满
    使用命令 top 查看 CPU 的使用状况:

    可以看到系统只使用了核心 1,下面我们来看看如何将程序绑到特定的 CPU 核心上。

(2)将程序绑到特定的 CPU 核心

将程序绑到指定的核心其实很简单,只需设置好 cpuset 控制器就行了。

  • systemctl 可以管理受其控制资源的 cgroup 控制器,但只能管理有限的控制器(CPU、内存和 BlockIO),不能管理 cpuset 控制器。

  • cgroup文件系统默认情况下都是挂载到 /sys/fs/cgroup 目录下的

$ ll /sys/fs/cgroup总用量 0
drwxr-xr-x 2 root root  0 3月  28 2020 blkio
lrwxrwxrwx 1 root root 11 3月  28 2020 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 3月  28 2020 cpuacct -> cpu,cpuacct
drwxr-xr-x 2 root root  0 3月  28 2020 cpu,cpuacct
drwxr-xr-x 2 root root  0 3月  28 2020 cpuset
drwxr-xr-x 4 root root  0 3月  28 2020 devices
drwxr-xr-x 2 root root  0 3月  28 2020 freezer
drwxr-xr-x 2 root root  0 3月  28 2020 hugetlb
drwxr-xr-x 2 root root  0 3月  28 2020 memory
lrwxrwxrwx 1 root root 16 3月  28 2020 net_cls -> net_cls,net_prio
drwxr-xr-x 2 root root  0 3月  28 2020 net_cls,net_prio
lrwxrwxrwx 1 root root 16 3月  28 2020 net_prio -> net_cls,net_prio
drwxr-xr-x 2 root root  0 3月  28 2020 perf_event
drwxr-xr-x 2 root root  0 3月  28 2020 pids
drwxr-xr-x 4 root root  0 3月  28 2020 systemd可以看到 cpuset 控制器已经默认被创建并挂载好了。
  • 看一下 cpuset 目录下有什么:
$ ll /sys/fs/cgroup/cpuset总用量 0
-rw-r--r-- 1 root root 0 3月  28 2020 cgroup.clone_children
--w--w--w- 1 root root 0 3月  28 2020 cgroup.event_control
-rw-r--r-- 1 root root 0 3月  28 2020 cgroup.procs
-r--r--r-- 1 root root 0 3月  28 2020 cgroup.sane_behavior
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.cpu_exclusive
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.cpus
-r--r--r-- 1 root root 0 3月  28 2020 cpuset.effective_cpus
-r--r--r-- 1 root root 0 3月  28 2020 cpuset.effective_mems
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.mem_exclusive
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.mem_hardwall
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.memory_migrate
-r--r--r-- 1 root root 0 3月  28 2020 cpuset.memory_pressure
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.memory_pressure_enabled
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.memory_spread_page
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.memory_spread_slab
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.mems
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.sched_load_balance
-rw-r--r-- 1 root root 0 3月  28 2020 cpuset.sched_relax_domain_level
-rw-r--r-- 1 root root 0 3月  28 2020 notify_on_release
-rw-r--r-- 1 root root 0 3月  28 2020 release_agent
-rw-r--r-- 1 root root 0 3月  28 2020 tasks

该目录下只有默认的配置,没有任何 cgroup 子系统。接下来我们来创建 cpuset 子系统并设置相应的绑核参数:

$ mkdir -p /sys/fs/cgroup/cpuset/test
$ echo "3" > /sys/fs/cgroup/cpuset/test/cpuset.cpus
$ echo "0" > /sys/fs/cgroup/cpuset/test/cpuset.mems首先创建了一个 cpuset 子系统叫 test,然后将核心 4 绑到该子系统,即 cpu3。
对于 cpuset.mems 参数而言,每个内存节点和 NUMA 节点一一对应。如果进程的内存需求量较大,可以把所有的 NUMA 节点都配置进去。这里就用到了 NUMA 的概念。
出于性能的考虑,配置的逻辑核和内存节点一般属于同一个 NUMA 节点,可用 numactl --hardware 命令获知它们的映射关系。
很显然,我的主机没有采用 NUMA 架构,只需将其设为节点 0 就好了。
  • 查看 test 目录:
$ cd /sys/fs/cgroup/cpuset/test
$ ll
总用量 0
-rw-rw-r-- 1 root root 0 3月  28 17:07 cgroup.clone_children
--w--w---- 1 root root 0 3月  28 17:07 cgroup.event_control
-rw-rw-r-- 1 root root 0 3月  28 17:07 cgroup.procs
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.cpu_exclusive
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.cpus
-r--r--r-- 1 root root 0 3月  28 17:07 cpuset.effective_cpus
-r--r--r-- 1 root root 0 3月  28 17:07 cpuset.effective_mems
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.mem_exclusive
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.mem_hardwall
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.memory_migrate
-r--r--r-- 1 root root 0 3月  28 17:07 cpuset.memory_pressure
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.memory_spread_page
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.memory_spread_slab
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.mems
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.sched_load_balance
-rw-rw-r-- 1 root root 0 3月  28 17:07 cpuset.sched_relax_domain_level
-rw-rw-r-- 1 root root 0 3月  28 17:07 notify_on_release
-rw-rw-r-- 1 root root 0 3月  28 17:07 tasks$ cat cpuset.cpus
3$ cat tasks
0

目前 tasks 文件是空的,也就是说,还没有进程运行在该 cpuset 子系统上。需要想办法让指定的进程运行在该子系统上,有两种方法:

  • 方法1:将已经运行的进程的 PID 写入 tasks 文件中;
$ nohup sha1sum /dev/zero &
[1] 3767然后将 PID 写入 test 目录的 tasks 中:
$ echo "3767" > /sys/fs/cgroup/cpuset/test/tasks
  • 查看 CPU 使用情况:%Cpu3应该是100%才对

  • 方法2:使用 systemd 创建一个守护进程,将 cgroup 的设置写入 service 文件中(本质上和方法 1 是一样的)。

虽然目前 systemd 不支持使用 cpuset 去指定一个 Service 的 CPU,但我们还是有一个变相的方法,Service 文件内容如下:
$ cat /etc/systemd/system/foo.service[Unit]
Description=foo
After=syslog.target network.target auditd.service[Service]
ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/cpuset/testset
ExecStartPre=/bin/bash -c '/usr/bin/echo "2" > /sys/fs/cgroup/cpuset/testset/cpuset.cpus'
ExecStartPre=/bin/bash -c '/usr/bin/echo "0" > /sys/fs/cgroup/cpuset/testset/cpuset.mems'
ExecStart=/bin/bash -c "/usr/bin/sha1sum /dev/zero"
ExecStartPost=/bin/bash -c '/usr/bin/echo $MAINPID > /sys/fs/cgroup/cpuset/testset/tasks'
ExecStopPost=/usr/bin/rmdir /sys/fs/cgroup/cpuset/testset
Restart=on-failure[Install]
WantedBy=multi-user.target
  • 启动该服务,然后查看 CPU 使用情况:

    该服务中的进程确实被调度到了 cpu2 上。

  • 参考:Linux Cgroup 入门教程:cpuset,Cgroups 与 Systemd

  • Control Groups,CPUSETS¶,Memory Resource Controller,hrtimers - subsystem for high-resolution kernel timers,CFS Bandwidth Control,Deadline Task Scheduling,CFS Scheduler,Real-Time group scheduling

  • Cgroup 官方手册中文翻译版

临时或永久修改cgroup和Cgroup 入门教程:cpuset相关推荐

  1. linux中临时、永久修改ip

    临时修改ip 在linux中输入命令:ifconfig eth0 192.168.1.1 netmask 255.255.255.0   回车 这里的192.168.1.1是临时ip,netmask是 ...

  2. 乌班图 修改ip_Ubuntu临时和永久修改ip地址掩码和网关

    在终端修改指定网卡的ip地址,有临时修改和永久修改 查看网卡信息 终端输入 ifcofig 并回车,查看需要修改的网卡名称 临时修改 当重启电脑后,ip地址将恢复为原来的ip地址,此方法适合临时测试使 ...

  3. python快速编程入门教程-python从入门到精通之30天快速学python视频教程

    python从入门到精通之30天快速学python视频教程 课程目录: python入门教程-1-Python编程语言历史及特性.mkv python入门教程-2-Python编程语言初接触.mkv ...

  4. SharePoint 2013 入门教程--系列文章

    转:http://www.cnblogs.com/jianyus/p/3381415.html 以下文章是自己在学习SharePoint的过程中,不断积累和总结的博文,现在总结一个目录,分享给大家.这 ...

  5. SharePoint 2013 入门教程

    SharePoint 2013 入门教程 以下文章是自己在学习SharePoint的过程中,不断积累和总结的博文,现在总结一个目录,分享给大家.这个博客也是自己从SharePoint入门,到一个Sha ...

  6. Linux修改hostname(临时或者永久)

    1.临时修改主机名 显示主机名: [root@RHEL6 /root]$hostname RHEL6.2 临时修改主机名:hostname 主机名 重启之后失效 2.永久修改主机名 以上的修改只是临时 ...

  7. 正确姿势临时和永久开启关闭Android的SELinux

          正确姿势临时和永久关闭Android的SELinux Android SELinux开发多场景实战指南目录: Android SELinux开发入门指南之SELinux基础知识 Andro ...

  8. ubuntu永久修改主机名

    ubuntu永久修改主机名 1.查看主机名 在Ubuntu系统中,快速查看主机名有多种方法: 其一,打开一个GNOME终端窗口,在命令提示符中可以看到主机名,主机名通常位于"@"符 ...

  9. Ubuntu系统下如何在不重启的情况下永久修改hostname主机名

    刚在Vmware下安装了一个Ubuntu的操作系统,打开终端后发现主机名这个长啊,整个窗口都被占满了. 使用hostname修改主机名,运行命令:"hostname 新主机名" # ...

最新文章

  1. 20-javamail
  2. 误差error,偏置bias,方差variance的见解
  3. 函数的作用域以及预编译
  4. android缓存框架
  5. python param_python发送带param的post请求
  6. Kotlin入门(10)七十二变的输入参数
  7. android多个单选按钮互斥使用,在GUI设计中,单选按钮用来实现用户在多个选项中的互斥选择,在同一组内多个选项中只能选择一个,当...
  8. python支持arcpy的版本_ArcPy开发IDE
  9. mysql例题_mysql 练习题
  10. 解决QFontDatabase: Cannot find font directory XXX/lib/fonts
  11. python暑假培训成都
  12. java上下载的ex表格打不开怎么回事_Java 下载 Excel文件打不开
  13. 【CF446B】 DZY Loves Modification
  14. python处理Excel实现自动化办公教学(数据筛选、公式操作、单元格拆分合并、冻结窗口、图表绘制等)【三】
  15. vscode配置Go支持
  16. 计算机显卡和cpu的关系,cpu和显卡的关系
  17. [Android] 隐藏头顶状态栏
  18. oracle数据库应用中实现汉字“同音”查询(转发)
  19. C语言入门part4—大致梳理最终篇
  20. 西南科技大学城市学院计算机考试试题,西南科技大学城市学院考试试卷高数答案.doc...

热门文章

  1. 对ARM紧致内存TCM的理解 转
  2. html 怎么做动态效果图,css如何让图片动起来?
  3. OOA/OOD/OOP细讲
  4. 局域网、广域网、Internet
  5. 2021-11-3 软件模拟IIC时序,并和EEPROM存储芯片24C02双向通信以及显示功能
  6. MATLAB系列笔记:修改编辑器颜色为护眼色(绿豆沙)
  7. c盘java文件误删_C盘误删文件如何恢复?
  8. Unity 中摄像机跟踪的两种实现
  9. AAAI 2020:北大开源算法姿态辅助多摄像机协作以进行主动对象跟踪
  10. java开源项目CrapApi接口管理系统