mirror of
https://github.com/criyle/go-sandbox.git
synced 2025-11-04 14:49:53 +08:00
232 lines
5.2 KiB
Go
232 lines
5.2 KiB
Go
package cgroup
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Cgroup is the combination of sub-cgroups
|
|
type Cgroup struct {
|
|
prefix string
|
|
|
|
cpu *SubCgroup
|
|
cpuset *SubCgroup
|
|
cpuacct *SubCgroup
|
|
memory *SubCgroup
|
|
pids *SubCgroup
|
|
|
|
all []*SubCgroup
|
|
}
|
|
|
|
// Build creates new cgrouup directories
|
|
func (b *Builder) Build() (cg *Cgroup, err error) {
|
|
var (
|
|
paths []string
|
|
subCgroup = make(map[int]*SubCgroup)
|
|
info = getCachedCgroupHierarchy()
|
|
)
|
|
// if failed, remove potential created directory
|
|
defer func() {
|
|
if err != nil {
|
|
removeAll(paths...)
|
|
}
|
|
}()
|
|
for _, c := range []struct {
|
|
enable bool
|
|
name string
|
|
}{
|
|
{b.CPU, "cpu"},
|
|
{b.CPUSet, "cpuset"},
|
|
{b.CPUAcct, "cpuacct"},
|
|
{b.Memory, "memory"},
|
|
{b.Pids, "pids"},
|
|
} {
|
|
if !c.enable {
|
|
continue
|
|
}
|
|
h := info[c.name]
|
|
if subCgroup[h] == nil {
|
|
var path string
|
|
if path, err = CreateSubCgroupPath(c.name, b.Prefix); err != nil {
|
|
return
|
|
}
|
|
paths = append(paths, path)
|
|
subCgroup[h] = NewSubCgroup(path)
|
|
}
|
|
}
|
|
|
|
if b.CPUSet {
|
|
if err = initCpuset(subCgroup[info["cpuset"]].path); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
var all []*SubCgroup
|
|
for _, v := range subCgroup {
|
|
all = append(all, v)
|
|
}
|
|
|
|
return &Cgroup{
|
|
prefix: b.Prefix,
|
|
cpu: subCgroup[info["cpu"]],
|
|
cpuset: subCgroup[info["cpuset"]],
|
|
cpuacct: subCgroup[info["cpuacct"]],
|
|
memory: subCgroup[info["memory"]],
|
|
pids: subCgroup[info["pids"]],
|
|
all: all,
|
|
}, nil
|
|
}
|
|
|
|
// AddProc writes cgroup.procs to all sub-cgroup
|
|
func (c *Cgroup) AddProc(pid int) error {
|
|
for _, s := range c.all {
|
|
if err := s.WriteUint(cgroupProcs, uint64(pid)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Destroy removes dir for sub-cgroup, errors are ignored if remove one failed
|
|
func (c *Cgroup) Destroy() error {
|
|
var err1 error
|
|
for _, s := range c.all {
|
|
if err := remove(s.path); err != nil {
|
|
err1 = err
|
|
}
|
|
}
|
|
return err1
|
|
}
|
|
|
|
// SetCPUCfsPeriod set cpu.cfs_period_us in us
|
|
func (c *Cgroup) SetCPUCfsPeriod(p uint64) error {
|
|
return c.cpu.WriteUint("cpu.cfs_period_us", p)
|
|
}
|
|
|
|
// SetCPUCfsQuota set cpu.cfs_quota_us in us
|
|
func (c *Cgroup) SetCPUCfsQuota(p uint64) error {
|
|
return c.cpu.WriteUint("cpu.cfs_quota_us", p)
|
|
}
|
|
|
|
// SetCpusetCpus set cpuset.cpus
|
|
func (c *Cgroup) SetCpusetCpus(b []byte) error {
|
|
return c.cpuset.WriteFile("cpuset.cpus", b)
|
|
}
|
|
|
|
// SetCpusetMems set cpuset.mems
|
|
func (c *Cgroup) SetCpusetMems(b []byte) error {
|
|
return c.cpuset.WriteFile("cpuset.mems", b)
|
|
}
|
|
|
|
// CpuacctUsage read cpuacct.usage in ns
|
|
func (c *Cgroup) CpuacctUsage() (uint64, error) {
|
|
return c.cpuacct.ReadUint("cpuacct.usage")
|
|
}
|
|
|
|
// MemoryMaxUsageInBytes read memory.max_usage_in_bytes
|
|
func (c *Cgroup) MemoryMaxUsageInBytes() (uint64, error) {
|
|
return c.memory.ReadUint("memory.max_usage_in_bytes")
|
|
}
|
|
|
|
// SetMemoryLimitInBytes write memory.limit_in_bytes
|
|
func (c *Cgroup) SetMemoryLimitInBytes(i uint64) error {
|
|
return c.memory.WriteUint("memory.limit_in_bytes", i)
|
|
}
|
|
|
|
// MemoryMemswMaxUsageInBytes read memory.memsw.max_usage_in_bytes
|
|
func (c *Cgroup) MemoryMemswMaxUsageInBytes() (uint64, error) {
|
|
return c.memory.ReadUint("memory.memsw.max_usage_in_bytes")
|
|
}
|
|
|
|
// SetMemoryMemswLimitInBytes write memory.memsw.limit_in_bytes
|
|
func (c *Cgroup) SetMemoryMemswLimitInBytes(i uint64) error {
|
|
return c.memory.WriteUint("memory.memsw.limit_in_bytes", i)
|
|
}
|
|
|
|
// SetPidsMax write pids.max
|
|
func (c *Cgroup) SetPidsMax(i uint64) error {
|
|
return c.pids.WriteUint("pids.max", i)
|
|
}
|
|
|
|
// SetCpuacctUsage write cpuacct.usage in ns
|
|
func (c *Cgroup) SetCpuacctUsage(i uint64) error {
|
|
return c.cpuacct.WriteUint("cpuacct.usage", i)
|
|
}
|
|
|
|
// SetMemoryMaxUsageInBytes write cpuacct.usage in ns
|
|
func (c *Cgroup) SetMemoryMaxUsageInBytes(i uint64) error {
|
|
return c.memory.WriteUint("memory.max_usage_in_bytes", i)
|
|
}
|
|
|
|
// FindMemoryStatProperty find certain property from memory.stat
|
|
func (c *Cgroup) FindMemoryStatProperty(prop string) (uint64, error) {
|
|
content, err := c.memory.ReadFile("memory.stat")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
r := bytes.NewReader(content)
|
|
for {
|
|
var p string
|
|
var i uint64
|
|
_, err = fmt.Fscanln(r, &p, &i)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if p == prop {
|
|
return i, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// initCpuset will copy the config from the parent cpu sets if not exists
|
|
func initCpuset(path string) error {
|
|
for _, f := range []string{"cpuset.cpus", "cpuset.mems"} {
|
|
if err := copyCgroupPropertyFromParent(path, f); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func copyCgroupPropertyFromParent(path, name string) error {
|
|
// ensure current one empty
|
|
b, err := ioutil.ReadFile(filepath.Join(path, name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(strings.TrimSpace(string(b))) > 0 {
|
|
return nil
|
|
}
|
|
// otherwise copy from parent, first to ensure it is empty by recurssion
|
|
if err := copyCgroupPropertyFromParent(filepath.Dir(path), name); err != nil {
|
|
return err
|
|
}
|
|
b, err = ioutil.ReadFile(filepath.Join(filepath.Dir(path), name))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return ioutil.WriteFile(filepath.Join(path, name), b, filePerm)
|
|
}
|
|
|
|
func remove(name string) error {
|
|
if name != "" {
|
|
return os.Remove(name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func removeAll(name ...string) error {
|
|
var err1 error
|
|
for _, n := range name {
|
|
err := remove(n)
|
|
if err != nil && err1 == nil {
|
|
err1 = err
|
|
}
|
|
}
|
|
return err1
|
|
}
|