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:
230
main.go
230
main.go
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
types.go
2
types.go
@@ -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
24
util.go
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user