jfh@rpp386.cactus.org (John F. Haugh II) (11/26/89)
In article <17357@rpp386.cactus.org> root@rpp386.cactus.org (John F. Haugh II) writes: >this little program has been kicked around alt.sources a few >times and i thought i'd take a whack at it. and after a day of using it i realized i should have waited until i'd used it longer before posting ... >main(argc, argv) > char **argv; >{ > int i, n, n_run, n_disk; > int mypid = getpid(); this initialization of mypid needs to be moved after the fork() > signal (SIGHUP, SIG_IGN); > if (fork ()) > exit (0); > > setpgrp (); right here, like this > mypid = getpid (); this gave me a semi-permanent load average of 1.0 with nothing running. -- John F. Haugh II +-Things you didn't want to know:------ VoiceNet: (512) 832-8832 Data: -8835 | The real meaning of IBM is ... InterNet: jfh@rpp386.cactus.org | ... I've Been to a Meeting. UUCPNet: {texbell|bigtex}!rpp386!jfh +--<><--<><--<><--<><--<><--<><--Yea!--
root@rpp386.cactus.org (John F. Haugh II) (12/15/89)
this little program has been kicked around alt.sources a few times and i thought i'd take a whack at it. it reads the process table at one second intervals and computes the load average from the status of the various processes. the previous version worked just fine, except it counted processes sleeping on FIFOs in the load average, as well as counting itself. on my system that gave a permanent load average of 3+ with NO active processes ... the fix was to ignore processes sleeping on disk i/o above PZERO and to ignore the load average daemon itself. that immediately improved the numbers. as a freebie i moved certain loop-invariant statements out of the main loop and added code to put the process into the background with hangup disabled. so, without further fanfare, here it is ... --------------------- cut here ------------------------ #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # Makefile # lload.c # This archive created: Sat Nov 25 10:35:26 1989 # By: John F. Haugh II (River Parishes Programming, Plano TX) export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else cat << \SHAR_EOF > 'Makefile' SHELL = /bin/sh # # Load average daemon # # Xenix/386 flags CFLAGS = -Ox -DM_XENIX LIBS = -lx -lm # Normal flags # CFLAGS = -O # LIBS = -lm lload: lload.o cc -o lload lload.o $(LIBS) install: lload cp lload /etc touch /etc/loadav chown sysinfo /etc/lload /etc/loadav chgrp sysinfo /etc/lload /etc/loadav chmod 4700 /etc/lload chmod 644 /etc/loadav clean: rm -f lload.o core a.out clobber: clean rm -f lload SHAR_EOF fi if test -f 'lload.c' then echo shar: "will not over-write existing file 'lload.c'" else cat << \SHAR_EOF > 'lload.c' /* * Load Average deamon. * * The load average is updated every constant time interval, and the result * written to a file as 3 double values. * * The load average is for the last 1, 5 and 15 minutes (3 values). * * A second argument (-v) will set the program in a verbose mode, writing * the load average to the standard output and not to the file. * * Preferably start this process from the inittab. It needs special * priviledges to read from /dev/kmem. * * The following processes are regarded as "runnning": * A process that has the SRUN status (simply running). * A process that is being created (SIDLE). * A process that is being swapped out (SXBRK). * A process that waits for disk I/O. * * A process is regarded as waiting for disk I/O if it is SSLEEP and * has wchan set to a buf in the buffer pool and it has a priority * below PZERO. * * The sleep is implemented using poll on a stream device, not the * more usual sleep() call. Why ? * Because you do not want to wake up simultaneosly with other programs * doing sleep(), which might give wrong load average. * Of course, if you do not have the stream pipe device, use the normal * sleep(). */ /* For Xenix: compile as follows: cc -Ox lload.c -lm -lx -o lload */ /* For Xenix 286: compile as follows: cc -M2e -i -Ox lload.c -lm -lx -o lload */ /* Modified for Xenix by Sanford Zelkovitz XBBS 714-898-8634 */ /* Modified for Xenix 286 by Bodo Rueskamp, <br@unido.uucp> */ /* Modified to ignore sleeps on FIFOs by John F. Haugh II, jfh@rpp386 */ #include <fcntl.h> #include <signal.h> #ifdef M_XENIX #include <sys/a.out.h> #else #include <nlist.h> #endif #include <stdio.h> #ifndef M_XENIX #include <stropts.h> #include <poll.h> #endif #include <math.h> #include <sys/types.h> #include <sys/param.h> #include <sys/buf.h> #ifdef M_XENIX #include <sys/page.h> #else #include <sys/immu.h> #include <sys/region.h> #endif #include <sys/var.h> #include <sys/proc.h> /* #define DEBUG */ /* Will append all values in a debug file */ #define LOADAV "/etc/loadav" /* Where to write the load avarge */ #define STREAM_PIPE "/dev/spx" /* Used for polling with timeout */ /* * You may or may not need the '_' in the following names. */ #define VAR_NAME "_v" #ifdef M_I386 #define BUF_NAME "_pbuf" #else #define BUF_NAME "_buf" #endif #define PROC_NAME "_proc" struct nlist nl[] = { {VAR_NAME}, {BUF_NAME}, {PROC_NAME}, {0}, }; int loadfile; /* file descr to result file */ struct proc *p; int p_max = 0; struct var v; int kmem; struct nlist *v_p, *proc_p, *buf_p; int size; int first_buf, last_buf; int sleeping = 1; /* Poll frequency in seconds */ int verbose = 0; double av[3] = { 0.0, 0.0, 0.0 }; /* The loadaverage */ double apa[3]; /* Holding constants */ main(argc, argv) char **argv; { int i, n, n_run, n_disk; int mypid = getpid(); #ifdef DEBUG int debug_fd; char buff[100]; debug_fd = open("/tmp/loadavdebug", O_CREAT|O_WRONLY, 0600); #endif if (argc == 2 && strcmp(argv[1], "-v") == 0) { verbose = 1; printf("Verbose\n"); } kmem = open("/dev/kmem", O_RDONLY); if (kmem == -1) { perror("/dev/kmem"); exit(1); } if (!verbose) { loadfile = open(LOADAV, O_RDWR|O_CREAT,0664); if (loadfile == -1) { fprintf(stderr, "%s:", argv[0]); perror(LOADAV); exit(1); } } #ifdef M_XENIX if (nlist("/xenix", nl) == -1) { #else if (nlist("/unix", nl) == -1) { #endif perror("nlist"); exit(1); } for (i=0; nl[i].n_name; i++) { #ifdef M_XENIX if(nl[i].n_name[0] == '\0') break; #endif #ifdef DEBUG fprintf(stderr, "nl[%d] = %s\n", i, nl[i].n_name); #endif if (nl[i].n_value == 0) { fprintf(stderr, "Could not get address for %s\n", nl[i].n_name); exit(1); } if (strcmp(nl[i].n_name, VAR_NAME) == 0) v_p = &nl[i]; if (strcmp(nl[i].n_name, PROC_NAME) == 0) proc_p = &nl[i]; if (strcmp(nl[i].n_name, BUF_NAME) == 0) buf_p = &nl[i]; } /* * Setup the constants used for computing load average. */ apa[0] = exp(-sleeping/60.0); apa[1] = exp(-sleeping/300.0); apa[2] = exp(-sleeping/900.0); /* * Read struct var for buffer addresses and process table info. */ if (lseek(kmem, (long) v_p->n_value, 0) == -1) { perror("lseek v"); exit(1); } if (read(kmem, &v, sizeof v) == -1) { perror("read v"); exit(1); } /* * Setup the bounds of the system buffers */ first_buf = buf_p->n_value; last_buf = first_buf + v.v_buf * sizeof (struct buf); /* * Setup the process table */ p_max = v.v_proc; if (! (p = (struct proc *) malloc ((unsigned) (p_max * sizeof *p)))) { perror ("malloc p"); exit (1); } /* * Put myself into the background */ signal (SIGHUP, SIG_IGN); if (fork ()) exit (0); setpgrp (); /* * Start looping */ while(1) { /* * Read the 'v' structure every time. It says how * many procs are used. */ if (lseek(kmem, (long) v_p->n_value, 0) == -1) { perror("lseek v"); exit(1); } if (read(kmem, &v, sizeof v) != sizeof v) { perror("read v"); exit(1); } size = (struct proc *)v.ve_proc - (struct proc *)proc_p->n_value; if (lseek(kmem, (long) proc_p->n_value, 0) == -1) { perror("lseek proc"); exit(1); } n = read(kmem, p, size * sizeof (struct proc)); if (n != size * sizeof (struct proc)) { if (n == -1) { perror("read procs"); exit(1); } fprintf(stderr, "Could only read %d (%d) procs\n", n, size); size = n / sizeof (struct proc); } n_run = 0; n_disk = 0; for (i=0; i<size; i++) { if (p[i].p_pid == mypid) continue; if (p[i].p_stat == SRUN || p[i].p_stat == SIDL || p[i].p_stat == SXBRK) n_run++; else if (p[i].p_stat == SSLEEP && p[i].p_pri < PZERO && (unsigned int)p[i].p_wchan >= first_buf && (unsigned int)p[i].p_wchan < last_buf) { n_disk++; } } /* * Update the load average using a decay filter. */ for (i = 0; i < 3; i++) av[i] = apa[i] * av[i] + (n_run + n_disk) * (1.0 - apa[i]); if (!verbose) { if (lseek(loadfile, 0L, 0) == -1) { fprintf(stderr, "Couldn't seek in %s\n", LOADAV); exit(1); } if (write(loadfile, (char *)av, sizeof av) != sizeof av) { perror(argv[0]); exit(1); } } else printf("(%d %d) %f %f %f\n", n_run, n_disk, av[0], av[1], av[2]); #ifdef DEBUG sprintf(buff, "(%d %d) %4.2f\n", n_run, n_disk, av[0]); write(debug_fd, buff, strlen(buff)); #endif (void)nap(sleeping * 1000L); } } #ifndef M_XENIX /* * Use a stream pipe to implement a sleep. * We have a stream pipe for ourselves, so we know noone will write * on it. */ nap(milli) { static int fd = 0; static struct pollfd pollfd; if (fd == 0) { fd = open(STREAM_PIPE, 0); if (fd == -1) { perror(STREAM_PIPE); exit(1); } pollfd.fd = fd; pollfd.events = POLLIN; } if (poll(&pollfd, 1, milli) == -1) { perror("nap: poll"); exit(1); } if (pollfd.revents != 0) { fprintf(stderr, "nap: poll: got something\n"); exit(1); } } #endif SHAR_EOF fi exit 0 # End of shell archive