为了提升系统的整体效率,需要在不同的场景中控制进程的cpu使用率。控制进程cpu的使用率,实质上是控制进程在单位时间内的CPU时间片。常用的手段有利用进程优先级,信号,cgroup。
目前,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用于控制实时进程的调度属性:调度策略,优先级等。例如:修改pid为111的进程的调度策略为SCHED_FIFO, 并设置优先级为10。
chrt -p -f 10 111
nice和renice用控制普通进程的优先级,nice值的范围为-20到19,nice数值越低,优先级越高。
例如:进程启动时设置进程的nice值为19。
nice -n 19 vi &
信号是进程间异步通信机制,信号被发送到进程时,操作系统会中断进程的正常流程,并且进入相应的信号处理函数执行操作,完成后在回到中断的地方继续执行。
信号的接收
信号的接收由内核完成,内核接收到信号后,会将信号放到对应进程的信号队列中,同时向进程发送中断,进程陷入内核。此时信号还在信号队列中,对进程来说,还不知道信号的到来。
信号的检测
进程陷入内核状态后,在以下两种情景中会对信号进行检测:进程从内核态返回到用户态前;进程在内核态中从睡眠状态被唤醒的时候。
信号的处理
信号处理函数运行在用户态,内核会将当前内核栈的内容拷贝到用户栈上,接着返回用户态,执行相应的信号处理函数。执行完信号处理函数后,返回内核态,检查是否还有未处理的信号。
cpulimit通过周期发送SIGSTOP和SIGCONT来使进程不断的暂停和激活,从而控制CPU使用率。例如:限制pid为123的进程cpu使用率为50%。
cpulimit --pid 123 --limit 50
cgroups主要功能有资源限制,优先级分配,资源统计,进程控制。cgroups的api以一个伪文件系统实现,用户可以通过文件系统实现cgroup的管理。cgroups的组织管理单元可以细粒度到线程级别。所有资源管理功能以subsystem方式实现。
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子系统永远无法绑定到两个不同层级。但如果新建的层级要绑定的子系统与现有的层级结构相同,那么新的挂载会重用原来的层级。
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是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 中。默
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
...
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 - -
...
临时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配置文件位于 /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