手写docker—资源限制(三)

简介

上一节中我们完成了 Mydocker run -it /bin/sh 的方式创建并启动容器。本节我们通过 cgroups 来实现对容器资源的限制。通过增加 -mem-cpu 等命令行参数来限制资源,完整命令比如 Mydocker run -it -mem 10m -cpu 20 /bin/sh

核心需要实现以下逻辑:

  • 新增 -mem-cpu 命令行参数解析;
  • 实现统一的 CgroupsManager
  • 实现各个 subSystem
  • 容器创建、停止时调用对应方法配置 cgroup

资源限制原理——CPU

在 cgroup 中,与 CPU 相关的子系统有 cpusetscpuacctcpu

  • cpusets:主要用于配置 CPU 核的运行情况,可以限制 cgroup 中的进程只能在指定的 CPU 上运行,或者不能在指定的 CPU 上运行。
  • cpuacct:包括当前 cgroup 所使用的 CPU 的统计信息;
  • cpu:限制进程对 CPU 的使用率。

Cgroup 及其配置信息

在当前进程下创建 cgroup 目录,可以发现目录中自己初始化创建了一些文件:

#进入/sys/fs/cgroup/cpu并创建子cgroup
root@DESKTOP-9K4GB6E:/sys/fs/cgroup/cpu# cd /sys/fs/cgroup/cpu
root@DESKTOP-9K4GB6E:/sys/fs/cgroup/cpu# mkdir test
root@DESKTOP-9K4GB6E:/sys/fs/cgroup/cpu# cd test/
root@DESKTOP-9K4GB6E:/sys/fs/cgroup/cpu/test# ls
cgroup.clone_children  cpu.cfs_period_us  cpu.rt_period_us   cpu.shares  notify_on_release
cgroup.procs           cpu.cfs_quota_us   cpu.rt_runtime_us  cpu.stat    tasks

Cgroup 关于 CPU 资源限制配置:

  • cpu_cfs_period_us:配置 CPU 时间周期长度;
  • cpu_cfs_quota_us:配置 cgroup 在设置的时间周期内能够使用的 CPU时间数,单位 ms;
  • cpu.shares:指定各个 cgroup 可以使用的 CPU 时间片比例,默认为 1024;
    • 假如系统中有两个 cgroup,分别是 A 和 B,A 的 shares 值是 1024,B 的 shares 值是512,那么 A 将获得 1024/(1204+512)=66% 的 CPU 资源,而 B 将获得 33% 的 CPU 资源。
  • cpu.stat:记录当前 cgroup 使用的 CPU时间片信息;
    • nr_periods:已经使用的 CPU 时间片数;
    • nr_throttled:CPU 使用受限次数;
    • throttled_time:CPU 被限制使用了多少秒。

CFS(Completely Fair Scheduler)是 Linux 内核中的调度器,决定哪个进程在给定的 CPU 时间片中运行。

CFS 通过协调 Cgroups 实现协同工作,它负责追踪每个 Cgroup 中进程消耗的 CPU 时间,并且在每个调度周期结束时根据 Cgroup 中的 CPU 配额调整进程使用 CPU 的时间。

总的来说,cgroupssubsystem 负责提供配置,cfs 负责记录进程使用的 CPU 时间,当检测到进程使用时间达到阈值时从调度层面对当前进程资源进行调整限制。

资源限制原理——Memory

内存控制的必要性

  • 普通开发者角度,内存控制能够限制一组进程中所使用的内存数,即使代码运行出现问题(例如内存泄漏等问题)导致内存资源耗尽,那么进程会重启运行。
  • 系统管理角度,内存控制能够限制每组进程使用的内存量,不管程序质量如何,都能够将对系统的影响降低到最低。

如何进行内存控制

内核控制主要控制如下方面:

  • 限制 Cgroup 中所有进程使用的物理内存总量;
  • 限制 Cgroup 中所有进程使用的物理内存总量+交换空间总量(Swap空间);
  • 限制 Cgroup 中所有进程使用的物理内存总量及一些其它资源,比如进程内核栈空间、socket所占用的内存空间,通过限制内核内存,在内存吃紧时组织新进程的创建。

