Initial commit
This commit is contained in:
20
LICENSE.txt
Normal file
20
LICENSE.txt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2022 Sergei Fedosoff <eleksir@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
66
README.md
Normal file
66
README.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Reniced
|
||||||
|
|
||||||
|
Сервис автоматического изменения приоритета исполнения процессов. Работает по принципу периодиеского вылавливания списка
|
||||||
|
запущенных процессов и изменения приоритета "проклятым" и (если получится) "благословлённым" процессам.
|
||||||
|
|
||||||
|
## Зачем это нужно?
|
||||||
|
|
||||||
|
В некоторых случаях необходимо, чтобы система обладала достаточной интерактивностью, чтобы, например, нормально работала
|
||||||
|
видеосвязь в таких мессенджерах, как VK Teams.
|
||||||
|
|
||||||
|
## Как это установить и заставить работать?
|
||||||
|
|
||||||
|
Для установки нам понадобятся пакеты с перлом и перловым модулем JSON::XS из текущего дистрибутива линукса, например, их
|
||||||
|
можно уставить командами:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
apt-get install perl perl-json-xs
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dnf install perl perl-json-xs
|
||||||
|
```
|
||||||
|
|
||||||
|
Как заставить это работать?
|
||||||
|
|
||||||
|
Вначале надо скопировать конфиг из **config_example.json** в **~/.reniced.json** и далее подправить все необходимые
|
||||||
|
значения в нём.
|
||||||
|
|
||||||
|
После настройки списка процессов и приоритетов их работы, можно запускать сервис как unix daemon или как обычный
|
||||||
|
процесс. Второй вариант нужен для запуска сервиса через systemd.
|
||||||
|
|
||||||
|
Если reniced планируется запускать от рута (uid=0), то конфиг будет находиться не в **~/.reniced.json**, а в
|
||||||
|
**/etc/reniced.json**.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
### Для MacOS
|
||||||
|
|
||||||
|
В свежих выпусках Mac OS нет возможности запускать сервисы от пользователя с uid=0, при запуске системы. Во всяком
|
||||||
|
случае без танцев с бубнами и отключения "защиты" системных файлов от модификации этого не сделать. Поэтому придётся при
|
||||||
|
запуске системы ручками писать
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo reniced
|
||||||
|
```
|
||||||
|
|
||||||
|
в каталоге с reniced. Кроме того под MacOS конфиг всегда ищется в ~/.reniced.json
|
||||||
|
|
||||||
|
В линуксах такой проблемы пока нет (но возможно будет, когда придут "атомарные" обДНОвления).
|
||||||
|
|
||||||
|
### Для Linux
|
||||||
|
|
||||||
|
В линуксах есть такая фича, как namespaces. Так вот, без особых приседаний и раскачиваний, нет возможности собрать
|
||||||
|
список процессов не из текущего namespace-а. Так что скорее всего в некоторых контейнерных системах изоляции окружений
|
||||||
|
возможны варианты, при которых у изолированных таким образом процессов не будет меняться приоритет исполнения.
|
||||||
|
|
||||||
|
### Общего характера
|
||||||
|
|
||||||
|
По понятным причинам, нет возможности изменять niceness до отрицательных величин, если процесс reniced запущен от
|
||||||
|
обычного пользователя (uid > 0).
|
||||||
|
|
||||||
|
При запуске reniced от обычного пользователя, niceness процесса может идти только в плюс. То есть процесс с nice=11, не
|
||||||
|
получится сделать с nice=9, например (без перезапуска процесса, конечно). Зато от суперпользователя такое работает.
|
||||||
|
|
||||||
|
## Special thans
|
||||||
|
Авторам VK Teams. Без их мессенджера, с его "недостатками", не было бы этого костыля.
|
||||||
137
example_config.json
Normal file
137
example_config.json
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
# 0 - запустить как обычный прооцесс (для systemd, например)
|
||||||
|
# > 0 - запустить как unix daemon
|
||||||
|
"foreground" : 0
|
||||||
|
},
|
||||||
|
|
||||||
|
"kill" : {
|
||||||
|
"STOP": [
|
||||||
|
# Это такая бэкапилка для MacOS, но на удалёнке она больше жрёт проц и греет макбук, чем что-то там реально
|
||||||
|
# бэкапит.
|
||||||
|
"commvault"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
# Это на самом деле nice level, safe values 0-19, для обычного пользователя и -19-19 для суперпользователя (uid=0)
|
||||||
|
# Значения в "списке процессов" задаются регулярными выражениями
|
||||||
|
"prio" : {
|
||||||
|
# High prio: Audio, Streaming, Video, Windows management
|
||||||
|
"-11": [
|
||||||
|
"coreaudiod",
|
||||||
|
"WindowServer",
|
||||||
|
"com.apple.audio.SandboxHelper",
|
||||||
|
"com.apple.audio.DriverHelper",
|
||||||
|
"audioclocksyncd",
|
||||||
|
"SystemUIServer"
|
||||||
|
],
|
||||||
|
|
||||||
|
# Medium prio
|
||||||
|
"-5": [
|
||||||
|
"VTDecoderXPCService ",
|
||||||
|
"VTEncoderXPCService",
|
||||||
|
"VK Teams ",
|
||||||
|
"mDNSResponder$",
|
||||||
|
"mDNSResponderHelper$",
|
||||||
|
"AudioComponentRegistrar",
|
||||||
|
"CVMServer"
|
||||||
|
],
|
||||||
|
|
||||||
|
# Above normal - keyboard event remapper and trapper
|
||||||
|
"-3": [
|
||||||
|
"Karabiner\\-Elements",
|
||||||
|
"Hammerspoon",
|
||||||
|
"MTLCompilerService",
|
||||||
|
"com.apple.appkit.xpc.openAndSavePanelService"
|
||||||
|
],
|
||||||
|
|
||||||
|
# Background processes, loggers, misc less valuable services
|
||||||
|
"3": [
|
||||||
|
"\\/logd$",
|
||||||
|
"\\/syslogd",
|
||||||
|
"syspolicyd",
|
||||||
|
"\\/cron$",
|
||||||
|
"softwareupdated",
|
||||||
|
"periodic\\-wrapper",
|
||||||
|
"iconservicesagent",
|
||||||
|
"com.apple.quicklook.ThumbnailsAgent",
|
||||||
|
"appstored"
|
||||||
|
],
|
||||||
|
|
||||||
|
# IO active and system services
|
||||||
|
"5": [
|
||||||
|
"corebrightnessd",
|
||||||
|
"airportd",
|
||||||
|
"containermanagerd",
|
||||||
|
"searchpartyd",
|
||||||
|
"\\/usr\\/bin\\/log",
|
||||||
|
"corespeechd",
|
||||||
|
"peopled",
|
||||||
|
"AssetCacheLocatorService",
|
||||||
|
"avatarsd",
|
||||||
|
"contactsdonationagent",
|
||||||
|
"storedownloadd",
|
||||||
|
"Dock\\.app",
|
||||||
|
"Dock$",
|
||||||
|
"Finder$",
|
||||||
|
"DockHelper$"
|
||||||
|
],
|
||||||
|
|
||||||
|
# Browsers and such
|
||||||
|
"9": [
|
||||||
|
"\\/Google Chrome",
|
||||||
|
"\\/Google Chrome Helper",
|
||||||
|
"EmojiFunction",
|
||||||
|
"Microsoft Edge Helper",
|
||||||
|
"Opera Helper",
|
||||||
|
"UserNotificationCenter"
|
||||||
|
],
|
||||||
|
|
||||||
|
# Even more less important services
|
||||||
|
"11": [
|
||||||
|
"siriactionsd",
|
||||||
|
"CoreSpotlightService",
|
||||||
|
"spotlightknowledged",
|
||||||
|
"Spotlight",
|
||||||
|
"suggestd",
|
||||||
|
"studentd",
|
||||||
|
"com.apple.siri-distributed\\-evaluation",
|
||||||
|
"com.apple.CloudPhotosConfiguration"
|
||||||
|
],
|
||||||
|
"12": ["bash"],
|
||||||
|
# IO hogs and other "bad" services
|
||||||
|
"19":[
|
||||||
|
"coreduetd",
|
||||||
|
"endpointsecurityd",
|
||||||
|
"analyticsd",
|
||||||
|
"backupd",
|
||||||
|
"com.apple.CodeSigningHelper",
|
||||||
|
"distnoted",
|
||||||
|
"backupd\\-helper",
|
||||||
|
"JamfDaemon",
|
||||||
|
"osqueryd",
|
||||||
|
"commvault",
|
||||||
|
"diagnosticd",
|
||||||
|
"JamfProCommService",
|
||||||
|
"XprotectService",
|
||||||
|
"XProtectPluginService",
|
||||||
|
"media\\-indexer",
|
||||||
|
"com.apple.Safari.SafeBrowsing.Service",
|
||||||
|
"SafariNotificationAgent",
|
||||||
|
"AMPArtworkAgent",
|
||||||
|
"AMPLibraryAgent",
|
||||||
|
"IDSBlastDoorService",
|
||||||
|
"photoanalysisd",
|
||||||
|
"photolibraryd",
|
||||||
|
"Crash Reports",
|
||||||
|
"\\/mds$",
|
||||||
|
"\\/mds_stores$",
|
||||||
|
"\\/mdsync",
|
||||||
|
"mdbulkimport",
|
||||||
|
"mdworker_shared",
|
||||||
|
"osqueryd",
|
||||||
|
"opendirectoryd",
|
||||||
|
"mediaanalysisd"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
224
reniced
Normal file
224
reniced
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
# Renice some processes, to make system more respositive
|
||||||
|
|
||||||
|
# TODO: Add -c parameter to pointout where config actually is
|
||||||
|
# TODO: Add perlcritic rules :)
|
||||||
|
# TODO: Запользовать модуль Proc::Find для получения списка pid-ов искомых процессов и их текущих парметров.
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
use open qw (:std :utf8);
|
||||||
|
use Encode qw (encode);
|
||||||
|
use English qw (-no_match_vars);
|
||||||
|
use JSON::XS ();
|
||||||
|
use POSIX qw (setsid);
|
||||||
|
|
||||||
|
local $SIG{INT} = \&__signal_handler;
|
||||||
|
local $SIG{TERM} = \&__signal_handler;
|
||||||
|
|
||||||
|
my $list;
|
||||||
|
my $F;
|
||||||
|
|
||||||
|
my $uid = $<;
|
||||||
|
|
||||||
|
my $config = LoadConf();
|
||||||
|
my $pidfile = '/run/reniced.pid'; # Пусть будет глобальной переменной, чтобы обратиться к ней в хэндлерах сигналов
|
||||||
|
|
||||||
|
if ($uid > 0) {
|
||||||
|
$pidfile = sprintf '/tmp/reniced-%s.pid', $uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((defined $config->{patameters}->{pidfile}) && ($config->{patameters}->{pidfile} ne '')) {
|
||||||
|
$pidfile = $config->{patameters}->{pidfile};
|
||||||
|
}
|
||||||
|
|
||||||
|
unless ($config->{foreground}) {
|
||||||
|
daemonize ();
|
||||||
|
my $pidfilehandler;
|
||||||
|
|
||||||
|
# Поскольку мы логи никуда не пишем, то обработать ошибку, если она возникнет, вербозно не можем.
|
||||||
|
if (open ($pidfilehandler, '>', $pidfile)) {
|
||||||
|
if (print {$pidfilehandler} $PID) {
|
||||||
|
close $pidfilehandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (sleep 2) {
|
||||||
|
open $F, qq{ps xau |} or do { warn "Can't 'run ps xau': $!" ; next; };
|
||||||
|
binmode $F, ':utf8';
|
||||||
|
|
||||||
|
my $cmd;
|
||||||
|
|
||||||
|
while (my $str = readline $F) {
|
||||||
|
chomp $str;
|
||||||
|
my ($user, $pid, $cpu, $mem, $vsz, $rss, $tt, $stat, $started, $time, $command) = split /\s+/, $str, 11;
|
||||||
|
if ($uid > 0) { # i'm not root!
|
||||||
|
my $u = getpwuid ($<);
|
||||||
|
|
||||||
|
if (defined $pid && defined $command && defined $user) {
|
||||||
|
if (($u eq $user) && ($pid =~ /^\d+$/)) {
|
||||||
|
push @{$cmd->{$command}}, $pid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { # fuck, yeah! i'm root!
|
||||||
|
# since we interested in changing niceness of processes, we need to make map command->[pids]
|
||||||
|
if (defined $pid && defined $command) {
|
||||||
|
if ($pid =~ /^\d+$/) {
|
||||||
|
push @{$cmd->{$command}}, $pid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close $F;
|
||||||
|
|
||||||
|
if (defined $cmd) {
|
||||||
|
# bulk chanage niceness :)
|
||||||
|
my $prio = $config->{prio};
|
||||||
|
my $kill = $config->{kill};
|
||||||
|
|
||||||
|
foreach my $kill_signal (keys %{$kill}) {
|
||||||
|
my @pids;
|
||||||
|
|
||||||
|
foreach my $command (keys %{$cmd}) {
|
||||||
|
foreach my $command_pattern (@{$kill->{$kill_signal}}) {
|
||||||
|
if ($command =~ /$command_pattern/) {
|
||||||
|
push @pids, @{$cmd->{$command}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($#pids >= 0) {
|
||||||
|
my $killcmd = sprintf 'kill -%s %s', $kill_signal, join (' ', @pids);
|
||||||
|
`$killcmd`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $priority (keys %{$prio}) {
|
||||||
|
my @pids;
|
||||||
|
|
||||||
|
foreach my $command (keys %{$cmd}) {
|
||||||
|
foreach my $command_pattern (@{$prio->{$priority}}) {
|
||||||
|
if ($command =~ /$command_pattern/u) {
|
||||||
|
push @pids, @{$cmd->{$command}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($#pids >= 0) {
|
||||||
|
my $renice;
|
||||||
|
|
||||||
|
if (($uid > 0) && ($priority < 0)) {
|
||||||
|
$renice = sprintf "renice 0 -p %s", join (' ', @pids);
|
||||||
|
} else {
|
||||||
|
$renice = sprintf "renice %s -p %s", $priority, join (' ', @pids);
|
||||||
|
}
|
||||||
|
|
||||||
|
`$renice`;
|
||||||
|
|
||||||
|
if ($priority > 2) {
|
||||||
|
if ($OSNAME eq 'darwin') {
|
||||||
|
my $taskpol = sprintf 'taskpolicy -b -p %s', join (' '. @pids);
|
||||||
|
`$taskpol`;
|
||||||
|
} elsif ($OSNAME eq 'linux') {
|
||||||
|
foreach my $pid (@pids) {
|
||||||
|
`ionice -c 3 -p $pid`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$#pids = -1; undef @pids;
|
||||||
|
sleep 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
undef $prio;
|
||||||
|
undef $cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# This code unreachable, but for formal correctness it should be here :)
|
||||||
|
if (-f $pidfile) {
|
||||||
|
unlink $pidfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
|
||||||
|
sub LoadConf {
|
||||||
|
my $file = '/etc/reniced.json';
|
||||||
|
my $uid = $<;
|
||||||
|
|
||||||
|
# On recent MacOS we have no direct ability to run reniced as system service, so we have to run it as user, probably
|
||||||
|
# via sudo, so we have to check OS here and use $HOME/.reniced.json for MacOS.
|
||||||
|
if (($OSNAME eq 'darwin') || ($uid != 0)) {
|
||||||
|
# Assume that script is not running under completely sanitized environment
|
||||||
|
die "No HOME variable found.\n" unless (defined $ENV{HOME});
|
||||||
|
|
||||||
|
$file = sprintf '%s/.reniced.json', $ENV{HOME};
|
||||||
|
}
|
||||||
|
|
||||||
|
open my $FILEHANDLE, '<', $file || die "[FATA] No conf at $file: $OS_ERROR\n";
|
||||||
|
|
||||||
|
# Use binmode in order to get amount of read bytes (not chars!) from read()
|
||||||
|
binmode $FILEHANDLE;
|
||||||
|
my $len = (stat $file) [7];
|
||||||
|
my $json;
|
||||||
|
my $readlen = read $FILEHANDLE, $json, $len;
|
||||||
|
$json = encode 'UTF-8', $json;
|
||||||
|
|
||||||
|
unless (defined $readlen) {
|
||||||
|
close $FILEHANDLE;
|
||||||
|
die "Unable to read $file: $OS_ERROR\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($readlen != $len) {
|
||||||
|
close $FILEHANDLE;
|
||||||
|
die "File $file is $len bytes on disk, but we read only $readlen bytes\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
close $FILEHANDLE;
|
||||||
|
|
||||||
|
my $j = JSON::XS->new->utf8->relaxed;
|
||||||
|
my $config = eval { $j->decode ($json); };
|
||||||
|
|
||||||
|
unless (defined $config) {
|
||||||
|
die "File $file does not contain a valid json data: $EVAL_ERROR\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub daemonize {
|
||||||
|
__fork ();
|
||||||
|
setsid ();
|
||||||
|
open STDIN, '<', '/dev/null';
|
||||||
|
open STDOUT, '>', '/dev/null';
|
||||||
|
open STDERR, '>', '/dev/null';
|
||||||
|
__fork ();
|
||||||
|
umask 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub __fork {
|
||||||
|
my $pid = fork;
|
||||||
|
|
||||||
|
unless (defined $pid) {
|
||||||
|
die ("Can't fork: $!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pid != 0) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub __signal_handler {
|
||||||
|
# remove pidfile if it exist
|
||||||
|
if (-f $pidfile) {
|
||||||
|
unlink $pidfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user