lars@myab.se (Lars Pensj|) (05/25/89)
A program (background deamon) that computes the load average on a SYSV.3 machine. Probably easy to port to SYSV.2. #--------CUT---------CUT---------CUT---------CUT--------# ######################################################### # # # This is a shell archive file. To extract files: # # # # 1) Make a directory for the files. # # 2) Write a file, such as "file.shar", containing # # this archive file into the directory. # # 3) Type "sh file.shar". Do not use csh. # # # ######################################################### # # echo Extracting lload.c: sed 's/^Z//' >lload.c <<\STUNKYFLUFF Z/* Z * Load Average deamon. Z * Z * The load average is updated every constant time interval, and the result Z * written to a file as 3 double values. Z * Z * The load average is for the last 1, 5 and 15 minutes (3 values). Z * Z * A second argument (-v) will set the program in a verbose mode, writing Z * the load average to the standard output and not to the file. Z * Z * Preferably start this process from the inittab. It needs special Z * priviledges to read from /dev/kmem. Z * Z * The following processes are regarded as "runnning": Z * A process that has the SRUN status (simply running). Z * A process that is being created (SIDLE). Z * A process that is being swapped out (SXBRK). Z * A process that waits for disk I/O. Z * Z * A process is regarded as waiting for disk I/O if it is SSLEEP and Z * has wchan set to a buf in the buffer pool. Z * Z * The sleep is implemented using poll on a stream device, not the Z * more usual sleep() call. Why ? Z * Because you do not want to wake up simultaneosly with other programs Z * doing sleep(), which might give wrong load average. Z * Of course, if you do not have the stream pipe device, use the normal Z * sleep(). Z */ Z Z#include <fcntl.h> Z#include <nlist.h> Z#include <stdio.h> Z#include <stropts.h> Z#include <poll.h> Z#include <math.h> Z#include <sys/types.h> Z#include <sys/param.h> Z#include <sys/buf.h> Z#include <sys/immu.h> Z#include <sys/region.h> Z#include <sys/var.h> Z#include <sys/proc.h> Z Z/* #define DEBUG */ /* Will append all values in a debug file */ Z Z#define LOADAV "/etc/loadav" /* Where to write the load avarge */ Z#define STREAM_PIPE "/dev/spx" /* Used for polling with timeout */ Z Z/* Z * You may or may not need the '_' in the following names. Z */ Z Z#define VAR_NAME "_v" Z#define BUF_NAME "_buf" Z#define PROC_NAME "_proc" Z Zstruct nlist nl[] = { Z {VAR_NAME}, Z {BUF_NAME}, Z {PROC_NAME}, Z {0}, Z}; Z Zint loadfile; /* file descr to result file */ Zstruct proc *p; Zstruct var v; Zint kmem; Zstruct nlist *v_p, *proc_p, *buf_p; Zint size; Zint first_buf, last_buf; Zint sleeping = 1; /* Poll frequency in seconds */ Zint verbose = 0; Z Zdouble av[3] = { 0.0, 0.0, 0.0 }; /* The loadaverage */ Zdouble apa[3]; /* Holding constants */ Z Zmain(argc, argv) Z char **argv; Z{ Z int i, n, n_run, n_disk; Z#ifdef DEBUG Z int debug_fd; Z char buff[100]; Z debug_fd = open("/tmp/loadavdebug", O_CREAT|O_WRONLY); Z#endif Z Z if (argc == 2 && strcmp(argv[1], "-v") == 0) { Z verbose = 1; Z printf("Verbose\n"); Z } Z kmem = open("/dev/kmem", 0); Z if (kmem == -1) { Z perror("/dev/kmem"); Z exit(1); Z } Z if (!verbose) { Z loadfile = open(LOADAV, 1|O_CREAT,0664); Z if (loadfile == -1) { Z fprintf(stderr, "%s:", argv[0]); Z perror(LOADAV); Z exit(1); Z } Z } Z if (nlist("/unix", nl) == -1) { Z perror("nlist"); Z exit(1); Z } Z for (i=0; nl[i].n_name; i++) { Z if (nl[i].n_value == 0) { Z fprintf(stderr, "Could not get address for %s\n", nl[i].n_name); Z exit(1); Z } Z if (strcmp(nl[i].n_name, VAR_NAME) == 0) Z v_p = &nl[i]; Z if (strcmp(nl[i].n_name, PROC_NAME) == 0) Z proc_p = &nl[i]; Z if (strcmp(nl[i].n_name, BUF_NAME) == 0) Z buf_p = &nl[i]; Z } Z /* Z * Setup the constants used for computing load average. Z */ Z apa[0] = exp(-sleeping/60.0); Z apa[1] = exp(-sleeping/300.0); Z apa[2] = exp(-sleeping/900.0); Z /* Z * Start looping Z */ Z while(1) { Z /* Z * Read the 'v' structure every time. It says how Z * many procs are used. Z */ Z if (lseek(kmem, v_p->n_value, 0) == -1) { Z perror("lseek v"); Z exit(1); Z } Z if (read(kmem, &v, sizeof v) == -1) { Z perror("read v"); Z exit(1); Z } Z size = (struct proc *)v.ve_proc - (struct proc *)proc_p->n_value; Z first_buf = buf_p->n_value; Z last_buf = first_buf + v.v_buf * sizeof (struct buf); Z if (lseek(kmem, proc_p->n_value, 0) == -1) { Z perror("lseek proc"); Z exit(1); Z } Z p = (struct proc *)malloc(size * sizeof (struct proc)); Z if (p == 0) { Z fprintf(stderr, "Could not malloc %d bytes\n", Z size * sizeof (struct proc)); Z exit(1); Z } Z n = read(kmem, p, size * sizeof (struct proc)); Z if (n != size * sizeof (struct proc)) { Z if (n == -1) { Z perror("read procs"); Z exit(1); Z } Z fprintf(stderr, "Could only read %d (%d) procs\n", Z n, size); Z size = n / sizeof (struct proc); Z } Z n_run = 0; Z n_disk = 0; Z for (i=0; i<size; i++) { Z if (p[i].p_stat == SRUN || p[i].p_stat == SIDL || Z p[i].p_stat == SXBRK) Z n_run++; Z else if (p[i].p_stat == SSLEEP && Z (unsigned int)p[i].p_wchan >= first_buf && Z (unsigned int)p[i].p_wchan < last_buf) Z n_disk++; Z } Z /* Z * Update the load average using a decay filter. Z */ Z for (i = 0; i < 3; i++) Z av[i] = apa[i] * av[i] + (n_run + n_disk) * (1.0 - apa[i]); Z if (!verbose) { Z if (lseek(loadfile, 0L, 0) == -1) { Z fprintf(stderr, "Couldn't seek in %s\n", LOADAV); Z exit(1); Z } Z if (write(loadfile, (char *)av, sizeof av) != sizeof av) { Z perror(argv[0]); Z exit(1); Z } Z } else Z printf("(%d %d) %f %f %f\n", n_run, n_disk, Z av[0], av[1], av[2]); Z#ifdef DEBUG Z sprintf(buff, "(%d %d) %4.2f\n", n_run, n_disk, Z av[0]); Z write(debug_fd, buff, strlen(buff)); Z#endif Z nap(sleeping * 1000); Z free(p); Z } Z} Z Z/* Z * Use a stream pipe to implement a sleep. Z * We have a stream pipe for ourselves, so we know noone will write Z * on it. Z */ Znap(milli) { Z static int fd = 0; Z static struct pollfd pollfd; Z Z if (fd == 0) { Z fd = open(STREAM_PIPE, 0); Z if (fd == -1) { Z perror(STREAM_PIPE); Z exit(1); Z } Z pollfd.fd = fd; Z pollfd.events = POLLIN; Z } Z if (poll(&pollfd, 1, milli) == -1) { Z perror("nap: poll"); Z exit(1); Z } Z if (pollfd.revents != 0) { Z fprintf(stderr, "nap: poll: got something\n"); Z exit(1); Z } Z} STUNKYFLUFF set `wc lload.c` if test 231 != $1 then echo lload.c: Checksum error. Is: $1, should be: 231. fi echo All done exit 0 -- Lars Pensj| lars@myab.se