niceload: Implemented '-q'

This commit is contained in:
Ole Tange 2011-07-20 18:20:29 +02:00
parent 10e4f9c5f8
commit ded4ad022f
2 changed files with 106 additions and 86 deletions

View file

@ -36,6 +36,32 @@ if(not defined $::opt_run_mem) { $::opt_run_mem = $::opt_mem; }
if(not defined $::opt_start_noswap) { $::opt_start_noswap = $::opt_noswap; } if(not defined $::opt_start_noswap) { $::opt_start_noswap = $::opt_noswap; }
if(not defined $::opt_run_noswap) { $::opt_run_noswap = $::opt_noswap; } if(not defined $::opt_run_noswap) { $::opt_run_noswap = $::opt_noswap; }
my $limit = Limit->new();
my $process = Process->new($::opt_nice,@ARGV);
if(not $::opt_pid) {
# Wait until limit is below start_limit and run_limit
while($limit->over_start_limit()
or
($limit->hard() and $limit->over_run_limit())) {
$limit->sleep_for_recheck();
}
}
$process->start();
while($process->is_running()) {
if($limit->over_run_limit()) {
$process->suspend();
$limit->sleep_for_recheck();
if(not $limit->hard()) {
$process->resume();
$limit->sleep_while_running();
}
} else {
$process->resume();
$limit->sleep_while_running();
}
}
sub get_options_from_array { sub get_options_from_array {
# Run GetOptions on @array # Run GetOptions on @array
# Returns: # Returns:
@ -77,6 +103,7 @@ sub get_options_from_array {
"process|pid|p=s" => \$::opt_pid, "process|pid|p=s" => \$::opt_pid,
"suspend|s=s" => \$::opt_suspend, "suspend|s=s" => \$::opt_suspend,
"recheck|t=s" => \$::opt_recheck, "recheck|t=s" => \$::opt_recheck,
"quote|q" => \$::opt_quote,
"help|h" => \$::opt_help, "help|h" => \$::opt_help,
"verbose|v" => \$::opt_verbose, "verbose|v" => \$::opt_verbose,
"version|V" => \$::opt_version, "version|V" => \$::opt_version,
@ -88,11 +115,13 @@ sub get_options_from_array {
return @retval; return @retval;
} }
sub die_usage { sub die_usage {
help(); help();
exit 1; exit 1;
} }
sub help { sub help {
print q{ print q{
Usage: Usage:
@ -102,6 +131,22 @@ Usage:
}; };
} }
sub die_bug {
my $bugid = shift;
print STDERR
("$Global::progname: This should not happen. You have found a bug.\n",
"Please contact <parallel\@gnu.org> and include:\n",
"* The version number: $Global::version\n",
"* The bugid: $bugid\n",
"* The command line being run\n",
"* The files being read (put the files on a webserver if they are big)\n",
"\n",
"If you get the error on smaller/fewer files, please include those instead.\n");
exit(255);
}
sub usleep { sub usleep {
# Sleep this many milliseconds. # Sleep this many milliseconds.
my $secs = shift; my $secs = shift;
@ -109,12 +154,14 @@ sub usleep {
select(undef, undef, undef, $secs/1000); select(undef, undef, undef, $secs/1000);
} }
sub debug { sub debug {
if($::opt_debug) { if($::opt_debug) {
print STDERR @_; print STDERR @_;
} }
} }
sub my_dump { sub my_dump {
# Returns: # Returns:
# ascii expression of object if Data::Dump(er) is installed # ascii expression of object if Data::Dump(er) is installed
@ -138,6 +185,7 @@ sub my_dump {
} }
} }
sub version { sub version {
# Returns: N/A # Returns: N/A
print join("\n", print join("\n",
@ -152,6 +200,7 @@ sub version {
); );
} }
sub multiply_binary_prefix { sub multiply_binary_prefix {
# Evalualte numbers with binary prefix # Evalualte numbers with binary prefix
# 13G = 13*1024*1024*1024 = 13958643712 # 13G = 13*1024*1024*1024 = 13958643712
@ -169,6 +218,7 @@ sub multiply_binary_prefix {
return $s; return $s;
} }
sub max { sub max {
# Returns: # Returns:
# Maximum value of array # Maximum value of array
@ -182,30 +232,6 @@ sub max {
return $max; return $max;
} }
my $limit = Limit->new();
my $process = Process->new($::opt_nice,@ARGV);
if(not $::opt_pid) {
# Wait until limit is below start_limit and run_limit
while($limit->over_start_limit()
or
($limit->hard() and $limit->over_run_limit())) {
$limit->sleep_for_recheck();
}
}
$process->start();
while($process->is_running()) {
if($limit->over_run_limit()) {
$process->suspend();
$limit->sleep_for_recheck();
if(not $limit->hard()) {
$process->resume();
$limit->sleep_while_running();
}
} else {
$process->resume();
$limit->sleep_while_running();
}
}
package Process; package Process;
@ -222,6 +248,7 @@ sub new {
}, ref($class) || $class; }, ref($class) || $class;
} }
sub start { sub start {
# Start the program # Start the program
my $self = shift; my $self = shift;
@ -240,12 +267,17 @@ sub start {
setpgrp(0,0); setpgrp(0,0);
::debug("Child pid: $$, pgrp: ",getpgrp $$,"\n"); ::debug("Child pid: $$, pgrp: ",getpgrp $$,"\n");
::debug("@{$self->{'command'}}\n"); ::debug("@{$self->{'command'}}\n");
system("@{$self->{'command'}}"); if($::opt_quote) {
system(@{$self->{'command'}});
} else {
system("@{$self->{'command'}}");
}
::debug("Child exit\n"); ::debug("Child exit\n");
exit; exit;
} }
} }
use POSIX ":sys_wait_h"; use POSIX ":sys_wait_h";
sub REAPER { sub REAPER {
@ -256,12 +288,14 @@ sub REAPER {
$SIG{CHLD} = \&REAPER; # install *after* calling waitpid $SIG{CHLD} = \&REAPER; # install *after* calling waitpid
} }
sub kill_child_CONT { sub kill_child_CONT {
my $self = $Global::process; my $self = $Global::process;
::debug("SIGCONT received. Killing $self->{'pid'}\n"); ::debug("SIGCONT received. Killing $self->{'pid'}\n");
kill CONT => -getpgrp($self->{'pid'}); kill CONT => -getpgrp($self->{'pid'});
} }
sub kill_child_TSTP { sub kill_child_TSTP {
my $self = $Global::process; my $self = $Global::process;
::debug("SIGTSTP received. Killing $self->{'pid'} and self\n"); ::debug("SIGTSTP received. Killing $self->{'pid'} and self\n");
@ -269,6 +303,7 @@ sub kill_child_TSTP {
kill STOP => -$$; kill STOP => -$$;
} }
sub kill_child_INT { sub kill_child_INT {
my $self = $Global::process; my $self = $Global::process;
::debug("SIGINT received. Killing $self->{'pid'} Exit\n"); ::debug("SIGINT received. Killing $self->{'pid'} Exit\n");
@ -276,6 +311,7 @@ sub kill_child_INT {
exit; exit;
} }
sub resume { sub resume {
my $self = shift; my $self = shift;
if(not $self->{'running'}) { if(not $self->{'running'}) {
@ -285,6 +321,7 @@ sub resume {
} }
} }
sub suspend { sub suspend {
my $self = shift; my $self = shift;
if($self->{'running'}) { if($self->{'running'}) {
@ -294,6 +331,7 @@ sub suspend {
} }
} }
sub is_running { sub is_running {
# The process is dead if none of the pids exist # The process is dead if none of the pids exist
my $self = shift; my $self = shift;
@ -342,6 +380,7 @@ sub new {
}, ref($class) || $class; }, ref($class) || $class;
} }
sub over_run_limit { sub over_run_limit {
my $self = shift; my $self = shift;
my $status = 0; my $status = 0;
@ -351,7 +390,6 @@ sub over_run_limit {
# 50% available => 1 (2-1) # 50% available => 1 (2-1)
# 10% available => 9 (10-1) # 10% available => 9 (10-1)
my $mem = $self->mem_status(); my $mem = $self->mem_status();
# $status += (::max(1,$self->{'runmem'}/$mem)-1)*10;
::debug("Run memory: $self->{'runmem'}/$mem\n"); ::debug("Run memory: $self->{'runmem'}/$mem\n");
$status += (::max(1,$self->{'runmem'}/$mem)-1); $status += (::max(1,$self->{'runmem'}/$mem)-1);
} }
@ -389,7 +427,6 @@ sub over_start_limit {
# 50% available => 1 (2-1) # 50% available => 1 (2-1)
# 10% available => 9 (10-1) # 10% available => 9 (10-1)
my $mem = $self->mem_status(); my $mem = $self->mem_status();
# $status += (::max(1,$self->{'startmem'}/$mem)-1)*10;
::debug("Start memory: $self->{'startmem'}/$mem\n"); ::debug("Start memory: $self->{'startmem'}/$mem\n");
$status += (::max(1,$self->{'startmem'}/$mem)-1); $status += (::max(1,$self->{'startmem'}/$mem)-1);
} }
@ -418,46 +455,19 @@ sub over_start_limit {
return $self->{'over_start_limit'}; return $self->{'over_start_limit'};
} }
sub __over_start_limit {
my $self = shift;
my $status = 0;
if($self->{'startmem'}) {
# mem should be between 0-10ish
# 100% available => 0 (1-1)
# 50% available => 1 (2-1)
# 10% available => 9 (10-1)
my $mem = $self->mem_status();
# $status += (::max(1,$self->{'startmem'}/$mem)-1)*10;
$status += (::max(1,$self->{'startmem'}/$mem)-1);
}
$self->{'over_start_limit'} = $status;
if(not $::opt_recheck) {
# Wait at least 0.5s. Otherwise niceload might cause the load
$self->{'recheck'} = $self->{'factor'} * $self->{'over_start_limit'};
}
::debug("over_start_limit: $status\n");
return $self->{'over_start_limit'};
}
sub __over_general_limit {
# Return:
# 0 if under all limits
# >0 if over limit
my $self = shift;
my $status = 0;
return $status;
}
sub hard { sub hard {
my $self = shift; my $self = shift;
return $self->{'hard'}; return $self->{'hard'};
} }
sub verbose { sub verbose {
my $self = shift; my $self = shift;
return $self->{'verbose'}; return $self->{'verbose'};
} }
sub sleep_for_recheck { sub sleep_for_recheck {
my $self = shift; my $self = shift;
if($self->{'recheck'} < 0.5) { if($self->{'recheck'} < 0.5) {
@ -472,6 +482,7 @@ sub sleep_for_recheck {
::usleep(1000*$self->{'recheck'}); ::usleep(1000*$self->{'recheck'});
} }
sub sleep_while_running { sub sleep_while_running {
my $self = shift; my $self = shift;
::debug("check in $self->{'runtime'}s\n"); ::debug("check in $self->{'runtime'}s\n");
@ -483,6 +494,7 @@ sub sleep_while_running {
::usleep(1000*$self->{'runtime'}); ::usleep(1000*$self->{'runtime'});
} }
sub load_status { sub load_status {
# Returns: # Returns:
# loadavg # loadavg
@ -497,6 +509,7 @@ sub load_status {
return $self->{'load_status'}; return $self->{'load_status'};
} }
sub load_status_linux { sub load_status_linux {
my ($loadavg); my ($loadavg);
if(open(IN,"/proc/loadavg")) { if(open(IN,"/proc/loadavg")) {
@ -505,7 +518,7 @@ sub load_status_linux {
if($upString =~ m/^(\d+\.\d+)/) { if($upString =~ m/^(\d+\.\d+)/) {
$loadavg = $1; $loadavg = $1;
} else { } else {
die; ::die_bug("proc_loadavg");
} }
close IN; close IN;
} elsif (open(IN,"uptime|")) { } elsif (open(IN,"uptime|")) {
@ -513,13 +526,14 @@ sub load_status_linux {
if($upString =~ m/average.\s*(\d+\.\d+)/) { if($upString =~ m/average.\s*(\d+\.\d+)/) {
$loadavg = $1; $loadavg = $1;
} else { } else {
die; ::die_bug("uptime");
} }
close IN; close IN;
} }
return $loadavg; return $loadavg;
} }
sub swap_status { sub swap_status {
# Returns: # Returns:
# (swap in)*(swap out) kb # (swap in)*(swap out) kb
@ -535,6 +549,7 @@ sub swap_status {
return $self->{'swap_status'}; return $self->{'swap_status'};
} }
sub swap_status_linux { sub swap_status_linux {
my $swap_activity; my $swap_activity;
$swap_activity = "vmstat 1 2 | tail -n1 | awk '{print \$7*\$8}'"; $swap_activity = "vmstat 1 2 | tail -n1 | awk '{print \$7*\$8}'";
@ -542,6 +557,7 @@ sub swap_status_linux {
return qx{ $swap_activity }; return qx{ $swap_activity };
} }
sub mem_status { sub mem_status {
# Returns: # Returns:
# number of bytes (free+cache) # number of bytes (free+cache)
@ -556,6 +572,7 @@ sub mem_status {
return $self->{'mem_status'}; return $self->{'mem_status'};
} }
sub mem_status_linux { sub mem_status_linux {
# total used free shared buffers cached # total used free shared buffers cached
# Mem: 3366496 2901664 464832 0 179228 1850692 # Mem: 3366496 2901664 464832 0 179228 1850692
@ -566,6 +583,7 @@ sub mem_status_linux {
return $free*1024; return $free*1024;
} }
sub io_status { sub io_status {
# Returns: # Returns:
# max percent for all devices # max percent for all devices
@ -576,21 +594,16 @@ sub io_status {
$self->{'io_status'} = io_status_linux(); $self->{'io_status'} = io_status_linux();
$self->{'io_status_cache_time'} = time; $self->{'io_status_cache_time'} = time;
} }
::debug("io_status: $self->{'io_status'}\n");
return $self->{'io_status'}; return $self->{'io_status'};
} }
sub io_status_linux { sub io_status_linux {
# Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util # Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
# sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 # sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
# sdb 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
# sdd 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
# sde 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
# sdf 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
# dm-0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
# sdg 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
my @iostat_out = `LANG=C iostat -x 1 2`; my @iostat_out = `LANG=C iostat -x 1 2`;
# throw away all execpt the last Device:-section # throw away all execpt the last Device:-section
# print @iostat_out;
my @iostat; my @iostat;
for(reverse @iostat_out) { for(reverse @iostat_out) {
/Device:/ and last; /Device:/ and last;
@ -599,6 +612,3 @@ sub io_status_linux {
my $io = ::max(@iostat); my $io = ::max(@iostat);
return $io/10; return $io/10;
} }
# Keep -w happy
# = 1;

View file

@ -4,16 +4,19 @@ niceload - slow down a program when the load average is above a certain limit
=head1 SYNOPSIS =head1 SYNOPSIS
B<niceload> [-v] [-n nice] [-L load] [-t time] [-s time|-f factor] command B<niceload> [-v] [-h] [-n nice] [-I io] [-L load] [-M mem] [-N]
[-t time] [-s time|-f factor] ( command | -p PID )
B<niceload> [-v] [-h] [-n nice] [-l load] [-t time] [-s time|-f factor] -p=PID
=head1 DESCRIPTION =head1 DESCRIPTION
GNU B<niceload> will slow down a program when the load average is GNU B<niceload> will slow down a program when the load average (or
above a certain limit. When the limit is reached the program will be other system activity) is above a certain limit. When the limit is
suspended for some time. Then resumed again for some time. Then the reached the program will be suspended for some time. Then resumed
load average is checked again and we start over. again for some time. Then the load average is checked again and we
start over.
Instead of load average B<niceload> can also look at disk I/O, amount
of free memory, or swapping activity.
If the load is 3.00 then the default settings will run a program If the load is 3.00 then the default settings will run a program
like this: like this:
@ -102,6 +105,14 @@ Sets niceness. See B<nice>(1).
Process ID of process to suspend. Process ID of process to suspend.
=item B<--quote>
=item B<-q>
Quote the command line. Useful if the command contains chars like *,
$, >, and " that should not be interpreted by the shell.
=item B<--run-io> I<iolimit> =item B<--run-io> I<iolimit>
=item B<--ri> I<iolimit> =item B<--ri> I<iolimit>
@ -134,15 +145,6 @@ Start limit. The program will not start until the system is below the
limit. See: B<--io>, B<--load>, B<--mem>, B<--noswap>. limit. See: B<--io>, B<--load>, B<--mem>, B<--noswap>.
=item B<--suspend> I<SEC>
=item B<-s> I<SEC>
Suspend time. Suspend the command this many seconds when the max load
average is reached.
=item B<--soft> =item B<--soft>
=item B<-S> =item B<-S>
@ -152,6 +154,14 @@ let it run for a second thus only slowing down a process while the
system is over one of the given limits. This is the default. system is over one of the given limits. This is the default.
=item B<--suspend> I<SEC>
=item B<-s> I<SEC>
Suspend time. Suspend the command this many seconds when the max load
average is reached.
=item B<--recheck> I<SEC> =item B<--recheck> I<SEC>
=item B<-t> I<SEC> =item B<-t> I<SEC>