Loadmin

Loadmin is a program I wrote which suspends a process when the load average of the system raises above a certain level and continues the process when the load gets lower.

Suspending a process

A process can be suspended by sending it the signal SIGSTOP. This sets the status in the process table to TASK_STOPPED, which lets the process scheduler know that this process is not to be run. A SIGCONT signal puts the state back to TASK_INTERRUPTIBLE, which tells the scheduler that this process is ready to be run. When it is running, its state is TASK_RUNNING. The C code to suspend and resume a process is as follows:

#include <sys/types.h>
#include <signal.h>

// suspend the process (same as hitting ctrl-z)
kill(pid, SIGSTOP);

// continue the process
kill(pid, SIGCONT);

Determining the load

When you run uptime, it shows (among other things) the load average. These three numbers give the load average in 1 minute, 5 minutes and 15 minutes. The load average is the average number of processes which are waiting to be run. These numbers are load average figures giving the number of jobs in the run queue (state R) or waiting for disk I/O (state D) averaged over 1, 5, and 15 minutes.

The load average figures are also available from the special file /proc/loadavg. The load average can be read with the following C function:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

double getload() {
	int fd;
	int n;
	char buf[16];
	double load;
	char* loadavgfile = "/proc/loadavg";
	
	fd = open(loadavgfile, O_RDONLY);
	if (fd < 0) {
		printf("can't open %s, %m", loadavgfile);
		return -1;
	}
	
	n = read(fd, buf, sizeof(buf) - 1);
	if (n < 0) {
		printf("can't read %s, %m", loadavgfile);
		return -1;
	}
	
	if (sscanf(buf, "%lf", &load) != 1) {
		printf("can't scanf load, \"%s\"", buf);
		return -1;
	}

	return load;
}

Suspending a process on high load

The problem I was facing was that I had to run a long-running, resource intensive program. At the same time, the load should not get too high, because the server I was running it on had other things to do. My solution is to suspend the process when the load gets too high. The code below will do exactly that:

/*
loadmin: a wrapper with a load average limit
Based on ctx-loadmax
Copyright (C) 2000 LibertySurf Telecom
Copyright (C) 2006 Yes-Co Nederland
Licensed under the GPL v2
*/

#include <unistd.h>
#include <stdlib.h>
#include <syslog.h>
#include <assert.h>
#include <sys/wait.h>

#include <stdio.h>
#include <fcntl.h>

#define LOADMAX 1.0

main(int argc, char **argv, char **env) {
	pid_t pid;
	int status;
	
	if (argc < 2) {
		printf("usage: loadmin  [arguments]\n");
		exit(1);
	}
	
	pid = fork();
	if (pid == -1) {
		printf("could not fork\n");
		exit(1);
	}
	if (pid == 0) {
		// child
		execvp(argv[1], argv+1);
		printf("exec failed\n");
		exit(1);
	} else {
		// parent
		do {
			if (lowload()) {
				kill(pid, SIGCONT);
			} else {
				kill(pid, SIGSTOP);
			}
			sleep(10);
		} while (0 == waitpid(pid, &status, WNOHANG));
	}
}

int lowload() {
	int fd;
	int n;
	char buf[16];
	double load;
	char* loadavgfile = "/proc/loadavg";
	
	fd = open(loadavgfile, O_RDONLY);
	if (fd < 0) {
		printf("can't open %s, %m", loadavgfile);
		return -1;
	}
	
	n = read(fd, buf, sizeof(buf) - 1);
	if (n < 0) {
		printf("can't read %s, %m", loadavgfile);
		return -1;
	}
	
	if (sscanf(buf, "%lf", &load) != 1) {
		printf("can't scanf load, \"%s\"", buf);
		return -1;
	}

	return (load < LOADMAX);
}