Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81288389c6 | ||
|
|
5976d0b327 | ||
|
|
62bbf9d277 |
@@ -4,8 +4,23 @@ daemon: true
|
|||||||
# PID file for daemon.
|
# PID file for daemon.
|
||||||
pidfile: /var/run/reniced.pid
|
pidfile: /var/run/reniced.pid
|
||||||
|
|
||||||
# Delay among iterations.
|
# Delay between process list update task consecutive runs.
|
||||||
loop_delay: 5000
|
process_list_update_period: 5000
|
||||||
|
|
||||||
|
# Delay between kill consecutive tasks.
|
||||||
|
process_kill_period: 5000
|
||||||
|
|
||||||
|
# Delay between changing niceness consecutive tasks.
|
||||||
|
process_nice_period: 5000
|
||||||
|
|
||||||
|
# Delay between changing io niceness consecutive tasks (linux-only).
|
||||||
|
process_ionice_period: 5000
|
||||||
|
|
||||||
|
# Period of time between consecutive periodic_process list tasks hammering.
|
||||||
|
periodic_process_invocation_period: 60000
|
||||||
|
|
||||||
|
# Period of time between process exec() invocations.
|
||||||
|
periodic_process_invocation_delay: 500
|
||||||
|
|
||||||
# Whether to print debug messages to stdout.
|
# Whether to print debug messages to stdout.
|
||||||
debug: true
|
debug: true
|
||||||
@@ -15,7 +30,11 @@ max_workers: 3
|
|||||||
|
|
||||||
# Capacity of non-blocking tasks. All tasks that exceeding this capacity will block submission of new tasks until queue
|
# Capacity of non-blocking tasks. All tasks that exceeding this capacity will block submission of new tasks until queue
|
||||||
# has vacant place for new task.
|
# has vacant place for new task.
|
||||||
nb_capacity: 30
|
nb_capacity: 3000
|
||||||
|
|
||||||
|
# Time to time run these commands.
|
||||||
|
periodic_process:
|
||||||
|
- ps xa
|
||||||
|
|
||||||
kill:
|
kill:
|
||||||
- sig: stop
|
- sig: stop
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env gmake -f
|
#!/usr/bin/env gmake -f
|
||||||
|
|
||||||
BINARY=reniced
|
BINARY=reniced
|
||||||
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all=-l -trimpath
|
BUILDOPTS=-ldflags="-s -w" -a -gcflags=all="-l -B" -trimpath -buildvcs=false
|
||||||
|
|
||||||
all: clean build
|
all: clean build
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if d, err := cntxt.Reborn(); err != nil {
|
if d, err := cntxt.Reborn(); err != nil {
|
||||||
log.Fatal("Unable to run: ", err)
|
log.Fatalf("Unable to run: %s", err) //nolint: gocritic
|
||||||
|
|
||||||
return
|
|
||||||
} else if d != nil {
|
} else if d != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ package lib
|
|||||||
|
|
||||||
import proc "github.com/shirou/gopsutil/v3/process"
|
import proc "github.com/shirou/gopsutil/v3/process"
|
||||||
|
|
||||||
func GetIOPriority(which int, who int) (prio uint32, err error) {
|
func (cnf Config) GetIOPriority(which int, who int) (prio uint32, err error) {
|
||||||
return 0, NotImplementedError
|
return 0, cnf.NotImplementedError
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetIOPriority(which int, who int, prio uint32) (err error) {
|
func (cnf Config) SetIOPriority(which int, who int, prio uint32) (err error) {
|
||||||
return NotImplementedError
|
return cnf.NotImplementedError
|
||||||
}
|
}
|
||||||
|
|
||||||
func IORenice(cnf Config, p *proc.Process, processName string) {}
|
func (cnf Config) IORenice(p *proc.Process, processName string) {}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package lib
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -13,100 +14,149 @@ import (
|
|||||||
|
|
||||||
// reniced основная логика.
|
// reniced основная логика.
|
||||||
func (cnf Config) Reniced() {
|
func (cnf Config) Reniced() {
|
||||||
|
var (
|
||||||
|
processListUpdateTimer *time.Ticker
|
||||||
|
processKillTimer *time.Ticker
|
||||||
|
processReniceTimer *time.Ticker
|
||||||
|
processIONiceTimer *time.Ticker
|
||||||
|
processListTimer *time.Ticker
|
||||||
|
processList []*proc.Process
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// Прежде всего настроим таймеры
|
||||||
|
processListUpdateTimer = time.NewTicker(time.Duration(cnf.ProcessListUpdatePeriod) * time.Millisecond)
|
||||||
|
processReniceTimer = time.NewTicker(time.Duration(cnf.ProcessNicePeriod) * time.Millisecond)
|
||||||
|
processKillTimer = time.NewTicker(time.Duration(cnf.ProcessKillPeriod) * time.Millisecond)
|
||||||
|
processIONiceTimer = time.NewTicker(time.Duration(cnf.ProcessIONicePeriod) * time.Millisecond)
|
||||||
|
processListTimer = time.NewTicker(time.Duration(cnf.PeriodicProcessInvocationPeriod) * time.Millisecond)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
processList, err := proc.Processes()
|
select {
|
||||||
|
// Обновляем список процессов.
|
||||||
|
case <-processListUpdateTimer.C:
|
||||||
|
processList, err = proc.Processes()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to get process list: %s", err)
|
log.Fatalf("Unable to get process list: %s", err)
|
||||||
}
|
}
|
||||||
|
// Проходим по списку процессов и посылаем сигналы процессам.
|
||||||
// Не самый эффективный, зато работающий способ - простой перебор массивов.
|
case <-processKillTimer.C:
|
||||||
for _, p := range processList {
|
for _, p := range processList {
|
||||||
ok := cnf.Pool.TrySubmit(func() {
|
ok := cnf.Pool.TrySubmit(func() {
|
||||||
if processName, err := p.Name(); err == nil {
|
if processName, err := p.Name(); err == nil {
|
||||||
// Для каждого процесса извлекаем его текущий priority.
|
// Посылаем процессу сигналы, если таковые есть в конфиге.
|
||||||
if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
|
if killSignal := cnf.KillSignal[processName]; killSignal != 0 {
|
||||||
if niceLevel := cnf.Renice[processName]; niceLevel != 0 {
|
switch killSignal { //nolint:exhaustive
|
||||||
prioLevel := niceLevel
|
case syscall.SIGSTOP:
|
||||||
currentNiceLevel := currentPrioLevel
|
_ = p.SendSignal(killSignal)
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
|
||||||
// Value of PR = 20 + (-20 to +19) is 0 to 39
|
|
||||||
switch {
|
|
||||||
case niceLevel > 0:
|
|
||||||
prioLevel = 20 - niceLevel
|
|
||||||
case niceLevel < 0:
|
|
||||||
prioLevel = 20 + int(math.Abs(float64(niceLevel)))
|
|
||||||
default:
|
|
||||||
prioLevel = 20
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
if cnf.Debug {
|
||||||
log.Printf(
|
log.Printf(
|
||||||
"Set niceness for %s(%d) to %d (was %d)",
|
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
strings.ToUpper(killSignal.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
_ = p.SendSignal(killSignal)
|
||||||
|
|
||||||
|
if cnf.Debug {
|
||||||
|
log.Printf(
|
||||||
|
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
||||||
|
processName,
|
||||||
|
p.Pid,
|
||||||
|
strings.ToUpper(killSignal.String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Printf("Unable to add task to pool. Increase nb_capacity?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Проходим по списку процессов и меняем ionice процессам.
|
||||||
|
case <-processIONiceTimer.C:
|
||||||
|
for _, p := range processList {
|
||||||
|
ok := cnf.Pool.TrySubmit(func() {
|
||||||
|
if processName, err := p.Name(); err == nil {
|
||||||
|
cnf.IORenice(p, processName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Printf("Unable to add task to pool. Increase nb_capacity?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Проходим по списку того, что надо запустить и запускаем.
|
||||||
|
case <-processListTimer.C:
|
||||||
|
for _, v := range cnf.PeriodicProcesses {
|
||||||
|
log.Printf("Invoking command: %s", v)
|
||||||
|
exec.Command(v)
|
||||||
|
time.Sleep(time.Duration(cnf.PeriodicProcessInvocationDelay) * time.Millisecond)
|
||||||
|
}
|
||||||
|
// Проходим по списку процессов и меняем ionice процессам.
|
||||||
|
case <-processReniceTimer.C:
|
||||||
|
// Не самый эффективный, зато работающий способ - простой перебор массивов.
|
||||||
|
for _, p := range processList {
|
||||||
|
ok := cnf.Pool.TrySubmit(func() {
|
||||||
|
if processName, err := p.Name(); err == nil {
|
||||||
|
// Для каждого процесса извлекаем его текущий priority.
|
||||||
|
if currentPrioLevel, err := syscall.Getpriority(syscall.PRIO_PROCESS, int(p.Pid)); err == nil {
|
||||||
|
if niceLevel := cnf.Renice[processName]; niceLevel != 0 {
|
||||||
|
prioLevel := niceLevel
|
||||||
|
currentNiceLevel := currentPrioLevel
|
||||||
|
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
// Value of PR = 20 + (-20 to +19) is 0 to 39
|
||||||
|
switch {
|
||||||
|
case niceLevel > 0:
|
||||||
|
prioLevel = 20 - niceLevel
|
||||||
|
case niceLevel < 0:
|
||||||
|
prioLevel = 20 + int(math.Abs(float64(niceLevel)))
|
||||||
|
default:
|
||||||
|
prioLevel = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
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 %s(%d) already set to %d",
|
||||||
processName,
|
processName,
|
||||||
p.Pid,
|
p.Pid,
|
||||||
niceLevel,
|
niceLevel,
|
||||||
currentNiceLevel,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Niceness for %s(%d) already set to %d",
|
|
||||||
processName,
|
|
||||||
p.Pid,
|
|
||||||
niceLevel,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
cnf.IORenice(p, processName)
|
if !ok {
|
||||||
|
log.Printf("Unable to add task to pool. Increase nb_capacity?")
|
||||||
// Посылаем процессу сигналы, если таковые есть в конфиге.
|
|
||||||
if killSignal := cnf.KillSignal[processName]; killSignal != 0 {
|
|
||||||
switch killSignal { //nolint:exhaustive
|
|
||||||
case syscall.SIGSTOP:
|
|
||||||
_ = p.SendSignal(killSignal)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
p.Pid,
|
|
||||||
strings.ToUpper(killSignal.String()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
_ = p.SendSignal(killSignal)
|
|
||||||
|
|
||||||
if cnf.Debug {
|
|
||||||
log.Printf(
|
|
||||||
"Matching processName with killProcessname %s(%d), SIG%s sent",
|
|
||||||
processName,
|
|
||||||
p.Pid,
|
|
||||||
strings.ToUpper(killSignal.String()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
log.Printf("Unable to add task to pool. Increase nb_capacity?")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * time.Duration(cnf.LoopDelay))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Мы сюда никогда не попадём.
|
// Мы сюда никогда не попадём.
|
||||||
|
|||||||
@@ -29,8 +29,26 @@ type Config struct {
|
|||||||
// Where to put pid-file.
|
// Where to put pid-file.
|
||||||
Pidfile string `yaml:"pidfile,omitempty"`
|
Pidfile string `yaml:"pidfile,omitempty"`
|
||||||
|
|
||||||
// LoopDelay in milliseconds - whole set of action delay period.
|
// ProcessListUpdatePeriod - delay between process list update task consecutive runs.
|
||||||
LoopDelay int `yaml:"loop_delay,omitempty"`
|
ProcessListUpdatePeriod int `yaml:"process_list_update_period,omitempty"`
|
||||||
|
|
||||||
|
// ProcessKillPeriod in milliseconds - delay between kill consecutive tasks.
|
||||||
|
ProcessKillPeriod int `yaml:"process_kill_period,omitempty"`
|
||||||
|
|
||||||
|
// ProcessNicePeriod in milliseconds - delay between changing niceness consecutive tasks.
|
||||||
|
ProcessNicePeriod int `yaml:"process_nice_period,omitempty"`
|
||||||
|
|
||||||
|
// ProcessNicePeriod in milliseconds - delay between changing io niceness consecutive tasks (linux-only).
|
||||||
|
ProcessIONicePeriod int `yaml:"process_ionice_period,omitempty"`
|
||||||
|
|
||||||
|
// PeriodicProcessInvocationPeriod in milliseconds - delay between PeriodicProcesses invocations.
|
||||||
|
PeriodicProcessInvocationPeriod int `yaml:"periodic_process_invocation_period,omitempty"`
|
||||||
|
|
||||||
|
// PriodicProcessInvocationDelay in milliseconds - delay between each process calling in PeriodicProcesses list.
|
||||||
|
PeriodicProcessInvocationDelay int `yaml:"periodic_process_invocation_delay,omitempty"`
|
||||||
|
|
||||||
|
// PeriodicProcesses list of processes that should be executed priodically.
|
||||||
|
PeriodicProcesses []string `yaml:"periodic_process,omitempty"`
|
||||||
|
|
||||||
// Whether to print debug info on stdout.
|
// Whether to print debug info on stdout.
|
||||||
Debug bool `yaml:"debug,omitempty"`
|
Debug bool `yaml:"debug,omitempty"`
|
||||||
|
|||||||
@@ -70,9 +70,34 @@ func (cnf *Config) readConfFile(path string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cnf.LoopDelay == 0 {
|
if cnf.ProcessListUpdatePeriod == 0 {
|
||||||
cnf.LoopDelay = 2000
|
cnf.ProcessListUpdatePeriod = 5000
|
||||||
log.Printf("loop_delay set to %d milliseconds", cnf.LoopDelay)
|
log.Printf("process_list_update_priod set to %d milliseconds", cnf.ProcessListUpdatePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.ProcessNicePeriod == 0 {
|
||||||
|
cnf.ProcessNicePeriod = 5000
|
||||||
|
log.Printf("process_nice_priod set to %d milliseconds", cnf.ProcessNicePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.ProcessIONicePeriod == 0 {
|
||||||
|
cnf.ProcessIONicePeriod = 5000
|
||||||
|
log.Printf("process_ionice_period set to %d milliseconds", cnf.ProcessIONicePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.ProcessKillPeriod == 0 {
|
||||||
|
cnf.ProcessKillPeriod = 5000
|
||||||
|
log.Printf("process_kill_period set to %d milliseconds", cnf.ProcessIONicePeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.PeriodicProcessInvocationPeriod == 0 {
|
||||||
|
cnf.PeriodicProcessInvocationPeriod = 60000
|
||||||
|
log.Printf("periodic_process_invocation_period set to %d milliseconds", cnf.PeriodicProcessInvocationPeriod)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnf.PeriodicProcessInvocationDelay == 0 {
|
||||||
|
cnf.PeriodicProcessInvocationDelay = 500
|
||||||
|
log.Printf("periodic_process_invocation_delay set to %d milliseconds", cnf.PeriodicProcessInvocationDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cnf.Debug {
|
if cnf.Debug {
|
||||||
@@ -80,7 +105,7 @@ func (cnf *Config) readConfFile(path string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cnf.MaxWorkers == 0 {
|
if cnf.MaxWorkers == 0 {
|
||||||
cnf.MaxWorkers = 5
|
cnf.MaxWorkers = runtime.NumCPU()
|
||||||
log.Printf("max_workers set to %d", cnf.MaxWorkers)
|
log.Printf("max_workers set to %d", cnf.MaxWorkers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user