William LeFebvre <phil@rice.ARPA> (12/03/84)
This message contains a shar file distribution for "top". "Top" is a program that will display and update information about a Berkeley Unix (either 4.1 or 4.2) system, including the top cpu using processes. It would be similar to continually doing a "ps aux" except that it doesn't consume half of the cpu to gather the information. By default, it will display the top ten processes and update this information every five seconds. I have noticed that this requires about 10% of the cpu. Of course, the percentage decreases as the time delay increases. Not having access to a Bell Unix machine, I cannot say how or if this program will work on such a system. Just run the shar file through your favorite shell, then read the README. This is a new version of "top", version 1.6 to be precise. This includes several fixes and enhancements, including #ifdef code for the SUN. Judicious sprinkling of the "register" keyword and some rewriting have also occurred in an attempt to make it a little faster. Note that this software has a copyright notice. However, as is clearly stated in the program itself, I give permission to freely redistribute "top" to other Unix sites. Enjoy! William LeFebvre Department of Computer Science Rice University <phil@Rice.arpa> ------------------------------- CUT HERE ------------------------------- echo 'Start of distribution file top.shar:' echo 'Extracting Changes...' sed 's/^X//' > Changes << '/' XTue Nov 20 1984 - wnl (1.6) X Added an "exit" if sbrk's fail. Added changes from Jonathon X Feiber at Sun: ifdef SUN to make top work on Suns (they don't use X doubles in the proc structure), register declarations, check for X getting a user structure that has disappeared since the proc array X was read (it used to die, now it just shows the process as swapped). X XTue Nov 13 1984 - wnl (1.5) X If the number of displayable processes ("active_procs") was less X than the number of requested processes ("topn"), top would X segmentation fault. This bug has been fixed. Thanks to Prentiss X Riddle at ut-sally for pointing out the existence of this bug. X XTue Oct 23 1984 - wnl (1.4) X Finally fixed the hash table bug that caused processes owned by X root to sometimes appear with either no name or a different name X that had UID 0 (such as "operator"). Removed all the ifdef DEBUG X blocks to make top ready for distribution to the real world. X XSun Apr 8 1984 - wnl (still 1.3) X Made some slight changes to the display format. It now looks more X aesthetically pleasing. Added some preprocessor constants so that X the two defaults (number of processes and seconds of delay) easier X to change. X XThu Apr 5 1984 - wnl (1.3) X Changed the order in which things are done at initialization time. X This way, if an error occurs before starting the main loop, curses X will never get started. Also changed other error handlers so that X endwin() is called before any flavor of exit. Specifying a number X of processes that is more than the screen can handle is no longer X fatal. It displays a warning message and pretends the user X specified the maximum for the screen. Finally cured all the TSTP X blues (well, almost all). I removed my TSTP handler and convinced X the system to always use the one that curses sets up. Turns out X that "sleep" was stepping all over it during a pause. So, I don't X use sleep anymore. The only problem that remains with it now is X redrawing the old display before updating it after a pause. X XTue Apr 3 1984 - wnl (from 1.0 to 1.2) X I changed the format of the TIME column from just "seconds" to X "minutes:seconds". I also made pausing work correctly. Screen X redraws with an up to date display. For compatability with 4.2, I X changed the name of the "zero" function to "bzero". The makefile X has been altered to handle versions for 4.1 and 4.2, and README X has been updated to reflect these recent changes. / echo 'Extracting Makefile4.1...' sed 's/^X//' > Makefile4.1 << '/' X# Makefile for 4.1 version of "top" X X# values for two defaults in "top" XTOPN = 10 XDELAY = 5 X X# various other useful things XMAN = mancat XTROFF = xtroff XTARFILES = Makefile Makefile4.1 Makefile4.2 Changes README top.c bzero.c top.1 XCFLAGS = -O X Xtop: top.o bzero.o X cc $(CFLAGS) -o top top.o bzero.o -lcurses -ltermcap -lm X Xtop.o: top.c X cc -c $(CFLAGS) -DDefault_TOPN=$(TOPN) -DDefault_DELAY=$(DELAY) top.c X X# This will switch the Makefile over to a 4.2 version X4.2: X rm Makefile X ln Makefile4.2 Makefile X Xman: X nroff -$(MAN) top.1 | cat -s >top.cat X Xtroff: X $(TROFF) -man top.1 X Xtar: X tar cvf top.tar $(TARFILES) / echo 'Extracting Makefile4.2...' sed 's/^X//' > Makefile4.2 << '/' X# Makefile for 4.2 version of "top" X X# values for two defaults in "top" XTOPN = 10 XDELAY = 5 X XMAN = man XTROFF = xtroff XTARFILES = Makefile Makefile4.1 Makefile4.2 Changes README top.c bzero.c top.1 XCFLAGS = -DFOUR_TWO -O X# To make a version for the SUN, comment out the previous line and X# uncomment the following line: X#CFLAGS = -DFOUR_TWO -DSUN -O X Xtop: top.o X cc $(CFLAGS) -o top top.o -lcurses -ltermcap -lm X Xtop.o: top.c X cc -c $(CFLAGS) -DDefault_TOPN=$(TOPN) -DDefault_DELAY=$(DELAY) top.c X X# This will switch the Makefile over to a 4.1 version X4.1: X rm Makefile X ln Makefile4.1 Makefile X Xman: X nroff -$(MAN) -rN$(TOPN) -rD$(DELAY) top.1 | cat -s >top.cat X Xtroff: X $(TROFF) -man -rN$(TOPN) -rD$(DELAY) top.1 X Xtar: X tar cvf top.tar $(TARFILES) / echo 'Extracting README...' sed 's/^X//' > README << '/' XThis file contains a few comments about "top". X X"top" is a program that will give continual reports about the state of the Xsystem, including a list of the top cpu using processes. It requires read Xaccess to the memory files "/dev/kmem" and "/dev/mem" as well as the system Ximage "/vmunix". Some installations have these files protected from general Xaccess. These sites would have to install this program in the same way that Xprograms such as "ps" are installed. X XThere are a few things that need to be checked before compiling the program: X XThe most important item is the internal hash table size. This size is Xdefined in the program with the preprocessor variable "Table_size". This Xconstant MUST be larger than the number of lines in the file /etc/passwd. XIt is advisable that this number be about twice the number of lines, and Xthat it be a prime number (since it dictates the size of the hash table). XMake sure this is checked before compilation. X XThe next thing is the routine called "bzero". This function simply zeros Xa block of memory. The file "bzero.c" in this distribution is written for Xa VAX and will not work on any other type of machine. This routine is not Xneeded under 4.2, since it is already defined in the C run time library. XThe routine is very easy to write, but it is something that should be as Xfast as possible. If the local machine is not a VAX then "bzero.c" will Xhave to be replaced with something else. If you can't come up with a Xslick method, there is a very simple C equivalent (although it will not be Xvery fast) that will work: X X bzero(memory, amount) X X char *memory; X int amount; X X { X while (amount > 0) X { X *memory++ = 0; X amount--; X } X } X XThere are two Makefiles in this distribution. One makes a version of top Xfor a 4.1 system (called "Makefile4.1") and the other makes a version for a X4.2 system (called "Makefile4.2"). Rename or link the version that is Xappropriate for your machine to "Makefile" before running make. There are Xsome differences compiling and linking the different versions. For example: X"bzero" is already defined in the 4.2 C library, so that routine is not needed Xunder 4.2. There are also differences in the source, which are Xconditionally compiled. Both of these cases are handled correctly by the Xappropriate Makefile. X XFinally, there are two preprocessor variables that are defined at compile Xtime by the makefile. These are "Default_TOPN" and "Default_DELAY". Their Xvalues are the defaults used for the top number of processes to be displayed Xand the number of seconds to delay between displays, respectively. They are Xset by the Makefile variables "TOPN" and "DELAY", respecitvely. These Xconstants are preset as follows: TOPN=10, DELAY=5. These can be overriden Xby either changing the Makefile or by specifying the change on the make Xcommand line (with something like "make TOPN=15"). X XIf you make a change to "top" that you feel would be beneficial to others Xwho use this program, or if you find and fix a bug, feel free to send me the Xchange. X XEnjoy! X X William LeFebvre X Department of Computer Science X Rice University X ARPANet address: <phil@Rice.arpa> X X U.S. Mail address: X William LeFebvre X P.O. Box 1892 X Department of Computer Science X Houston, TX 77251 / echo 'Extracting bzero.c...' sed 's/^X//' > bzero.c << '/' X/* X * Fast, sleazy, and ugly zero function. X * X * Note that this will only work on a VAX, but it is real easy to write a X * similar function for whatever machine you may need. If nothing else, X * just a simple loop in C will suffice. X * X * Dave Johnson, Rice University. X * X * Enhanced by William LeFebvre of Rice University to handle zeroing more X * than 64K. X */ X X# define K 1024 X X/* X * bzero(memory, amount) - set "amount" bytes starting at "memory" to the X * value 0. X */ X Xbzero(memory, amount) X Xchar *memory; Xint amount; X X{ X while (amount >= 64*K) X { X _bzero64(memory, 64*K-1); X memory += 64*K-1; X amount -= 64*K-1; X } X _bzero64(memory, amount); X} X X_bzero64(memory, amount) X Xchar *memory; Xint amount; X X{ X asm(" movc5 $0, (sp), $0, 8(ap), *4(ap)"); X} / echo 'Extracting top.c...' sed 's/^X//' > top.c << '/' Xchar *copyright = "Top, version 1.6, copyright (c) 1984, William LeFebvre"; X X/* X * Top users display for Berkeley Unix X * Version 1.6 X * X * This program may be freely redistributed to other Unix sites, but this X * entire comment MUST remain intact. X * X * Copyright (c) 1984, William LeFebvre, Rice University X * X * This program is designed to run on either Berkeley 4.1 or 4.2 Unix. X * Compile with the preprocessor variable "FOUR_TWO" set to get an X * executable that will run on Berkeley 4.2 Unix. X * X * The Sun kernel uses scaled integers instead of floating point so compile X * with the preprocessor variable "SUN" to get an executable that will run X * on Sun Unix version 1.1 or later. X * X * Fixes and enhancements since version 1.5: X * X * Jonathon Feiber at sun: X * added "#ifdef SUN" code to make top work on a Sun, X * fixed race bug in getkval for getting user structure, X * efficiency improvements: added register variables and X * removed the function hashit X */ X X#include <curses.h> X#include <stdio.h> X#include <pwd.h> X#include <nlist.h> X#include <signal.h> X#ifdef FOUR_TWO X#include <machine/pte.h> X#else X#include <sys/pte.h> X#endif X#include <sys/param.h> X#include <sys/dir.h> X#include <sys/user.h> X#include <sys/proc.h> X#include <sys/dk.h> X#include <sys/vm.h> X X/* obvious file names */ X X#define VMUNIX "/vmunix" X#define KMEM "/dev/kmem" X#define MEM "/dev/mem" X X/* X * The number of users in /etc/passwd CANNOT be greater than Table_size! X * If you get the message "table overflow: too many users", then increase X * Table_size. Since this is the size of a hash table, things will work X * best if it is a prime number that is about twice the number of users. X */ X X#define Table_size 421 X X/* Number of lines of header information on the standard screen */ X# define Header_lines 5 X X/* wish list for kernel symbols */ X Xstruct nlist nlst[] = { X { "_avenrun" }, X#define X_AVENRUN 0 X { "_ccpu" }, X#define X_CCPU 1 X { "_cp_time" }, X#define X_CP_TIME 2 X { "_hz" }, X#define X_HZ 3 X { "_mpid" }, X#define X_MPID 4 X { "_nproc" }, X#define X_NPROC 5 X { "_proc" }, X#define X_PROC 6 X { 0 }, X}; X X/* useful externals */ Xextern int errno; Xextern char *sys_errlist[]; Xextern int LINES; X X/* signal handling routines */ Xint leave(); Xint onalrm(); X X/* file descriptors for memory devices */ Xint kmem; Xint mem; X Xint nproc; Xint mpid; X X/* kernel "hz" variable -- clock rate */ Xlong hz; X X/* All this is to calculate the cpu state percentages */ X Xlong cp_time[CPUSTATES]; Xlong cp_old[CPUSTATES]; Xlong cp_change[CPUSTATES]; Xlong total_change; Xlong cp_time_offset; Xlong mpid_offset; Xlong avenrun_offset; X X#ifdef SUN Xlong ccpu; Xlong avenrun[3]; X#else Xdouble ccpu; Xdouble avenrun[3]; X#endif Xdouble logcpu; X Xstruct proc *proc; Xstruct proc *pbase; Xint bytes; X Xstruct user u; X X/* Verbose process state names */ X Xchar *state_name[] = { X "", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped" X}; X X/* process state names for the "STATE" column of the display */ X Xchar *state_abbrev[] = { X "", "sleep", "WAIT", "run", "idl", "zomb", "stop" X}; X X/* cpu state names for percentages */ X Xchar *cpu_state[] = { X "user", "nice", "system", "idle" X}; X X/* routines that don't return int */ X Xstruct passwd *getpwent(); Xchar *username(); Xchar *ctime(); Xchar *rindex(); X Xint proc_compar(); Xdouble log(); Xdouble exp(); X Xmain(argc, argv) X Xint argc; Xchar *argv[]; X X{ X register struct proc *pp; X register struct proc **prefp; X register int i; X register int active_procs; X register int change; X register long cputime; X X struct proc **pref; X int total_procs; X int proc_brkdn[7]; X int topn = Default_TOPN; X int delay = Default_DELAY; X long curr_time; X long time(); X char do_cpu = 0; X char *myname = "top"; X double pctcpu; X X /* get our name */ X if (argc > 0) X { X if ((myname = rindex(argv[0], '/')) == 0) X { X myname = argv[0]; X } X else X { X myname++; X } X } X X /* check for time delay option */ X if (argc > 1 && argv[1][0] == '-') X { X if (argv[1][1] != 's') X { X fprintf(stderr, "Usage: %s [-sn] [number]\n", myname); X exit(1); X } X delay = atoi(&(argv[1][2])); X argc--; X argv++; X } X X /* get count of top processes to display (if any) */ X if (argc > 1) X { X topn = atoi(argv[1]); X } X X /* open kmem and mem */ X if ((kmem = open(KMEM, 0)) < 0) X { X perror(KMEM); X exit(1); X } X if ((mem = open(MEM, 0)) < 0) X { X perror(MEM); X exit(1); X } X X /* get the list of symbols we want to access in the kernel */ X errno = 0; X nlist(VMUNIX, nlst); X if (nlst[0].n_type == 0) X { X fprintf(stderr, "%s: can't nlist image\n", VMUNIX); X exit(1); X } X X /* get the symbol values out of kmem */ X getkval(nlst[X_PROC].n_value, &proc, sizeof(int), nlst[X_PROC].n_name); X getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int), nlst[X_NPROC].n_name); X getkval(nlst[X_CCPU].n_value, &ccpu, sizeof(int), nlst[X_CCPU].n_name); X getkval(nlst[X_HZ].n_value, &hz, sizeof(int), nlst[X_HZ].n_name); X X /* some calculations we use later */ X X cp_time_offset = nlst[X_CP_TIME].n_value; X mpid_offset = nlst[X_MPID].n_value; X avenrun_offset = nlst[X_AVENRUN].n_value; X X /* this is used in calculating WCPU -- calculate it ahead of time */ X#ifdef SUN X logcpu = log((double)ccpu / FSCALE); X#else X logcpu = log(ccpu); X#endif X X /* allocate space for proc structure array and array of pointers */ X bytes = nproc * sizeof(struct proc); X pbase = (struct proc *)sbrk(bytes); X pref = (struct proc **)sbrk(nproc * sizeof(struct proc *)); X X /* Just in case ... */ X if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL) X { X fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); X exit(1); X } X X /* initialize the hashing stuff */ X init_hash(); X X /* initialize curses and screen (last) */ X initscr(); X erase(); X clear(); X refresh(); X X /* setup signal handlers */ X signal(SIGINT, leave); X signal(SIGQUIT, leave); X X /* can only display (LINES - Header_lines) processes */ X if (topn > LINES - Header_lines) X { X printw("Warning: this terminal can only display %d processes...\n", X LINES - Header_lines); X refresh(); X sleep(2); X topn = LINES - Header_lines; X clear(); X } X X /* main loop ... */ X X while (1) /* while(1)'s are forever ... */ X { X /* read all the proc structures in one fell swoop */ X getkval(proc, pbase, bytes, "proc array"); X X /* get the cp_time array */ X getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time"); X X /* get load average array */ X getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun"); X X /* get mpid -- process id of last process */ X getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid"); X X /* count up process states and get pointers to interesting procs */ X total_procs = 0; X active_procs = 0; X bzero(proc_brkdn, sizeof(proc_brkdn)); X prefp = pref; X for (pp = pbase, i = 0; i < nproc; pp++, i++) X { X /* place pointers to each valid proc structure in pref[] */ X /* (processes with SSYS set are system processes) */ X if (pp->p_pid != 0 && pp->p_stat != 0 && (pp->p_flag & SSYS) == 0) X { X total_procs++; X proc_brkdn[pp->p_stat]++; X if (pp->p_stat != SZOMB) X { X *prefp++ = pp; X active_procs++; X } X } X } X X /* display the load averages */ X printw("last pid: %d; load averages", mpid); X { X for (i = 0; i < 3; i++) X { X printw("%c %4.2f", X i == 0 ? ':' : ',', X#ifdef SUN X (double)avenrun[i] / FSCALE); X#else X avenrun[i]); X#endif X } X } X X /* X * Display the current time. X * "ctime" always returns a string that looks like this: X * X * Sun Sep 16 01:03:52 1973 X * 012345678901234567890123 X * 1 2 X * X * We want indices 11 thru 18 (length 8). X */ X X curr_time = time(0); X move(0, 79-8); X printw("%-8.8s\n", &(ctime(&curr_time)[11])); X X /* display process state breakdown */ X printw("%d processes", total_procs); X for (i = 1; i < 7; i++) X { X if (proc_brkdn[i] != 0) X printw("%c %d %s%s", X i == 1 ? ':' : ',', X proc_brkdn[i], X state_name[i], X (i == SZOMB) && (proc_brkdn[i] > 1) ? "s" : ""); X } X X /* calculate percentage time in each cpu state */ X printw("\nCpu states: "); X if (do_cpu) /* but not the first time */ X { X total_change = 0; X for (i = 0; i < CPUSTATES; i++) X { X /* calculate changes for each state and overall change */ X if (cp_time[i] < cp_old[i]) X { X /* this only happens when the counter wraps */ X change = (int) X ((unsigned long)cp_time[i]-(unsigned long)cp_old[i]); X } X else X { X change = cp_time[i] - cp_old[i]; X } X total_change += (cp_change[i] = change); X } X for (i = 0; i < CPUSTATES; i++) X { X printw("%s%4.1f%% %s", X i == 0 ? "" : ", ", X ((float)cp_change[i] / (float)total_change) * 100.0, X cpu_state[i]); X cp_old[i] = cp_time[i]; X } X } X else X { X /* we'll do it next time */ X for (i = 0; i < CPUSTATES; i++) X { X printw("%s %s", X i == 0 ? "" : ", ", X cpu_state[i]); X cp_old[i] = 0; X } X do_cpu = 1; X } X printw("\n"); X X if (topn > 0) X { X printw("\n PID USERNAME PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND\n"); X X /* sort by cpu percentage (pctcpu) */ X qsort(pref, active_procs, sizeof(struct proc *), proc_compar); X X /* now, show the top whatever */ X if (active_procs > topn) X { X /* adjust for a lack of processes */ X active_procs = topn; X } X for (prefp = pref, i = 0; i < active_procs; prefp++, i++) X { X pp = *prefp; X if (getu(pp) == -1) X { X strcpy(u.u_comm, "<swapped>"); X cputime = 0; X } X else X { X cputime = X#ifdef FOUR_TWO X (u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec); X#else X (int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz); X#endif X } X#ifdef SUN X pctcpu = (double)pp->p_pctcpu / FSCALE; X#else X pctcpu = pp->p_pctcpu; X#endif X printw("%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %-14.14s\n", X pp->p_pid, X username(pp->p_uid), X pp->p_pri - PZERO, X pp->p_nice - NZERO, X (pp->p_tsize + pp->p_dsize + pp->p_ssize) >> 1, X pp->p_rssize >> 1, X state_abbrev[pp->p_stat], X cputime / 60l, X cputime % 60l, X#ifdef SUN X (100.0 * pctcpu / (1.0 - exp(pp->p_time * logcpu))), X (100.0 * pctcpu), X#else X (100.0 * pctcpu / (1.0 - exp(pp->p_time * logcpu))), X (100.0 * pctcpu), X#endif X u.u_comm); X } X } X refresh(); X X /* wait ... */ X signal(SIGALRM, onalrm); X alarm(delay); X pause(); X X /* clear for new display */ X erase(); X } X} X X/* X * signal handlers X */ X Xleave() /* exit under normal conditions -- INT handler */ X X{ X move(LINES - 1, 0); X refresh(); X endwin(); X exit(0); X} X Xquit(status) /* exit under duress */ X Xint status; X X{ X endwin(); X exit(status); X} X Xonalrm() X X{ X return(0); X} X X/* X * comparison function for "qsort" X */ X Xproc_compar(p1, p2) X Xstruct proc **p1; Xstruct proc **p2; X X{ X if ((*p1)->p_pctcpu < (*p2)->p_pctcpu) X { X return(1); X } X else X { X return(-1); X } X} X X/* X * These routines handle uid to username mapping. X * They use a hashing table scheme to reduce reading overhead. X */ X Xstruct hash_el { X int uid; X char name[8]; X}; X X#define H_empty -1 X X/* simple minded hashing function */ X#define hashit(i) ((i) % Table_size) X Xstruct hash_el hash_table[Table_size]; X Xinit_hash() X X{ X register int i; X register struct hash_el *h; X X for (h = hash_table, i = 0; i < Table_size; h++, i++) X { X h->uid = H_empty; X } X X setpwent(); X} X Xchar *username(uid) X Xregister int uid; X X{ X register int index; X register int found; X register char *name; X X /* This is incredibly naive, but it'll probably get changed anyway */ X index = hashit(uid); X while ((found = hash_table[index].uid) != uid) X { X if (found == H_empty) X { X /* not here -- get it out of passwd */ X index = get_user(uid); X break; /* out of while */ X } X index = index++ % Table_size; X } X return(hash_table[index].name); X} X Xenter_user(uid, name) X Xregister int uid; Xregister char *name; X X{ X register int length; X register int index; X register int try; X static int uid_count = 0; X X /* avoid table overflow -- insure at least one empty slot */ X if (++uid_count >= Table_size) X { X fprintf(stderr, "table overflow: too many users\n"); X quit(1); X } X X index = hashit(uid); X while ((try = hash_table[index].uid) != H_empty) X { X if (try == uid) X { X return(index); X } X index = index++ % Table_size; X } X hash_table[index].uid = uid; X strncpy(hash_table[index].name, name, 8); X return(index); X} X Xget_user(uid) X Xregister int uid; X X{ X struct passwd *pwd; X static char buff[20]; X register int last_index; X X while ((pwd = getpwent()) != NULL) X { X last_index = enter_user(pwd->pw_uid, pwd->pw_name); X if (pwd->pw_uid == uid) X { X return(last_index); X } X } X sprintf(buff, "%d", uid); X return(enter_user(uid, buff)); X} X X/* X * All of this stuff gets things out of the memory files. X */ X X/* X * Get the user structure for the process who's proc structure is in p. The X * user structure is returned in u. X */ Xgetu(p) X Xregister struct proc *p; X X{ X struct pte uptes[UPAGES]; X register caddr_t upage; X register struct pte *pte; X register nbytes, n; X X /* X * Check if the process is currently loaded or swapped out. The way we X * get the u area is totally different for the two cases. For this X * application, we just don't bother if the process is swapped out. X */ X if ((p->p_flag & SLOAD) == 0) X { X return(-1); X } X X /* X * Process is currently in memory, we hope! X */ X if (!getkval(p->p_addr, uptes, sizeof(uptes), "p->p_addr")) X { X /* we can't seem to get to it, so pretend it's swapped out */ X return(-1); X } X upage = (caddr_t)&u; X pte = uptes; X for (nbytes = sizeof(u); nbytes > 0; nbytes -= NBPG) X { X lseek(mem, pte++->pg_pfnum * NBPG, 0); X n = MIN(nbytes, NBPG); X if (read(mem, upage, n) != n) X { X /* we can't seem to get to it, so pretend it's swapped out */ X return(-1); X } X upage += n; X } X return(0); X} X X/* X * get the value of something from /dev/kmem X */ X Xgetkval(offset, ptr, size, refstr) X Xlong offset; Xint *ptr; Xint size; Xchar *refstr; X X{ X if (lseek(kmem, offset, 0) == -1) X { X fprintf(stderr, "%s: lseek to %s: %s\n", X KMEM, refstr, sys_errlist[errno]); X quit(1); X } X if (read(kmem, ptr, size) == -1) X { X if (strcmp(refstr, "p->p_addr") == 0) X { X /* we lost the race with the kernel, process isn't in memory */ X return(0); X } X else X { X fprintf(stderr, "%s: reading %s: %s\n", X KMEM, refstr, sys_errlist[errno]); X quit(1); X } X } X return(1); X} X X/* X * outc - real subroutine that writes a character; for termcap X */ X Xoutc(ch) X Xregister char ch; X X{ X putchar(ch); X} / echo 'Extracting top.1...' sed 's/^X//' > top.1 << '/' X.TH TOP 1 Local X.UC 4 X.SH NAME Xtop \- display and update information about the top cpu processes X.SH SYNOPSIS X.B top X[ X.BI \-s time X] [ X.I number X] X.SH DESCRIPTION X.\" This defines appropriate quote strings for nroff and troff X.ds lq \&" X.ds rq \&" X.if t .ds lq `` X.if t .ds rq '' X.\" Just in case the command line didn't set these number registers... X.if !\nN .nr N 10 X.if !\nD .nr D 5 X.I Top Xdisplays the top \nN processes on the system and periodically Xupdates this information. It uses raw cpu percentage Xto determine the top processes. The X.IR curses (3) Xpackage is used to do semi-optimal screen updating. If X.I number Xis given, then the top X.I number Xprocesses will be displayed instead of the default amount of \nN. XThe X.B \-s Xoption sets the delay between screen updates to X.I time Xseconds. The default delay between updates is \nD seconds. X.PP XThe top few lines of the display show general information about the state of Xthe system, including the last process id assigned to a process, the three Xload averages, the current time, the number of existing processes, the number Xof processes in each state X(sleeping, ABANDONED, running, starting, zombies, and stopped), Xand a percentage of time Xspent in each of the processor states (user, nice, system, and idle). X.PP XThe remainder of the screen displays information about individual Xprocesses. This display is similar in spirit to X.IR ps (1) Xbut it is not exactly the same. PID is the process id, USERNAME is the name Xof the process's owner, PRI is the current priority of the process, NICE is Xthe nice amount (in the range \-20 to 20), SIZE is the total size of the Xprocess (text, data, and stack), RES is the current amount of resident Xmemory (both SIZE and RES are given in kilobytes), STATE is the current Xstate (one of \*(lqsleep\*(rq, \*(lqWAIT\*(rq, \*(lqrun\*(rq, X\*(lqidl\*(rq, \*(lqzomb\*(rq, or \*(lqstop\*(rq), TIME is Xthe number of system and user Xcpu seconds that the process has used, WCPU is the weighted cpu percentage X(this is the same value that X.IR ps (1) Xdisplays as CPU), CPU is the raw percentage and is the field that is sorted Xto determine the order of the processes, and COMMAND is the name of the Xcommand that the process is currently running (if the process is swapped Xout, this column is marked \*(lq<swapped>\*(rq). X.SH NOTES XThe \*(lqABANDONED\*(rq state (known in the kernel as \*(lqSWAIT\*(rq) was Xabandoned, thus the name. A process should never end up in this state. X.SH AUTHOR XWilliam LeFebvre, Rice University graduate student X.SH FILES X.DT X/dev/kmem kernel memory X.br X/dev/mem physical memory X.br X/vmunix system image X.SH BUGS XThe command name for swapped processes should be tracked down, but this Xwould make the program run slower. X.PP XAs with X.IR ps (1), Xthings can change while X.I top Xis collecting information for an update. The picture it gives is only a Xclose approximation to reality. X.SH "SEE ALSO" Xps(1), mem(4) / echo 'Distribution file top.shar complete.'