loverso@encore.UUCP (John LoVerso) (11/06/87)
In a recent article George Jones <george@fubar.cis.ohio-state.EDU> writes: > >Xload needs a bit of work since the load average is computed differently. > >Nlist() is not a valid operation and avenrun does not contain the load. > > I have a quick and dirty xload (X10) that picks up the load from the stdout > of "uptime" if anyone is interested. The problem with this is the lack of documentation on the inq_stats(2) system call. There is quite a bit of information available by using it, if you know what to look for. Below, I've included the source to a version of `uptime' I've written using inq_stats. It produces output identical to uptime (see note below). The get_stats() routine can be grafted into xload (and/or anything else you want), and should be a clear example of how to use inq_stats(). There is a bug in probably all released versions of w/uptime and rwhod since 2.0. It causes them to compute the load average incorrectly. Basically, the ps_nrun[] array is a circular buffer holding the number of runnable jobs, as seen every 5 seconds. To compute the 1/5/15 minute load avgs, you just average the last 12/60/180 entries. The bug was that w/uptime and rwhod thought the size of the array was 180 entries, rather than the 300 it really is. When they have to wrap around the start of the array, they will avg the wrong entries. This happened because the size of ps_nrun[] was 180 until R2.0 or so. However, this value was put directly into w/uptime and rwhod. A constant, PS_NRUNSIZE, wasn't made up until R3.0, and rwhod & w/uptime were only fixed recently. Corrected versions should be seen with the final release of 3.1. The code below is not an official product of Encore, nor is it supported or guaranteed to be correct. I'm only providing it to show an example use of inq_stats. Please direct any comments about it to my address below. John Robert LoVerso, Encore Computer Corp encore!loverso, loverso@multimax.arpa /* * `uptime' for UMAX4.2 using inq_stats(2) syscall */ #include <sys/time.h> #include <stdio.h> #include <sys/param.h> time_t time(); main() { double avenrun[3]; /* load avgs over last 1, 5, 15 mins */ time_t uptime; /* time of last reboot & elapsed time since */ int nusers; /* number of users logged in now */ int days, hrs, mins; /* * get uptime and load avgs */ get_stats(avenrun,&uptime); /* * Print time of day */ printtod(time((time_t *)0)); /* silly lint */ /* * Print how long system has been up. */ uptime += 30; days = uptime / (60 * 60 * 24); uptime %= (60 * 60 * 24); hrs = uptime / (60 * 60); uptime %= (60 * 60); mins = uptime / 60; printf(" up"); if (days > 0) printf(" %d day%s,", days, days>1?"s":""); if (hrs > 0 && mins > 0) printf(" %2d:%02d,", hrs, mins); else { if (hrs > 0) printf(" %d hr%s,", hrs, hrs>1?"s":""); if (mins > 0) printf(" %d min%s,", mins, mins>1?"s":""); } /* * Print number logged in users */ count_users(&nusers); if (nusers > 1) printf(" %d users", nusers); else printf(" %d user", nusers); /* * Print 1, 5, and 15 minute load averages. */ printf(", load average: %.2f %.2f %.2f\n", avenrun[0], avenrun[1], avenrun[2]); } /* * Print time of day in 12 hour format */ printtod(tod) time_t tod; { register int hours; register char *cycle; struct tm *timep; timep = localtime(&tod); hours = timep->tm_hour; if (hours > 11) { cycle = "pm"; hours -= 12; } else cycle = "am"; if (hours == 0) hours = 12; printf("%3d:%02d%s", hours, timep->tm_min, cycle); } /* * read utmp to count number of users logged in */ #include <utmp.h> #define UTMP "/etc/utmp" count_users(nusers) register int *nusers; { FILE *ut = fopen(UTMP,"r"); struct utmp utmp; *nusers = 0; while (fread((char *)&utmp, sizeof(utmp), 1, ut)) if (utmp.ut_name[0] != '\0') (*nusers)++; (void) fclose(ut); } /* * Use inq_stats() to determine boottime, load average */ #include <sys/stat.h> #include <sys/statistics.h> #include <sys/sysdefs.h> #include <sys/procstats.h> #include <sys/msgbuf.h> #include <sys/syscall.h> #include <sys/sysstats.h> #ifndef PS_NRUNSIZE #define PS_NRUNSIZE 300 /* see dimension of ps_nrun[] in procstats.h */ #endif get_stats(avenrun,uptime) double avenrun[]; time_t *uptime; { struct stat_descr Boot_info; struct sys_boot Boot_data; struct stat_descr Proc_info; struct proc_summary Proc_sum_data; struct timeval boottime; register int count, index, interval; /* * struct to get system boot data, * esp boottime */ Boot_info.sd_next = &Proc_info; Boot_info.sd_subsys = SUBSYS_SYS; Boot_info.sd_type = SYSTYPE_BOOT; Boot_info.sd_addr = (char *) &Boot_data; Boot_info.sd_size = sizeof (struct sys_boot); Boot_info.sd_sizeused = 0; /* * struct to get process summary data, * esp circular buffer containing counts of runnable processes */ Proc_info.sd_next = NULL; Proc_info.sd_subsys = SUBSYS_PROC; Proc_info.sd_type = PROCTYPE_SUMMARY; Proc_info.sd_addr = (char *) &Proc_sum_data; Proc_info.sd_size = sizeof (struct proc_summary); Proc_info.sd_sizeused = 0; /* * fill in Boot_data and Proc_sum_data structures */ if (inq_stats(1, &Boot_info) != 0) { perror("inq_stats"); exit(1); } /* * Compute seconds since last boot */ boottime = Boot_data.sb_boottime; *uptime = Proc_info.sd_time.tv_sec - boottime.tv_sec; /* * each entry in ps_nrun[] is a count of number of processes. * a new entry is added every 5 seconds. * period[] is the number of entries to scan for 1,5,15 min intervals */ index = Proc_sum_data.ps_nrunidx; interval = 0; avenrun[0] = 0.0; for (count = 0; count < 3; count++) { static int period[] = { 12, 60, 180 }; for (; interval < period[count]; interval++) { avenrun[count] += Proc_sum_data.ps_nrun[index]; if (--index < 0) index = PS_NRUNSIZE-1; } if (count < 2) avenrun[count+1] = avenrun[count]; avenrun[count] /= period[count]; } }