限制CPU使用率:优先级,信号,cgroups,systemd

为了提升系统的整体效率,需要在不同的场景中控制进程的cpu使用率。控制进程cpu的使用率,实质上是控制进程在单位时间内的CPU时间片。常用的手段有利用进程优先级,信号,cgroup。

优先级

进程优先级、nice和权重之间的关系

目前,Linux内核默认实现了4种调度器,分别是deadline, realtime, CFS和idel。进程能够获得多少CPU时间,和调度策略紧密相关。Linux支持的调度策略有:

[include/uapi/linux/sched.h]
/*
 * Scheduling policies
 */
#define SCHED_NORMAL		0
#define SCHED_FIFO		1
#define SCHED_RR		2
#define SCHED_BATCH		3
/* SCHED_ISO: reserved but not implemented yet */
#define SCHED_IDLE		5
#define SCHED_DEADLINE		6

其中,SCHED_NORMAL和SCHED_BATCH使用CFS调度器,SCHED_FIFO和SCHED_RR使用realtime调度器,SCHED_IDLE 指的是idel调度。内核使用0–139的数值表示进程的优先级,数值越低优先级越高。优先级0–99给实时进程使用,100–139给普通进程使用。普通进程的优先级,即100–139,被用户空间用nice值来映射。nice值的范围从-20–19,nice值默认为0.nice值的含义类似于等级,nice值越高,则优先级越低。例如CPU密集型应用程序nice值从0增加到1,那么它相对于其他nice值为0的应用程序将减少10%的CPU时间。CFS调度器引入虚拟时钟概念,每个进程虚拟运行时间是实际运行时间相对于nice值为0的权重的比例值。进程按照各自不同的速度在物理时钟节拍内前进。nice值小的进程,优先级权重大,虚拟时钟比正式时钟跑得慢,但是可以获取更多的运行时间。反之,nice值越大的进程,优先级越低,权重也越低,虚拟时钟比真实时钟跑得快,反而获得比较少的运行实际。CFS调度器总是选择虚拟时钟跑得慢的进程。

虚拟运行时间的计算公式为:

vruntime = delta_exec * nice_0_weight / weight

vruntiem: 进程虚拟运行时间
delta_exec:实际和运行时间
nice_0_weight:表示进程的权重值

工具

  • chrt

chrt用于控制实时进程的调度属性:调度策略,优先级等。例如:修改pid为111的进程的调度策略为SCHED_FIFO, 并设置优先级为10。

chrt -p -f 10 111
  • nice和renice

nice和renice用控制普通进程的优先级,nice值的范围为-20到19,nice数值越低,优先级越高。
例如:进程启动时设置进程的nice值为19。

nice -n 19 vi &

信号

信号原理

信号是进程间异步通信机制,信号被发送到进程时,操作系统会中断进程的正常流程,并且进入相应的信号处理函数执行操作,完成后在回到中断的地方继续执行。

  • 信号的接收
    信号的接收由内核完成,内核接收到信号后,会将信号放到对应进程的信号队列中,同时向进程发送中断,进程陷入内核。此时信号还在信号队列中,对进程来说,还不知道信号的到来。

  • 信号的检测
    进程陷入内核状态后,在以下两种情景中会对信号进行检测:进程从内核态返回到用户态前;进程在内核态中从睡眠状态被唤醒的时候。

  • 信号的处理
    信号处理函数运行在用户态,内核会将当前内核栈的内容拷贝到用户栈上,接着返回用户态,执行相应的信号处理函数。执行完信号处理函数后,返回内核态,检查是否还有未处理的信号。

工具

  • cpulimit

cpulimit通过周期发送SIGSTOP和SIGCONT来使进程不断的暂停和激活,从而控制CPU使用率。例如:限制pid为123的进程cpu使用率为50%。

 cpulimit --pid 123 --limit 50  

cgroups

cgroups主要功能有资源限制,优先级分配,资源统计,进程控制。cgroups的api以一个伪文件系统实现,用户可以通过文件系统实现cgroup的管理。cgroups的组织管理单元可以细粒度到线程级别。所有资源管理功能以subsystem方式实现。

cgroups的术语

  • task(任务),表示系统的一个进程
  • cgroup(控制组),资源控制的基本单位,按某种资源控制标准划分而成的任务组。
  • subsystem(子系统),资源调度器,如cpu子系统可以控制cpu时间分配。
  • hierarchy(层级树),由一系列cgroup以树状结构排列而成,每个层级树通过绑定对应的子系统进行资源调度。

libcgroup

libcgroup Man Page