内存限制主要通过修改 cgroup 中下列文件实现:

cgroup.event_control       #用于eventfd的接口
memory.usage_in_bytes      #显示当前已用的内存
memory.limit_in_bytes      #设置/显示当前限制的内存额度
memory.failcnt             #显示内存使用量达到限制值的次数
memory.max_usage_in_bytes  #历史内存最大使用量
memory.soft_limit_in_bytes #设置/显示当前限制的内存软额度
memory.stat                #显示当前cgroup的内存使用情况
memory.use_hierarchy       #设置/显示是否将子cgroup的内存使用情况统计到当前cgroup里面
memory.force_empty         #触发系统立即尽可能的回收当前cgroup中可以回收的内存
memory.pressure_level      #设置内存压力的通知事件,配合cgroup.event_control一起使用
memory.swappiness          #设置和显示当前的swappiness
memory.move_charge_at_immigrate #设置当进程移动到其他cgroup中时,它所占用的内存是否也随着移动过去
memory.oom_control         #设置/显示oom controls相关的配置
memory.numa_stat           #显示numa相关的内存

内存限制实例

举个例子:

# 修改内存资源限制
echo 1M > memory.limit_in_bytes:限制当前进程组只使用 1M 内存;
echo -1 > memory.limit_in_bytes:不限制进程组内存使用量;
# 添加当前进程到 Cgroups
cd /sys/fs/cgroup/memory/test
echo $$ >> cgroup.procs

如果设置的内存限额比较小,当进程使用内存达到阈值后会怎么样呢,这里实践操作下:

# 启动一个容器(/bin/sh)进程
[root@localhost mydocker]# ./mydocker run -it -mem 10m /bin/sh

# 查看进程内存资源限制,符合配置 10M 内存
[root@localhost goproject]# cat /sys/fs/cgroup/memory/mydocker-cgroup/memory.limit_in_bytes 
10485760

#####################
# 进入容器进程内部,编译运行以下 c 程序,该程序每秒申请 1M 内存
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define MB (1024*1024)

#int main(int argc, char* argv[]) {
#    char *p;
#    int i = 0;
#    while (1) {
#        p = (char*) malloc(MB);
#        memset(p, 0, MB);
#        printf("%dM memory allocated\n", ++i);
#        sleep(1);
#    }
#    return 0;
#}
#####################

# 查看内存使用量达到限制的次数,初始为0
[root@localhost goproject]# cat /sys/fs/cgroup/memory/mydocker-cgroup/memory.failcnt 
0

# 查看进程已经使用的内存空间
[root@localhost goproject]# cat /sys/fs/cgroup/memory/mydocker-cgroup/memory.usage_in_bytes 
495616

当 c 程序运行一段时间后,查看 /sys/fs/cgroup/memory/mydocker-cgroup/memory.failcnt

# 查看进程已经使用的内存空间
sh-4.2# ./mem_allocate 
1M memory allocated
2M memory allocated
3M memory allocated
4M memory allocated
5M memory allocated
6M memory allocated
7M memory allocated
8M memory allocated
9M memory allocated
10M memory allocated
11M memory allocated
12M memory allocated
13M memory allocated

[root@localhost goproject]# cat /sys/fs/cgroup/memory/mydocker-cgroup/memory.failcnt 
0
[root@localhost goproject]# cat /sys/fs/cgroup/memory/mydocker-cgroup/memory.failcnt 
74
[root@localhost goproject]# cat /sys/fs/cgroup/memory/mydocker-cgroup/memory.failcnt 
74
[root@localhost goproject]# cat /sys/fs/cgroup/memory/mydocker-cgroup/memory.failcnt 
175
[root@localhost goproject]# cat /sys/fs/cgroup/memory/mydocker-cgroup/memory.failcnt 
276

可以发现内存申请超过 10M 时达到阈值开始限制,但是进程并没有停止,再查看系统 swap 空间使用情况:

