如何对待CPU资源?
因为过程和线程在Linux的CPU调度看来没啥区别,所以本文后续都会用过程这个名词来代表内核的调度对象,一般来讲也包含线程
如果要分配资源,咱们必须先搞清楚这个资源是如何存在的,或者说是如何组织的。我想CPU大家都不生疏,咱们都在零碎中用过各种工具查看过CPU的使用率,比如说以下这个命令和它的输入:
mpstat -P ALL 1 1
依据显示内容咱们晓得,这个计算机有4个cpu外围,目前的cpu利用率简直是0,就是说零碎整体比拟闲。
从这个例子大略能够看出,咱们对cpu资源的评估个别有两个察看角度:外围个数 和百分比。
目前的计算机根本都是多核甚至多cpu零碎,一个服务器上存在几个到几十个cpu外围的状况都很常见。所以,从这个角度看,cgroup应该提供一种伎俩,能够给过程们指定它们能够占用的cpu外围,以此来做到cpu计算资源的隔离。
百分比这个概念咱们须要多解释一下:这个百分比到底是怎么来的呢?难道每个cpu外围的计算能力就像一个带刻度表的水杯一样?一个过程要占用就会占用到它的肯定刻度么?
当然不是!这个cpu的百分比是按工夫比率计算的。基本思路是:一个CPU个别就只有两种状态,要么被占用,要么不被占用。当有多个过程要占用cpu的时候,那么操作系统在一个cpu外围上是进行分时解决的。比如说,咱们把一秒钟分成1000份,那么每一份就是1毫秒,假如当初有5个过程都要用cpu,那么咱们就让它们5个轮着应用,比方一人一毫秒,那么1秒过后,每个过程只占用了这个CPU的200ms,使用率为20%。整体cpu应用比率为100%。
同理,如果只有一个过程占用,而且它只用了300ms,那么在这一秒的尺度看来,cpu的占用工夫是30%。于是显示进去的状态就是占用30%的CPU工夫。
这就是内核是如何对待和调配计算资源的。当然理论状况要比这简单的多,然而基本思路就是这样。Linux内核是通过CPU调度器CFS--齐全偏心调度器对CPU的工夫进行调度的,因为本文的侧重点是cgroup而不是CFS,对这个题目感兴趣的同学能够到这里进一步学习。CFS是内核能够实现真对CPU资源隔离的外围伎俩,因而,了解分明CFS对了解分明CPU资源隔离会有很大的帮忙。
如何隔离CPU资源?
依据CPU资源的组织模式,咱们就能够了解cgroup是如何对CPU资源进行隔离的了。
无非也是两个思路,一个是调配外围进行隔离,另一个是调配CPU应用工夫进行隔离。
Cgroups介绍
Cgroups是linux的重要组件之一,能够对过程或用户进行隔离和限度
Cgroups全称Control Groups,是Linux内核提供的物理资源隔离机制,通过这种机制,能够实现对Linux过程或者过程组的资源限度、隔离和统计性能。比方能够通过cgroup限度特定过程的资源应用,比方应用特定数目的cpu核数和特定大小的内存,如果资源超限的状况下,会被暂停或者杀掉。
cgroups外围概念
工作(task)
在cgroup中,工作就是一个过程。
控制组(control group)
cgroup的资源管制是以控制组的形式实现,控制组指明了资源的配额限度。过程能够退出到某个控制组,也能够迁徙到另一个控制组。
层级(hierarchy)
控制组有层级关系,相似树的构造,子节点的控制组继承父控制组的属性(资源配额、限度等)。
子系统(subsystem)
一个子系统其实就是一种资源的控制器,比方memory子系统能够管制过程内存的应用。子系统须要退出到某个层级,而后该层级的所有控制组,均受到这个子系统的管制
cgroups进行CPU限度
咱们的机器自带cgproups,能够应用命令验证mount -t cgroup
cgroup裸露给用户的API为文件系统,所有对cgroup的操作均能够通过对文件的批改实现,cgroup API对应的门路为:/sys/fs/cgroup/,作为应用方,仅须要对文件系统中的内容进行编辑,即可达到配置对应的cgroup的要求。
创立cgroup
cd /sys/fs/cgroup/cpumkdir test
创立文件夹后,cgroup会主动在该文件夹下初始化出配置文件:
其中,须要关注的文件有3个,别离为:
cgroup的限度逻辑如下:
1 限度所有pid在tasks中的过程,
2 在 cpu.cfs_period_us 周期内,只能应用最多 cpu.cfs_quota_us 的cpu资源。
3 默认状况下,cpu.cfs_period_us的单位为微秒,默认值为100ms。cpu.cfs_quota_us的值为-1,暨不做限度。
4 例如: 限度在100ms中,只能应用30ms的cpu资源,暨限度cpu占用率为30%
echo 30000 > cpu.cfs_quota_us
5 启动测试程序,并增加pid到tasks文件中后,再察看CPU状况,能够清晰的看到被限度在了30%
echo pid(loglistener的过程号) > /sys/fs/cgroup/cpu/rocket/test
应用cgroups的go客户端
这是一个应用Golang封装的用来操作cgroups的工具包,反对创立、治理、检查和销毁cgroups。应用go提供的客户端,能够在服务器上提供一个守护过程,由守护过程接管申请后,进行cgroup治理。相干的外围代码如下:
package mainimport ( "flag" "github.com/containerd/cgroups" "github.com/opencontainers/runtime-spec/specs-go" "log" "os/exec" "strings" "time")var kb = 1024var mb = 1024 * kb// main// call some process and add this process into cgroupfunc main() { var cgPath = flag.String("cgroup_path", "", "cg-path is cgroup path name") var period = flag.Uint64("cpu_period", 100000, "cpu limit value, default is 100% ") var quota = flag.Int64("cpu_quota", -1, "cpu limit value, default is 100% ") var memLimit = flag.Int("mem_limit", 100, "mem limit value, default is 100mb ") var cmd = flag.String("cmd", "", "your application cmd") var args = flag.String("args", "", "cmd args") flag.Parse() cpuLimit := float32(*quota) / float32(*period) * 100 limit := int64(*memLimit * mb) log.Printf("cgroup_path: %s, cpu_quota: %v, cpu_period: %v,max (%v%%), mem_limit: %vm (%d), cmd: %s, args: %v \n", *cgPath, *quota, *period, cpuLimit, *memLimit, limit, *cmd, *args) control, err := cgroups.New(cgroups.V1, cgroups.StaticPath(*cgPath), &specs.LinuxResources{ CPU: &specs.LinuxCPU{ Quota: quota, Period: period, }, Memory: &specs.LinuxMemory{ Limit: &limit, }, }) if err != nil { log.Fatal(err) return } defer control.Delete() pid := run(*cmd, strings.Split(*args, " ")...) log.Printf("run process done, pid: %v, add to cgroup task\n", pid) if err = control.AddTask(cgroups.Process{Pid: pid}); err != nil { log.Fatal(err) return } tasks, err := control.Tasks(cgroups.Freezer, false) if err != nil { log.Fatal(err) } log.Printf("Current tasks: %v", tasks) time.Sleep(10 * time.Second)}// run cmd in background, and return pidfunc run(cmd string, args ...string) int { log.Printf("[run], cmd:%s, args: %v", cmd, args) command := exec.Command(cmd, args...) err := command.Start() if err != nil { log.Fatalf("Start error, %v", err) return 0 } for { if command.Process != nil { return command.Process.Pid } time.Sleep(1000 * time.Microsecond) }}
go run main.go -cgroup_path test -cmd /root/pi/main -cpu_quota 300000 -cpu_period 1000000 2022/08/08 12:21:23 cgroup_path: test, cpu_quota: 300000, cpu_period: 1000000,max (30.000002%), mem_limit: 100m (104857600), cmd: /root/pi/main, args: 2022/08/08 12:21:23 [run], cmd:/root/pi/main, args: []2022/08/08 12:21:23 run process done, pid: 11855, add to cgroup task2022/08/08 12:21:23 Current tasks: [{freezer 11855 /sys/fs/cgroup/freezer/test/}]