man 1 cgclassify – cgclassify 命​​​令​​​是​​​用​​​来​​​将​​​运​​​行​​​的​​​任​​​务​​​移​​​动​​​到​​​一​​​个​​​或​​​者​​​多​​​个​​​ cgroup。​​​
man 1 cgclear – cgclear 命​​​令​​​是​​​用​​​来​​​删​​​除​​​层​​​级​​​中​​​的​​​所​​​有​​​ cgroup。 ​​​
man 5 cgconfig.conf – 在​​​ cgconfig.conf 文​​​件​​​中​​​定​​​义​​​ cgroup。​​​
man 8 cgconfigparser – cgconfigparser 命​​​令​​​解​​​析​​​ cgconfig.conf 文​​​件​​​和​​​并​​​挂​​​载​​​层​​​级​​​。​​​
man 1 cgcreate – cgcreate 在​​​层​​​级​​​中​​​创​​​建​​​新​​​ cgroup。​​​
man 1 cgdelete – cgdelete 命​​​令​​​删​​​除​​​指​​​定​​​的​​​ cgroup。​​​
man 1 cgexec – cgexec 命​​​令​​​在​​​指​​​定​​​的​​​ cgroup 中​​​运​​​行​​​任​​​务​​​。​​​
man 1 cgget – cgget 命​​​令​​​显​​​示​​​ cgroup 参​​​数​​​。​​​
man 5 cgred.conf – cgred.conf 是​​​ cgred 服​​​务​​​的​​​配​​​置​​​文​​​件​​​。​​​
man 5 cgrules.conf – cgrules.conf 包​​​含​​​用​​​来​​​决​​​定​​​何​​​时​​​任​​​务​​​术​​​语​​​某​​​些​​​ cgroup 的​​​规​​​则​​​。​​​
man 8 cgrulesengd – cgrulesengd 在​​​ cgroup 中​​​发​​​布​​​任​​​务​​​。​​​
man 1 cgset – cgset 命​​​令​​​为​​​ cgroup 设​​​定​​​参​​​数​​​。​​​
man 1 lscgroup – lscgroup 命​​​令​​​列​​​出​​​层​​​级​​​中​​​的​​​ cgroup。​​​
man 1 lssubsys – lssubsys 命​​​令​​​列​​​出​​​包​​​含​​​指​​​定​​​子​​​系​​​统​​​的​​​层​​​级​​。

限制cpu使用率为50%

  • 创建层级和挂载子系统

任何单一子系统最多可绑定到一个层级中,如果要绑定的子系统已经被别的层级绑定,则会报错。例如,cpu子系统永远无法绑定到两个不同层级。但如果新建的层级要绑定的子系统与现有的层级结构相同,那么新的挂载会重用原来的层级。

mount -t cgroup -o cpu,cpuacct  cpu,cpuacct  /sys/fs/cgroup/cpu,cpuacct
  • 创建控制组群
cgcreate -g cpu:/cg_half
  • 设置参数

设置50%的CPU使用率,cpu.cfs_quota_us / cpu.cfs_period_us

cgset -r cpu.cfs_quota_us=50000 cg_half
  • 将进程移到控制组群中
cgclassify -g cpu:cg_half 2128,2129

systemd

systemd是Linux的init系统, systemd引入了units的概念。systemd依赖cgroups, systemd主要通过unit和cgroups管理资源。可以使用systemctl指令,或者通过修改systemd的unit配置文件来管理系统资源。系统中所有进程都是systemd init进程的子进程,默认情况下systemd会创建slice,scope和service层级,

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

  • scope, 一组外部创建的进程,任何通过fork()启动和终止,并且在运行时注册到systemd的进程,scope可以将其封装。例如:用户会话、容器和虚拟机。

  • slice, 一组按层级排列的单位。slice并不包含进程,但会组建一个层级,并将scope和 service 都放置其中。
    真正的进程包含在 scope 或 service 中。默

systemctl

  • systemd-cgls, 查看cgroups层级
root@localhost:~# systemd-cgls
Control group /:
-.slice
├─init.scope
│ └─1 /sbin/init
├─system.slice
│ ├─dbus.service
│ │ └─617 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
...

  • systemd-cgtop, 显示cgoups的实时资源消耗情况
Control Group                            Tasks   %CPU   Memory  Input/s Output/s
/                                            -    2.1   975.1M        -        -
/system.slice                              112    1.4   714.6M        -        -
/system.slice/aegis.service                 28    1.1    84.8M        -        -
/user.slice                                  5    0.7   137.2M        -        -
/user.slice/user-0.slice                     5    0.7    49.8M        -        -
...
  • systemd-run, 创建临时cgroup

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

systemd-run --unit=name --scope --slice=slice_name command

root@localhost:~# systemd-run --unit=toptest --slice=test top -b
Running as unit toptest.service.

unit配置文件

unit配置文件位于 /usr/lib/systemd/system/ 目录。如:控制cpu使用率为120%,使用CPUQuota参数。

[Unit]
Description=miner
After=network.target

[Service]
User=miner
Group=miner

Type=simple
ExecStart=/opt/cpuminer-opt/cpuminer
ExecStop=/usr/bin/pkill cpuminer


CPUQuota=120%

PrivateTmp=true
TimeoutStopSec=60s
TimeoutStartSec=2s
StartLimitInterval=120s

[Install]
WantedBy=multi-user.target

结论

我们可以通过进程优先级,信号,以及cgroups等方式来控制cpu的使用率,相应的工具有nice, cpulimit, cgset等等。用cgroups才能实现对系统资源的精确控制,但现在libcgropup不被推荐使用,因为它很容易与systemd默认的cgroup层级产生冲突。所以要控制cpu使用率更优雅的方式是使用systemd,systemd被大多数主流系统支持。

参考

https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/resource_management_guide/chap-introduction_to_control_groups
https://access.redhat.com/articles/754933

你可能感兴趣的:(限制CPU使用率:优先级,信号,cgroups,systemd)