[root@localhost goproject]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.7G        615M         11M        1.4G        1.7G
Swap:          1.0G        768K        1.0G
[root@localhost goproject]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.7G        614M         11M        1.4G        1.7G
Swap:          1.0G        2.0M        1.0G
[root@localhost goproject]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.7G        613M         11M        1.4G        1.7G
Swap:          1.0G        3.2M        1.0G
[root@localhost goproject]# free -h
              total        used        free      shared  buff/cache   available
Mem:           3.7G        1.7G        614M         11M        1.4G        1.7G
Swap:          1.0G        4.0M        1.0G

从上面的结果可以看出,当物理内存不够时,就会触发 memory.failcnt 里面的数量加 1,但进程不会被 kill 掉,那是因为内核会尝试将物理内存中的数据移动到 swap 空间中,从而让内存分配成功。

新增命令行参数解析

新增关于内存 -mem xxxm、CPU -cpu xxx 的参数解析函数:

runCommand 解析中新增如下 Flag 变量:

cli.StringFlag{
  Name:  "mem", // 进程内存限制
  Usage: "memory limit",
},
cli.StringFlag{
  Name:  "cpushare", //
  Usage: "cpushare limit",
},
cli.StringFlag{
  Name:  "cpu", // CPU使用率限制
  Usage: "cpu quota,e.g.: -cpu 100",
},
cli.StringFlag{
  Name:  "cpuset", // 进程CPU限制
  Usage: "cpuset limit",
},

对应 Action 解析函数中新增 Cgroup 配置,包括 cpucpusetmem

Action: func(context *cli.Context) error {
  if len(context.Args()) < 1 {
    return fmt.Errorf("missing container command")
  }
  // collect all userCommands
  var cmdArray []string
  for _, arg := range context.Args() {
    cmdArray = append(cmdArray, arg)
  }
  tty := context.Bool("it")
  containerName := context.String("name")
  envSlice := context.StringSlice("envSlice")
  imageName := cmdArray[0]
  // init resourceConfig
  resConfig := &subsystems.ResourceConfig{
    MemoryLimit: context.String("mem"),
    CpuCfsQuota: context.Int("cpu"),
    CpuSet:      context.String("cpuset"),
  }
  // start container process
  Run(tty, cmdArray, resConfig, containerName, imageName, envSlice)
  return nil
}

SubSystem 实现

顶层接口定义

定义 Subsystem 子系统向上抽象出一个子系统管理接口,用于统一配置不同子系统(mem、cpu、cpuset):

/**
 * 传递资源限制配置结构体,包括内存限制、CPU使用限制、CPU核心数限制
 */
type ResourceConfig struct {
	MemoryLimit string
	CpuCfsQuota int
	CpuShare    string  // 不同 hierarchy 间CPU资源分配比例
	CpuSet      string  // 指定进程使用哪个CPU核
}

/**
 * subsystem 参数配置接口
 * for Example:mydocker run -it -m 100m -cpuset 1 -cpushare 512 /bin/sh
 *
 * hierarchy:cgroup树结构,并通过虚拟文件系统的方式暴露给用户;
 * cgroup:cgroup树中的节点,用于控制节点中进程的资源占用;
 * subsystem:作用于 hierarchy 中的 cgroup节点,控制节点中进程的资源占用;
 */
type Subsystem interface {
	// 子系统配置名称(cpu/memory/cpuset)
	Name() string
	// 添加 Subsystem 到 Cgroup 节点
	Set(cgroupPath string, conf *ResourceConfig) error
	// 添加对进程的 subsystem 限制
	Apply(cgroupPath string, pid int, conf *ResourceConfig) error
	// 移除指定路径的 Cgroup
	Remove(cgroupPath string) error
}

接口具体实现

cpu资源限制配置

  • /fs/sys/cgroup/xxx/cpu.cfs_period_us:所有进程使用总的CPU时间片;
  • /fs/sys/cgroup/xxx/cpu.cfs_quota_us:当前进程使用的CPU时间片阈值;
  • /fs/sys/cgroup/xxx/cpu.shares:不同 Cgroup 间使用CPU时间片的比例;
