/*
* Copyright (C) 2020 Sergio García-Cuevas González
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* load-open
* Control the load average in an open-loop fashion.
* The control loop cycles 10 times per second,
* alternating between high CPU usage and low CPU
* usage to reach the desired load.
*/
#include
#include
#include
#include
#include
#include
const int cycles_per_second = 10;
const double nanoseconds_per_cycle = 1e9 / cycles_per_second;
/*
* Return the number of system clocks the controller will stay
* at the high CPU usage part of the control cycle.
*/
clock_t high_cycle_clocks(double control_action)
{
if (control_action < 0)
{
return 0;
}
if (control_action > 1)
{
return CLOCKS_PER_SEC / cycles_per_second;
}
return (CLOCKS_PER_SEC / cycles_per_second) * control_action;
}
/*
* Return the number of nanoseconds the controller will stay
* at the low CPU usage part of the control cycle.
*/
long low_cycle_nanoseconds(double control_action)
{
if (control_action < 0)
{
return nanoseconds_per_cycle;
}
if (control_action > 1)
{
return 0;
}
return nanoseconds_per_cycle * (1 - control_action);
}
/*
* High CPU usage part of the control cycle.
*/
void high_cycle(double control_action)
{
const clock_t start_clock = clock();
const clock_t high_clocks = high_cycle_clocks(control_action);
while ((clock() - start_clock) < high_clocks)
{
}
}
/*
* Low CPU usage of the control cycle.
*/
void low_cycle(double control_action)
{
const struct timespec sleep_time = {
.tv_sec = 0,
.tv_nsec = low_cycle_nanoseconds(control_action)
};
(void) nanosleep(&sleep_time, NULL);
}
/*
* Control the system load with an open control loop.
*/
void load_control_loop(double target_load)
{
const double control_action = target_load / (1 + (int) target_load);
while (1)
{
high_cycle(control_action);
low_cycle(control_action);
}
}
/*
* Print usage instructions to the standard error output.
*/
void print_usage(char *program_name)
{
(void) fprintf(stderr, "Usage: %s TARGET_LOAD\n", program_name);
(void) fprintf(stderr, "load-open: control the load average with an open control loop.\n");
(void) fprintf(stderr, "Copyright (C) 2020 Sergio García-Cuevas González\n");
(void) fprintf(stderr, "License GPLv3+: GNU GPL version 3 or later .\n");
(void) fprintf(stderr, "This is free software: you are free to change and redistribute it.\n");
(void) fprintf(stderr, "There is NO WARRANTY, to the extent permitted by law.\n");
}
/*
* Entry point.
*/
int main(int argc, char *argv[])
{
if (argc != 2)
{
print_usage(argv[0]);
return EXIT_FAILURE;
}
const double target_load = atof(argv[1]);
for (int i = 0; i < (int) target_load; i++)
{
const pid_t pid = fork ();
if (pid == 0)
{
load_control_loop(target_load);
}
}
load_control_loop(target_load);
return EXIT_SUCCESS;
}