Do no set again nicelevel if it is already set, reduce cpu usage

* Fix logic thar gets nice level to avoide useless renice operations.
* Use maps instead of loop for process name matching
This commit is contained in:
eleksir
2024-04-13 12:41:55 +03:00
parent cf9c8f0b29
commit 227b9006dd
3 changed files with 129 additions and 127 deletions

230
main.go
View File

@@ -2,6 +2,7 @@ package main
import ( import (
"log" "log"
"math"
"os" "os"
"os/signal" "os/signal"
"os/user" "os/user"
@@ -21,6 +22,9 @@ var pool *pond.WorkerPool
// Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов. // Канал, в который приходят уведомления для хэндлера сигналов от траппера сигналов.
var sigChan = make(chan os.Signal, 1) var sigChan = make(chan os.Signal, 1)
var kill = make(map[string]string)
var renice = make(map[string]int)
// main основная функция программы. // main основная функция программы.
func main() { func main() {
// Самое время поставить траппер сигналов. // Самое время поставить траппер сигналов.
@@ -98,11 +102,21 @@ func reniced(cnf Config) {
ok := pool.TrySubmit(func() { ok := pool.TrySubmit(func() {
if processName, err := p.Name(); err == nil { if processName, err := p.Name(); err == nil {
for _, k := range cnf.Kill { for _, k := range cnf.Kill {
for _, killProcessName := range k.Name { if killSignal := kill[processName]; killSignal != "" {
if processName == killProcessName { switch killSignal {
switch k.Sig { case "kill":
case "kill": _ = p.SendSignal(syscall.SIGKILL)
_ = p.SendSignal(syscall.SIGKILL)
if cnf.Debug {
log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent",
processName,
strings.ToUpper(k.Sig),
)
}
case "stop":
if s, err := p.Status(); err == nil && s[0] != "stop" {
_ = p.SendSignal(syscall.SIGSTOP)
if cnf.Debug { if cnf.Debug {
log.Printf( log.Printf(
@@ -111,125 +125,121 @@ func reniced(cnf Config) {
strings.ToUpper(k.Sig), strings.ToUpper(k.Sig),
) )
} }
case "stop": } else if err == nil && s[0] == "stop" && cnf.Debug {
if s, err := p.Status(); err == nil && s[0] != "stop" { log.Printf(
_ = p.SendSignal(syscall.SIGSTOP) "Matching processName with killProcessname (%s) and it is already stopped",
processName,
)
}
case "term":
_ = p.SendSignal(syscall.SIGTERM)
if cnf.Debug { if cnf.Debug {
log.Printf( log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent", "Matching processName with killProcessname (%s), SIG%s sent",
processName, processName,
strings.ToUpper(k.Sig), strings.ToUpper(k.Sig),
) )
} }
} else if err == nil && s[0] == "stop" && cnf.Debug { case "int":
log.Printf( _ = p.SendSignal(syscall.SIGINT)
"Matching processName with killProcessname (%s) and it is already stopped",
processName,
)
}
case "term":
_ = p.SendSignal(syscall.SIGTERM)
if cnf.Debug { if cnf.Debug {
log.Printf( log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent", "Matching processName with killProcessname (%s), SIG%s sent",
processName, processName,
strings.ToUpper(k.Sig), strings.ToUpper(k.Sig),
) )
} }
case "int": case "quit":
_ = p.SendSignal(syscall.SIGINT) _ = p.SendSignal(syscall.SIGQUIT)
if cnf.Debug { if cnf.Debug {
log.Printf( log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent", "Matching processName with killProcessname (%s), SIG%s sent",
processName, processName,
strings.ToUpper(k.Sig), strings.ToUpper(k.Sig),
) )
} }
case "quit": case "abrt":
_ = p.SendSignal(syscall.SIGQUIT) _ = p.SendSignal(syscall.SIGABRT)
if cnf.Debug { if cnf.Debug {
log.Printf( log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent", "Matching processName with killProcessname (%s), SIG%s sent",
processName, processName,
strings.ToUpper(k.Sig), strings.ToUpper(k.Sig),
) )
} }
case "abrt": case "hup":
_ = p.SendSignal(syscall.SIGABRT) _ = p.SendSignal(syscall.SIGHUP)
if cnf.Debug { if cnf.Debug {
log.Printf( log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent", "Matching processName with killProcessname (%s), SIG%s sent",
processName, processName,
strings.ToUpper(k.Sig), strings.ToUpper(k.Sig),
) )
} }
case "hup": case "usr1":
_ = p.SendSignal(syscall.SIGHUP) _ = p.SendSignal(syscall.SIGUSR1)
if cnf.Debug { if cnf.Debug {
log.Printf( log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent", "Matching processName with killProcessname (%s), SIG%s sent",
processName, processName,
strings.ToUpper(k.Sig), strings.ToUpper(k.Sig),
) )
} }
case "usr1": case "usr2":
_ = p.SendSignal(syscall.SIGUSR1) _ = p.SendSignal(syscall.SIGUSR2)
if cnf.Debug { if cnf.Debug {
log.Printf( log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent", "Matching processName with killProcessname (%s), SIG%s sent",
processName, processName,
strings.ToUpper(k.Sig), strings.ToUpper(k.Sig),
) )
}
case "usr2":
_ = p.SendSignal(syscall.SIGUSR2)
if cnf.Debug {
log.Printf(
"Matching processName with killProcessname (%s), SIG%s sent",
processName,
strings.ToUpper(k.Sig),
)
}
} }
} }
} }
} }
// Для каждого процесса извлекаем его текущий nicelevel. // Для каждого процесса извлекаем его текущий priority.
if currentNiceLevel, err := p.Nice(); err == nil { if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
// Вынимаем список приоритетов. if niceLevel := renice[processName]; niceLevel != 0 {
for _, n := range cnf.Prio { prioLevel := niceLevel
// Для каждого приоритета, проходимся по списку имён процессов. currentNiceLevel := currentPrioLevel
for _, name := range n.Name {
// Достаём имя текущего процесса.
if processName, err = p.Name(); err == nil {
// Если имя процесса совпадает с одним из списка в конфиге, то пробуем менять ему nice.
if processName == name {
if currentNiceLevel != n.Nice {
_ = syscall.Setpriority(syscall.PRIO_PROCESS, int(p.Pid), int(n.Nice))
if cnf.Debug { if runtime.GOOS == "linux" {
log.Printf( // Value of PR = 20 + (-20 to +19) is 0 to 39
"Set niceness for %s(%d) to %d (was %d)", switch {
name, case niceLevel > 0:
p.Pid, prioLevel = 20 - niceLevel
n.Nice, case niceLevel < 0:
currentNiceLevel, prioLevel = 20 + int(math.Abs(float64(niceLevel)))
) default:
} prioLevel = 20
} else if cnf.Debug {
log.Printf("Nicelevel for %d already set to %d", p.Pid, n.Nice)
}
}
} }
currentNiceLevel = 20 - currentPrioLevel
}
if currentPrioLevel != prioLevel {
// on Linux Setpriority actually operates niceness value.
_ = syscall.Setpriority(syscall.PRIO_PROCESS, int(p.Pid), niceLevel)
if cnf.Debug {
log.Printf(
"Set niceness for %s(%d) to %d (was %d)",
processName,
p.Pid,
niceLevel,
currentNiceLevel,
)
}
} else if cnf.Debug {
log.Printf("Niceness for %d already set to %d", p.Pid, niceLevel)
} }
} }
} }

View File

@@ -32,7 +32,7 @@ type Config struct {
} }
type Prio []struct { type Prio []struct {
Nice int32 `yaml:"nice,omitempty"` Nice int `yaml:"nice,omitempty"`
Name []string `yaml:"name,omitempty"` Name []string `yaml:"name,omitempty"`
} }

24
util.go
View File

@@ -40,7 +40,7 @@ func readConf(path string) (Config, error) {
if cfg.CmdDelay == 0 { if cfg.CmdDelay == 0 {
cfg.CmdDelay = 20 cfg.CmdDelay = 20
log.Println("cmd_delay can not be 0, setting to %d milliseconds", cfg.CmdDelay) log.Printf("cmd_delay can not be 0, setting to %d milliseconds", cfg.CmdDelay)
} }
if cfg.LoopDelay == 0 { if cfg.LoopDelay == 0 {
@@ -67,10 +67,8 @@ func readConf(path string) (Config, error) {
log.Printf("nb_capacity set to same value as max_workers: %d", cfg.MaxWorkers) log.Printf("nb_capacity set to same value as max_workers: %d", cfg.MaxWorkers)
} }
newPrio := make(Prio, 0)
// Обычный юзер не может задрать nice больше 0, только руту такое можно. // Обычный юзер не может задрать nice больше 0, только руту такое можно.
var maxNice int32 var maxNice int
if os.Getuid() == 0 { if os.Getuid() == 0 {
maxNice = -20 maxNice = -20
@@ -79,10 +77,10 @@ func readConf(path string) (Config, error) {
for _, v := range cfg.Prio { for _, v := range cfg.Prio {
switch { switch {
case len(v.Name) != 0 && (v.Nice < 20 && v.Nice > maxNice): case len(v.Name) != 0 && (v.Nice < 20 && v.Nice > maxNice):
newPrio = append(newPrio, v) for _, procName := range v.Name {
renice[procName] = v.Nice
if cfg.Debug { if cfg.Debug {
for _, procName := range v.Name {
log.Printf("Add %s to list of nicelevel %d processes.", procName, v.Nice) log.Printf("Add %s to list of nicelevel %d processes.", procName, v.Nice)
} }
} }
@@ -93,18 +91,14 @@ func readConf(path string) (Config, error) {
} }
} }
cfg.Prio = newPrio
newKill := make(Kill, 0)
for _, v := range cfg.Kill { for _, v := range cfg.Kill {
if len(v.Name) != 0 { if len(v.Name) != 0 {
switch v.Sig { switch v.Sig {
case "kill", "stop", "term", "int", "quit", "abrt", "hup", "usr1", "usr2": case "kill", "stop", "term", "int", "quit", "abrt", "hup", "usr1", "usr2":
newKill = append(newKill, v) for _, procName := range v.Name {
kill[procName] = v.Sig
if cfg.Debug { if cfg.Debug {
for _, procName := range v.Name {
log.Printf( log.Printf(
"Add %s to list of processes that should be killed with SIG%s signal.", "Add %s to list of processes that should be killed with SIG%s signal.",
procName, procName,
@@ -120,8 +114,6 @@ func readConf(path string) (Config, error) {
} }
} }
cfg.Kill = newKill
return cfg, nil return cfg, nil
} }