/**
 *  进程 CPU 资源限额配置,主要修改以下配置文件:
 *  1.cpu.cfs_period_us;
 *  2.cpu.cfs_quota_us
 */
const (
	CPU_PERIOD_CONTROL_FILENAME = "cpu.cfs_period_us"
	CPU_QUOTA_CONTROL_FILENAME  = "cpu.cfs_quota_us"
	CPU_SHARES_CONTROL_FILENAME = "cpu.shares"
	CPU_DEFAULT_PERIOD          = 100000
	CPU_DEFAULT_PERCENT         = 100
)

type CpuSubsystem struct {
}

func (c *CpuSubsystem) Name() string {
	return "cpu"
}

func (c *CpuSubsystem) Set(cgroupPath string, conf *ResourceConfig) error {
	if conf.CpuCfsQuota == 0 && conf.CpuShare == "" {
		return nil
	}
	subsysCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", c.Name()), err)
	}
	// cpu.shares 控制 CPU 的使用比例
	if conf.CpuShare != "" {
		if err := ioutil.WriteFile(path.Join(subsysCgroupPath, CPU_SHARES_CONTROL_FILENAME), []byte(conf.CpuShare), container.Perm0644); err != nil {
			return meta.NewError(meta.NewErrorCode(meta.ErrWrite, meta.CGROUPS), fmt.Sprintf("set cgroup cpu.shares failed %s", "cpushares"), err)
		}
	}
	// cpu.cfs_period_us、cpu.cfs_quota_us 控制 CPU 的使用时间
	if conf.CpuCfsQuota != 0 {
		// 配置总的 CPU 总时间
		if err = ioutil.WriteFile(path.Join(subsysCgroupPath, CPU_PERIOD_CONTROL_FILENAME), []byte(strconv.Itoa(CPU_DEFAULT_PERIOD)), container.Perm0644); err != nil {
			return meta.NewError(meta.NewErrorCode(meta.ErrWrite, meta.CGROUPS), fmt.Sprintf("set cgroup cpu.cfs_period_us failed %s", "cpushares"), err)
		}
		// 配置进程可以使用的时间片长度
		if err = ioutil.WriteFile(path.Join(subsysCgroupPath, CPU_QUOTA_CONTROL_FILENAME), []byte(strconv.Itoa(CPU_DEFAULT_PERIOD/CPU_DEFAULT_PERCENT*conf.CpuCfsQuota)), container.Perm0644); err != nil {
			return meta.NewError(meta.NewErrorCode(meta.ErrWrite, meta.CGROUPS), fmt.Sprintf("set cgroup cpu.cfs_quota_us failed %s", "cpuquota"), err)
		}
	}
	return nil
}

func (c *CpuSubsystem) Apply(cgroupPath string, pid int, conf *ResourceConfig) error {
	if conf.CpuCfsQuota == 0 && conf.CpuShare == "" {
		return nil
	}
	subsysCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", c.Name()), err)
	}
	if err := ioutil.WriteFile(path.Join(subsysCgroupPath, "tasks"), []byte(strconv.Itoa(pid)), container.Perm0644); err != nil {
		return meta.NewError(meta.ErrWrite, fmt.Sprintf("set cgroup proc failed %v", err), err)
	}
	return nil
}

// 移除某个 cgroup
func (c *CpuSubsystem) Remove(cgroupPath string) error {
	subsysCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", c.Name()), err)
	}
	if err := os.RemoveAll(subsysCgroupPath); err != nil {
		return err
	}
	return nil
}

cpu核使用配置

  • cpuset.cpus:进程使用哪个CPU核。
/**
 *  进程 CPU 资源配置:
 */
const CPU_APPLY_CONTROL_FILENAME = "cpuset.cpus"

type CpusetSubsystem struct {
}

func (c *CpusetSubsystem) Name() string {
	return "cpuset"
}

