allbery@ncoast.UUCP (02/11/87)
+--------------- | Can anyone give me a lead on how one can determine the system load | on UNIX System V Release 2? (In particular, this is running on a | 3B2.) It doesn't have to be as elaborate as the 1,5,15 minute averages | available under Berkeley UNIX, but I need something that could be used | to get rough estimates of current busy-ness of the system. (This | would then by called by processes on other network hosts to find the | least-used machine to give a job to.) +--------------- The original form of these routines is From: budd@bu-cs.BU.EDU (Philip Budne). I include his README below. +--------------- | Here is a program I wrote to generate a load average for USG, we run | it on our 3b2s and our 3b5. Of course you might be better off putting | it into the kernal clock.c routines where the sysinf data is generated. | | This is a real T(w)enex (also BSD) style load average, the sysinf | structure (shockingly) provides exactly the needed information. | | The data is stored in a BSD style rwhod packet/file. +--------------- I have modified this program: if RWHOD is defined, it performs its original function of forging rwhod packets; undef'ed, it creates a shm segment whose key is ftok("/unix", 'a') and constantly updates three (double)'s stored in it. The non-RWHOD stuff doesn't need the network hacks the original needed. It is designed to be run as a ((terminology? daemon: dragon)). We typically start it in /etc/rc. BTW, in case you do telinit s/telinit 2, you should ipcrm the shm segment before starting avenrun. WARNING: The file below is NOT a ``shar'' file!!! ++Brandon -----------------------cut, bend, fold, spindle, mutilate--------------------- /* * avenrun.c -- calculate System V load average, post into shm segment * Brandon S. Allbery, TDI * Based on: * ldavg.c -- compute load averages for System V * Phil Budne @ Boston U / DSG * * Forges BSD 4.2 rwhod packets containing system load averages * * My version (no #define RWHOD) uses a shm segment, ftok("/unix", 'a'), * containing three (double)'s, for the 1, 5, and 15 minute load averages. */ # include <sys/types.h> /* system types */ # include <sys/sysinfo.h> /* sysinfo structure */ # include <sys/utsname.h> /* for uname(2) */ # include <sys/stat.h> /* for stat(2) */ # include <sys/param.h> /* for HZ */ # include <stdio.h> # include <nlist.h> # include <time.h> # include <math.h> # include <utmp.h> # include <fcntl.h> #ifdef RWHOD # include "rwhod.h" /* (from BSD) */ #else SHM # include <sys/ipc.h> # include <sys/shm.h> #endif RWHOD /* # define DEBUG /**/ #ifdef RWHOD # define UDP 1 # define DSK 1 # define PMUL 100 # if UDP # include "netdb.h" unsigned short port = 513; unsigned long ipaddr; # endif #endif RWHOD extern struct utmp *getutent(); # define SYSTEM "/unix" # define KMEM "/dev/kmem" struct nlist nl[] = { # define NL_SYSINFO 0 { "_sysinfo" }, /* 0 */ # define NL_LBOLT 1 { "_lbolt" }, /* 1 */ { 0 } }; #ifdef RWHOD struct whod proto; struct utsname utsn; char whopacket[100]; #else SHM key_t aven_key; int aven_shm; double *aven_seg; #endif RWHOD int fd, memfd; char *system = SYSTEM; char *kmem = KMEM; char *argv0; main(argc, argv) int argc; char *argv[]; { switch (fork()) { case -1: perror("fork"); exit(1); case 0: break; default: exit(0); } argv0 = argv[0]; #ifdef RWHOD uname(&utsn); /* get system names */ #endif RWHOD setpgrp(); /* create own pgrp */ init_nlist(); /* get name values, open kmem */ init_packet(); /* initialize packet prototype */ doit(); /* never returns */ } /* main */ init_nlist() { nlist(system, nl); /* get system values */ if(nl[NL_SYSINFO].n_value == 0) { fprintf(stderr, "%s: can't find sysinf structure\n", argv0); exit(1); } /* no value */ if ((memfd = open(kmem, O_RDONLY)) < 0) { fprintf(stderr, "%s: no mem\n", argv0); exit(1); } /* could not open kmem */ } /* init_nlist */ # define PERIOD 5 /* sample period (in seconds) */ # define INTERVAL1 60 /* average interval 1 (in seconds) */ # define INTERVAL2 (5*60) /* average interval 2 (in seconds) */ # define INTERVAL3 (15*60) /* average interval 3 (in seconds) */ # define PACKINTERVAL 30 /* interval for make_packet */ doit() { struct sysinfo sinf; int packt = 0; long occ, que, nocc, nque, n, c; double avg1, avg2, avg3, new; double exp1, exp2, exp3; exp1 = exp( - ((double) PERIOD) / INTERVAL1 ); exp2 = exp( - ((double) PERIOD) / INTERVAL2 ); exp3 = exp( - ((double) PERIOD) / INTERVAL3 ); getsysinf(&sinf); /* prime the pump */ occ = sinf.runocc; /* number of samples */ que = sinf.runque; /* run queue summation */ avg1 = avg2 = avg3 = ((double) que) / occ; for( ; ; ) { if( --packt < 0 ) { #ifdef RWHOD make_packet((int) (avg1 * PMUL), (int) (avg2 * PMUL), (int) (avg3 * PMUL)); #else SHM make_packet(avg1, avg2, avg3); #endif RWHOD packt = PACKINTERVAL / PERIOD; } /* packet time */ /* printf("runque: %ld runocc: %ld\n", que, occ ); /**/ sleep(PERIOD); getsysinf(&sinf); /* get new info */ nocc = sinf.runocc; nque = sinf.runque; n = nocc - occ; /* get number of times updated */ if( n <= 0 ) continue; c = nque - que - n; /* get number of runners w/o us */ if( c < 0 ) c = 0; /* mumble! */ new = ((double) c ) / n; /* new instantaneous avg */ /************************************************/ /* The following formwla is used to achieve */ /* exponential decay of old measurements: */ /* avgN = avgN * expN + new * (1 - expN) */ /* */ /* However, the factorized forms below */ /* require fewer floating point operations. */ /************************************************/ avg1 = ((avg1 - new) * exp1) + new; avg2 = ((avg2 - new) * exp2) + new; avg3 = ((avg3 - new) * exp3) + new; occ = nocc; que = nque; } /* for ever */ } /* doit */ getsysinf(s) struct sysinfo *s; { l_lseek(memfd, (long)nl[NL_SYSINFO].n_value, 0); r_read(memfd, (char *)s, sizeof(struct sysinfo)); } /* lseek with error checking */ l_lseek(fd, offset, whence) int fd, whence; long offset; { if (lseek(fd, offset, whence) == -1) { fprintf(stderr, "%s: error on lseek\n", argv0); exit(1); } } /* read with error checking */ r_read (fd, buf, nbytes) int fd, nbytes; char *buf; { if (read(fd, buf, nbytes) != nbytes) { fprintf(stderr, "%s: error on read\n", argv0); exit(1); } } init_packet() { #ifdef RWHOD time_t boothz; # if UDP struct hostent *he; he = gethostbyname( "localnet" ); if( he == NULL || he->h_addr == 0 ) { fprintf(stderr, "no address: localnet\n"); exit( 1 ); } ipaddr = he->h_addr; # endif # if DSK sprintf(whopacket, "/usr/spool/rwho/whod.%s", utsn.nodename); # endif memset(&proto, '\0', sizeof proto); /* clear proto packet */ strncat(proto.wd_hostname, utsn.nodename, 9); /* at most 9, add null */ proto.wd_vers = WHODVERSION; proto.wd_type = WHODTYPE_STATUS; l_lseek(memfd, (long)nl[NL_LBOLT].n_value, 0); r_read(memfd, (char *)&boothz, sizeof( boothz ) ); proto.wd_boottime = time(0) - (boothz / HZ); #else SHM if ((aven_key = ftok(SYSTEM, 'a')) == (key_t) -1) { perror(SYSTEM); exit(1); } if ((aven_shm = shmget(aven_key, 3 * sizeof (double), IPC_CREAT|IPC_EXCL|0644)) < 0) { perror("shmget"); exit(1); } if ((int) (aven_seg = (double *) shmat(aven_shm, (char *) 0, 0)) == -1) { perror("shmat"); if (shmdt((char *) aven_seg) == -1) perror("shmdt"); if (shmctl(aven_shm, IPC_RMID, (struct shmid_ds *) 0) < 0) perror("shmctl(IPC_RMID)"); exit(1); } #endif RWHOD } /* init_packet */ make_packet(iavg1, iavg2, iavg3) #ifdef RWHOD long iavg1, iavg2, iavg3; #else SHM double iavg1, iavg2, iavg3; #endif RWHOD { #ifdef RWHOD static struct whod packet; /* local packet copy */ register struct whoent *wep; /* pointer to packet whoent */ register struct utmp *utp; /* return from getutent */ int whof, cc; /* output file, char count */ packet = proto; /* copy proto packet */ time(&packet.wd_sendtime); time(&packet.wd_recvtime); /* forge this !! */ packet.wd_loadav[0] = iavg1; packet.wd_loadav[1] = iavg2; packet.wd_loadav[2] = iavg3; setutent(); /* open utmp file */ wep = &packet.wd_we[0]; /* get pointer to first user in pkt */ while( (utp = getutent()) != NULL ) { if( (utp->ut_type == USER_PROCESS) && utp->ut_user[0]) { strncpy(wep->we_utmp.out_line, utp->ut_id, 4); wep->we_utmp.out_line[4] = '\0'; strncpy(wep->we_utmp.out_name, utp->ut_user, 8); wep->we_utmp.out_time = utp->ut_time; wep->we_idle = idletime(utp); wep++; /* bump packet pointer */ } /* user process */ } /* while */ endutent(); # if DSK whof = creat(whopacket, 0644); /* open packt file */ if( whof >= 0 ) { cc = (char *)wep - (char *)&packet; if( write(whof, (char *)&packet, cc) != cc ) perror("write failed"); close(whof); } /* file opened */ else perror(whopacket); # endif # if UDP cc = (char *)wep - (char *)&packet; udpsend( (char *)&packet, cc, ipaddr, port, port, 1); # endif # ifdef DEBUG fprintf(stderr, "wrote packet (%d)\n", cc); fflush(stderr); # endif #else SHM aven_seg[0] = iavg1; aven_seg[1] = iavg2; aven_seg[2] = iavg3; #endif RWHOD } /* make_packet */ #ifdef RWHOD idletime(up) struct utmp *up; { register int i; register char *cp, *dp; char ttyname[10]; struct stat buf; time_t now; cp = "/dev/"; dp = ttyname; while( *cp != '\0' ) /* copy "/dev/" */ *dp++ = *cp++; cp = up->ut_line; /* get line name */ if( *cp == 's' ) /* starts with an 's'? (sxtnnn) */ *dp++ = 'v'; /* insert a 'v' */ for( i = 0; i < 8; i++ ) /* copy line name */ if( (*dp++ = *cp++) == '\0' ) break; /* or until null */ if( stat(ttyname, &buf) != 0 ) /* get file status */ return( 0 ); time(&now); /* get current time */ i = now - buf.st_atime; /* get differnce from last acces */ return( i ); /* return idle time */ } /* idletime */ #endif RWHOD ------------------------------------------------------------------------------ -- ++Brandon (Resident Elf @ ncoast.UUCP) ____ ______________ / \ / __ __ __ \ Brandon S. Allbery <backbone>!ncoast!allbery ___ | /__> / \ / \ aXcess Co., Consulting ncoast!allbery@Case.CSNET / \ | | `--, `--, 6615 Center St. #A1-105 (...@relay.CS.NET) | | \__/ \__/ \__/ Mentor, OH 44060-4101 \____/ \______________/ +1 216 974 9210