func (c *CpusetSubsystem) Set(cgroupPath string, conf *ResourceConfig) error {
	if conf.CpuSet == "" {
		return nil
	}
	subsysCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", c.Name()), err)
	}
	if err := ioutil.WriteFile(path.Join(subsysCgroupPath, CPU_APPLY_CONTROL_FILENAME), []byte(conf.CpuSet), container.Perm0644); err != nil {
		return meta.NewError(meta.ErrWrite, fmt.Sprintf("set cgroup cpuset failed %v", err), err)
	}
	return nil
}

func (c *CpusetSubsystem) Apply(cgroupPath string, pid int, conf *ResourceConfig) error {
	if conf.CpuSet == "" {
		return nil
	}
	subsysCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", c.Name()), err)
	}
	if err := ioutil.WriteFile(path.Join(subsysCgroupPath, "tasks"), []byte(strconv.Itoa(pid)), container.Perm0644); err != nil {
		return meta.NewError(meta.ErrWrite, fmt.Sprintf("set cgroup cpuset failed %v", err), err)
	}
	return nil
}

// 移除某个 cgroup
func (c *CpusetSubsystem) Remove(cgroupPath string) error {
	subsysCgroupPath, err := getCgroupPath(c.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", c.Name()), err)
	}
	if err := os.RemoveAll(subsysCgroupPath); err != nil {
		return err
	}
	return nil
}

memory资源限制

  • /fs/sys/cgroup/memory/xxx/memory.limit_in_bytes:限制进程的内存资源;
/**
 * 进程 memory 资源配置
 * 1.向 cgroup/memory.limit_in_bytes 文件中写入指定内存资源限制值;
 * 2.添加某个进程到 cgroup 中,也就是往对应的 tasks 文件中写入 pid;
 * 3.删除 cgroup 目录;
 */
const MEMORY_CONTROL_FILENAME = "memory.limit_in_bytes"

type MemorySubsystem struct {
}

func (m *MemorySubsystem) Name() string {
	return "memory"
}

func (m *MemorySubsystem) Set(cgroupPath string, conf *ResourceConfig) error {
	if conf.MemoryLimit == "" {
		return nil
	}
	subsysCgroupPath, err := getCgroupPath(m.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", m.Name()), err)
	}
	if err := ioutil.WriteFile(path.Join(subsysCgroupPath, MEMORY_CONTROL_FILENAME), []byte(conf.MemoryLimit), container.Perm0644); err != nil {
		return meta.NewError(meta.ErrWrite, fmt.Sprintf("set cgroup memory failed %v", err), err)
	}
	log.Infof("set cgroup memory for %s values %v", m.Name(), conf.MemoryLimit)
	return nil
}

func (m *MemorySubsystem) Apply(cgroupPath string, pid int, conf *ResourceConfig) error {
	if conf.MemoryLimit == "" {
		return nil
	}
	subsysCgroupPath, err := getCgroupPath(m.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", m.Name()), err)
	}
	if err := ioutil.WriteFile(path.Join(subsysCgroupPath, "tasks"), []byte(strconv.Itoa(pid)), container.Perm0644); err != nil {
		return meta.NewError(meta.ErrWrite, fmt.Sprintf("set cgroup memory failed %v", err), err)
	}
	return nil
}

// 移除某个 cgroup
func (m *MemorySubsystem) Remove(cgroupPath string) error {
	subsysCgroupPath, err := getCgroupPath(m.Name(), cgroupPath, true)
	if err != nil {
		return meta.NewError(meta.NewErrorCode(meta.ErrNotFound, meta.CGROUPS), fmt.Sprintf("find base path of subsystem %s failed", m.Name()), err)
	}
	if err := os.RemoveAll(subsysCgroupPath); err != nil {
		return err
	}
	return nil
}

CgroupManager 实现

定义完各个 Subsystem 之后,向上抽象出一层 CgroupManager 来统一管理各 subsystem

/**
 * CgroupManager 统一管理各 subsystem
 * 1.添加当前进程到路径 path 下各 subsystem;
 * 2.更新路径 path 下各 subsystem 配置;
 * 3.删除 subsystem 约束;
 */

type CgroupManager struct {
	Path     string
	Resource *subsystems.ResourceConfig
}

func NewCgroupManger(path string) *CgroupManager {
	return &CgroupManager{
		Path: path,
	}
}

/**
 * 添加进程到 cgroup 节点(进程组)
 */
func (c *CgroupManager) Apply(pid int, conf *subsystems.ResourceConfig) error {
	for _, subsysIns := range subsystems.SubsystemIns {
		if err := subsysIns.Apply(c.Path, pid, conf); err != nil {
			return meta.NewError(meta.ErrWrite, fmt.Sprintf("CgroupManger::Apply subsystem %s failed", subsysIns.Name()), err)
		}
	}
	return nil
}

/**
 * 更新 Cgroups 资源配置
 */
func (c *CgroupManager) Set(conf *subsystems.ResourceConfig) error {
	for _, subsysIns := range subsystems.SubsystemIns {
		if err := subsysIns.Set(c.Path, conf); err != nil {
			return meta.NewError(meta.ErrWrite, fmt.Sprintf("CgroupManger::Set new subsystem.ResourceConfig %s failed", subsysIns.Name()), err)
		}
	}
	return nil
}

/**
 * 销毁所有 Cgroups 配置
 */
func (c *CgroupManager) Destory() error {
	for _, subsysIns := range subsystems.SubsystemIns {
		if err := subsysIns.Remove(c.Path); err != nil {
			return meta.NewError(meta.ErrWrite, fmt.Sprintf("CgroupManger::Destory subsystem.ResourceConfig %s failed", subsysIns.Name()), err)
		}
		log.Infof("delete directory %s for cgroupManger", c.Path)
	}
	return nil
}

测试

memory测试

启动容器,并限制内存阈值 100M,接着启动一个 200M 的 stress 进程压力测试,通过 top 命令查看进程实际内存占用:

[root@localhost Mydocker]# ./Mydockker run -it -mem 100m stress --vm-bytes 200m --vm-keep -m 1
{"level":"info","msg":"exec init command","time":"2024-02-04T15:44:22+08:00"}
{"level":"info","msg":"set cgroup memory for memory values 100m","time":"2024-02-04T15:44:22+08:00"}
{"level":"info","msg":"run::sendInitCommands all commands:stress --vm-bytes 200m --vm-keep -m 1","time":"2024-02-04T15:44:22+08:00"}
{"level":"info","msg":"init::ContainerResourceInit execuatble path=/usr/bin/stress","time":"2024-02-04T15:44:22+08:00"}
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd

cpu测试

启动容器指定 -cpu 10,限定进程只占用 10% 的CPU时间片资源;并且在容器进程中执行死循环,通过 top 命令查看进程使用资源比例。

[root@localhost Mydocker]# ./Mydockker run -it -cpu 10 /bin/sh
{"level":"info","msg":"run::sendInitCommands all commands:/bin/sh","time":"2024-02-04T15:55:54+08:00"}
{"level":"info","msg":"exec init command","time":"2024-02-04T15:55:54+08:00"}
{"level":"info","msg":"init::ContainerResourceInit execuatble path=/bin/sh","time":"2024-02-04T15:55:54+08:00"}

sh-4.2# while : ; do : ; done &
[1] 7

总结

这一节实现了对容器资源的管理和限制,具体流程如下:

  • 1)解析命令行参数,获取 Cgroups 相关参数:
    • -mem 10m 表示将内存限制为 10M;
    • -cpu 10 表示进程占用最多 10% 的CPU时间片资源;
  • 2)根据参数创建对应的子Cgroups,并配置相应的 subsystem,最后将 fork 出的子进程 pid 加入到当前 Cgroups 的 tasks 文件中;
    • 创建 Cgroups;
    • 配置 Subsystem,修改对应的文件值;
    • fork 子进程;
    • 将子进程 pid 加入到 Cgroups 中;
  • 子进程结束时删除相应的 Cgroups 资源组;
posted @ 2024-02-04 16:23  Stitches  阅读(22)  评论(0编辑  收藏  举报