From 305e366fe47197851cc728bd12ee4d4f1d8f7d64 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Sun, 8 Nov 2015 11:56:52 +0000 Subject: [PATCH] ProcFS: get process information from MIB service Instead of pulling in process tables itself, ProcFS now queries the MIB service for process information. This reduces ProcFS's memory usage by about 1MB. The change does have two negative consequences. First, getting all the original /proc//psinfo fields filled in would take a lot of extra effort. Since the only program that uses those files at all is mtop(1), we reformat psinfo to expose only the information used by mtop(1). This means that with this patch, older copies of MINIX3 ps and top will cease to work. Second, since both MIB and ProcFS update their own view of the process list only once per clock tick, ProcFS' view may now be outdated by up to two clock ticks. This is unlikely to pose a problem in practice. Change-Id: Iaa6b60450c8fb52d092962394d33d08bd638bc01 --- minix/fs/procfs/glo.h | 6 +- minix/fs/procfs/inc.h | 4 +- minix/fs/procfs/pid.c | 366 ++++++++++----------------------- minix/fs/procfs/proto.h | 1 + minix/fs/procfs/tree.c | 131 +++--------- minix/include/minix/endpoint.h | 4 - minix/include/minix/procfs.h | 20 +- minix/include/minix/sysctl.h | 42 ++++ minix/lib/libminc/Makefile | 2 +- minix/servers/mib/mib.h | 4 + minix/servers/mib/minix.c | 11 + minix/servers/mib/proc.c | 116 +++++++++++ minix/usr.bin/mtop/mtop.c | 67 ++---- 13 files changed, 347 insertions(+), 427 deletions(-) diff --git a/minix/fs/procfs/glo.h b/minix/fs/procfs/glo.h index 03d573d71..fbb09d45f 100644 --- a/minix/fs/procfs/glo.h +++ b/minix/fs/procfs/glo.h @@ -1,8 +1,6 @@ #ifndef _PROCFS_GLO_H #define _PROCFS_GLO_H -#include - /* pid.c */ extern struct file pid_files[]; @@ -10,8 +8,6 @@ extern struct file pid_files[]; extern struct file root_files[]; /* tree.c */ -extern struct proc proc[NR_PROCS + NR_TASKS]; /* process table from kernel */ -extern struct mproc mproc[NR_PROCS]; /* process table from PM */ -extern struct fproc fproc[NR_PROCS]; /* process table from VFS */ +extern struct minix_proc_list proc_list[NR_PROCS]; #endif /* _PROCFS_GLO_H */ diff --git a/minix/fs/procfs/inc.h b/minix/fs/procfs/inc.h index a28fe3359..5ef36958b 100644 --- a/minix/fs/procfs/inc.h +++ b/minix/fs/procfs/inc.h @@ -2,6 +2,8 @@ #define _PROCFS_INC_H #include +#include +#include #include #include #include @@ -11,9 +13,7 @@ #include "kernel/const.h" #include "kernel/type.h" #include "kernel/proc.h" -#include "pm/mproc.h" #include "vfs/const.h" -#include "vfs/fproc.h" #include "vfs/dmap.h" #include "const.h" diff --git a/minix/fs/procfs/pid.c b/minix/fs/procfs/pid.c index 871047776..df80eb9fa 100644 --- a/minix/fs/procfs/pid.c +++ b/minix/fs/procfs/pid.c @@ -5,10 +5,6 @@ #include #include -#define S_FRAME_SIZE 4096 /* use malloc if larger than this */ -static char s_frame[S_FRAME_SIZE]; /* static storage for process frame */ -static char *frame; /* pointer to process frame buffer */ - static void pid_psinfo(int slot); static void pid_cmdline(int slot); static void pid_environ(int slot); @@ -34,214 +30,106 @@ static int is_zombie(int slot) { - return (slot >= NR_TASKS && - (mproc[slot - NR_TASKS].mp_flags & (TRACE_ZOMBIE | ZOMBIE))); + return (slot >= NR_TASKS && + (proc_list[slot - NR_TASKS].mpl_flags & MPLF_ZOMBIE)); } /* - * Print information used by ps(1) and top(1). + * Get MINIX3-specific process data for the process identified by the given + * kernel slot. Return OK or a negative error code. */ -static void -pid_psinfo(int i) +int +get_proc_data(pid_t pid, struct minix_proc_data * mpd) { - int pi, task, state, type, p_state, f_state; - char name[PROC_NAME_LEN+1], *p; - struct vm_usage_info vui; - pid_t ppid; + int mib[4] = { CTL_MINIX, MINIX_PROC, PROC_DATA, pid }; + size_t oldlen; - pi = i - NR_TASKS; - task = proc[i].p_nr < 0; + oldlen = sizeof(*mpd); + if (__sysctl(mib, __arraycount(mib), mpd, &oldlen, NULL, 0) != 0) + return -errno; - /* Get the name of the process. Spaces would mess up the format.. */ - if (task || mproc[i].mp_name[0] == 0) - strncpy(name, proc[i].p_name, sizeof(name) - 1); - else - strncpy(name, mproc[pi].mp_name, sizeof(name) - 1); - name[sizeof(name) - 1] = 0; - if ((p = strchr(name, ' ')) != NULL) - p[0] = 0; - - /* Get the type of the process. */ - if (task) - type = TYPE_TASK; - else if (mproc[i].mp_flags & PRIV_PROC) - type = TYPE_SYSTEM; - else - type = TYPE_USER; - - /* Get the state of the process. */ - if (!task) { - if (is_zombie(i)) - state = STATE_ZOMBIE; /* zombie */ - else if (mproc[pi].mp_flags & TRACE_STOPPED) - state = STATE_STOP; /* stopped (traced) */ - else if (proc[i].p_rts_flags == 0) - state = STATE_RUN; /* in run-queue */ - else if (fp_is_blocked(&fproc[pi]) || - (mproc[pi].mp_flags & (WAITING | SIGSUSPENDED))) - state = STATE_SLEEP; /* sleeping */ - else - state = STATE_WAIT; /* waiting */ - } else { - if (proc[i].p_rts_flags == 0) - state = STATE_RUN; /* in run-queue */ - else - state = STATE_WAIT; /* other i.e. waiting */ - } - - /* We assume that even if a process has become a zombie, its kernel - * proc entry still contains the old (although valid) information. - * Currently this is true, but in the future we may have to filter some - * fields. - */ - buf_printf("%d %c %d %s %c %d %d %lu %lu %lu %lu", - PSINFO_VERSION, /* information version */ - type, /* process type */ - (int)proc[i].p_endpoint, /* process endpoint */ - name, /* process name */ - state, /* process state letter */ - (int)P_BLOCKEDON(&proc[i]), /* endpt blocked on, or NONE */ - (int)proc[i].p_priority, /* process priority */ - (long)proc[i].p_user_time, /* user time */ - (long)proc[i].p_sys_time, /* system time */ - ex64hi(proc[i].p_cycles), /* execution cycles */ - ex64lo(proc[i].p_cycles) - ); - - memset(&vui, 0, sizeof(vui)); - - if (!is_zombie(i)) { - /* We don't care if this fails. */ - (void)vm_info_usage(proc[i].p_endpoint, &vui); - } - - /* If the process is not a kernel task, we add some extra info. */ - if (!task) { - if (mproc[pi].mp_flags & WAITING) - p_state = PSTATE_WAITING; - else if (mproc[pi].mp_flags & SIGSUSPENDED) - p_state = PSTATE_SIGSUSP; - else - p_state = '-'; - - if (mproc[pi].mp_parent == pi) - ppid = NO_PID; - else - ppid = mproc[mproc[pi].mp_parent].mp_pid; - - switch (fproc[pi].fp_blocked_on) { - case FP_BLOCKED_ON_NONE: f_state = FSTATE_NONE; break; - case FP_BLOCKED_ON_PIPE: f_state = FSTATE_PIPE; break; - case FP_BLOCKED_ON_LOCK: f_state = FSTATE_LOCK; break; - case FP_BLOCKED_ON_POPEN: f_state = FSTATE_POPEN; break; - case FP_BLOCKED_ON_SELECT: f_state = FSTATE_SELECT; break; - case FP_BLOCKED_ON_OTHER: f_state = FSTATE_TASK; break; - default: f_state = FSTATE_UNKNOWN; - } - - buf_printf(" %lu %lu %lu %c %d %u %u %u %d %c %d %llu", - vui.vui_total, /* total memory */ - vui.vui_common, /* common memory */ - vui.vui_shared, /* shared memory */ - p_state, /* sleep state */ - ppid, /* parent PID */ - mproc[pi].mp_realuid, /* real UID */ - mproc[pi].mp_effuid, /* effective UID */ - mproc[pi].mp_procgrp, /* process group */ - mproc[pi].mp_nice, /* nice value */ - f_state, /* VFS block state */ - (int)(fproc[pi].fp_blocked_on == FP_BLOCKED_ON_OTHER) ? - fproc[pi].fp_task : NONE, /* block proc */ - fproc[pi].fp_tty /* controlling tty */ - ); - } - - /* Always add kernel cycles. */ - buf_printf(" %lu %lu %lu %lu", - ex64hi(proc[i].p_kipc_cycles), - ex64lo(proc[i].p_kipc_cycles), - ex64hi(proc[i].p_kcall_cycles), - ex64lo(proc[i].p_kcall_cycles)); - - /* Add total memory for tasks at the end. */ - if (task) - buf_printf(" %lu", vui.vui_total); - - /* Newline at the end of the file. */ - buf_printf("\n"); + return OK; } /* - * If we allocated memory dynamically during a call to get_frame(), free it up - * here. + * Print process information. This feature is now used only by mtop(1), and as + * a result, we only provide information that mtop(1) actually uses. In the + * future, this file may be extended with additional fields again. */ static void -put_frame(void) +pid_psinfo(int slot) { + struct minix_proc_data mpd; + struct vm_usage_info vui; + pid_t pid; + uid_t uid; + char *p; + int task, type, state; - if (frame != s_frame) - free(frame); -} + if ((pid = pid_from_slot(slot)) == 0) + return; -/* - * Get the execution frame from the top of the given process's stack. It may - * be very large, in which case we temporarily allocate memory for it (up to a - * certain size). - */ -static int -get_frame(int slot, vir_bytes * basep, vir_bytes * sizep, size_t * nargsp) -{ - vir_bytes base, size; - size_t nargs; + if (get_proc_data(pid, &mpd) != OK) + return; + + task = (slot < NR_TASKS); - if (proc[slot].p_nr < 0 || is_zombie(slot)) - return FALSE; + /* Get the type of the process. */ + if (task) + type = TYPE_TASK; + else if (mpd.mpd_flags & MPDF_SYSTEM) + type = TYPE_SYSTEM; + else + type = TYPE_USER; /* - * Get the frame base address and size. Limit the size to whatever we - * can handle. If our static buffer is not sufficiently large to store - * the entire frame, allocate memory dynamically. It is then later - * freed by put_frame(). + * Get the (rudimentary) state of the process. The zombie flag is also + * in the proc_list entry but it just may be set since we obtained that + * entry, in which case we'd end up with the wrong state here. */ - base = mproc[slot - NR_TASKS].mp_frame_addr; - size = mproc[slot - NR_TASKS].mp_frame_len; - - if (size < sizeof(size_t)) return FALSE; - - if (size > ARG_MAX) size = ARG_MAX; - - if (size > sizeof(s_frame)) { - frame = malloc(size); - - if (frame == NULL) - return FALSE; - } else - frame = s_frame; - - /* Copy in the complete process frame. */ - if (sys_datacopy(proc[slot].p_endpoint, base, SELF, (vir_bytes)frame, - (phys_bytes)size) != OK) { - put_frame(); - - return FALSE; - } - - frame[size] = 0; /* terminate any last string */ + if (mpd.mpd_flags & MPDF_ZOMBIE) + state = STATE_ZOMBIE; + else if (mpd.mpd_flags & MPDF_RUNNABLE) + state = STATE_RUN; + else if (mpd.mpd_flags & MPDF_STOPPED) + state = STATE_STOP; + else + state = STATE_SLEEP; - nargs = *(size_t *)frame; - if (nargs < 1 || - sizeof(size_t) + sizeof(char *) * (nargs + 1) > size) { - put_frame(); + /* Get the process's effective user ID. */ + if (!task) + uid = proc_list[slot - NR_TASKS].mpl_uid; + else + uid = 0; - return FALSE; - } + /* Get memory usage. We do not care if this fails. */ + memset(&vui, 0, sizeof(vui)); + if (!(mpd.mpd_flags & MPDF_ZOMBIE)) + (void)vm_info_usage(mpd.mpd_endpoint, &vui); - *basep = base; - *sizep = size; - *nargsp = nargs; + /* Spaces in the process name would mess up the output format. */ + if ((p = strchr(mpd.mpd_name, ' ')) != NULL) + *p = '\0'; - /* The caller now has to called put_frame() to clean up. */ - return TRUE; + /* Print all the information. */ + buf_printf("%d %c %d %s %c %d %d %u %u " + "%"PRIu64" %"PRIu64" %"PRIu64" %lu %d %u\n", + PSINFO_VERSION, /* information version */ + type, /* process type */ + mpd.mpd_endpoint, /* process endpoint */ + mpd.mpd_name, /* process name */ + state, /* process state letter */ + mpd.mpd_blocked_on, /* endpt blocked on, or NONE */ + mpd.mpd_priority, /* process priority */ + mpd.mpd_user_time, /* user time */ + mpd.mpd_sys_time, /* system time */ + mpd.mpd_cycles, /* execution cycles */ + mpd.mpd_kipc_cycles, /* kernel IPC cycles */ + mpd.mpd_kcall_cycles, /* kernel call cycles */ + vui.vui_total, /* total memory */ + mpd.mpd_nice, /* nice value */ + uid /* effective user ID */ + ); } /* @@ -251,28 +139,24 @@ get_frame(int slot, vir_bytes * basep, vir_bytes * sizep, size_t * nargsp) static void pid_cmdline(int slot) { - vir_bytes base, size, ptr; - size_t i, len, nargs; - char **argv; + char buf[BUF_SIZE]; + int mib[] = { CTL_KERN, KERN_PROC_ARGS, 0, KERN_PROC_ARGV }; + size_t oldlen; + pid_t pid; - if (!get_frame(slot, &base, &size, &nargs)) + /* Kernel tasks and zombies have no memory. */ + if ((pid = pid_from_slot(slot)) <= 0 || is_zombie(slot)) return; - argv = (char **)&frame[sizeof(size_t)]; - - for (i = 0; i < nargs; i++) { - ptr = (vir_bytes)argv[i] - base; + mib[2] = proc_list[slot - NR_TASKS].mpl_pid; - /* Check for bad pointers. */ - if ((long)ptr < 0L || ptr >= size) - break; + /* TODO: zero-copy into the main output buffer */ + oldlen = sizeof(buf); - len = strlen(&frame[ptr]) + 1; - - buf_append(&frame[ptr], len); - } + if (__sysctl(mib, __arraycount(mib), buf, &oldlen, NULL, 0) != 0) + return; - put_frame(); + buf_append(buf, oldlen); } /* @@ -282,59 +166,51 @@ pid_cmdline(int slot) static void pid_environ(int slot) { - vir_bytes base, size, ptr; - size_t nargs, off, len; - char **envp; + char buf[BUF_SIZE]; + int mib[] = { CTL_KERN, KERN_PROC_ARGS, 0, KERN_PROC_ENV }; + size_t oldlen; + pid_t pid; - if (!get_frame(slot, &base, &size, &nargs)) + /* Kernel tasks and zombies have no memory. */ + if ((pid = pid_from_slot(slot)) <= 0 || is_zombie(slot)) return; - off = sizeof(size_t) + sizeof(char *) * (nargs + 1); - envp = (char **)&frame[off]; - - for (;;) { - /* Make sure there is no buffer overrun. */ - if (off + sizeof(char *) > size) - break; - - ptr = (vir_bytes) *envp; - - /* Stop at the terminating NULL pointer. */ - if (ptr == 0L) - break; + mib[2] = proc_list[slot - NR_TASKS].mpl_pid; - ptr -= base; + /* TODO: zero-copy into the main output buffer */ + oldlen = sizeof(buf); - /* Check for bad pointers. */ - if ((long)ptr < 0L || ptr >= size) - break; - - len = strlen(&frame[ptr]) + 1; - - buf_append(&frame[ptr], len); - - off += sizeof(char *); - envp++; - } + if (__sysctl(mib, __arraycount(mib), buf, &oldlen, NULL, 0) != 0) + return; - put_frame(); + buf_append(buf, oldlen); } /* * Print the virtual memory regions of a process. */ static void -dump_regions(int slot) +pid_map(int slot) { + struct minix_proc_data mpd; struct vm_region_info vri[MAX_VRI_COUNT]; vir_bytes next; + pid_t pid; int i, r, count; + /* Kernel tasks and zombies have no memory. */ + if ((pid = pid_from_slot(slot)) <= 0 || is_zombie(slot)) + return; + + /* Get the process endpoint. */ + if (get_proc_data(pid, &mpd) != OK) + return; + count = 0; next = 0; do { - r = vm_info_region(proc[slot].p_endpoint, vri, MAX_VRI_COUNT, + r = vm_info_region(mpd.mpd_endpoint, vri, MAX_VRI_COUNT, &next); if (r <= 0) @@ -352,19 +228,3 @@ dump_regions(int slot) } } while (r == MAX_VRI_COUNT); } - -/* - * Print a memory map of the process. Obtain the information from VM. - */ -static void -pid_map(int slot) -{ - - /* Zombies have no memory. */ - if (is_zombie(slot)) - return; - - /* Kernel tasks also have no memory. */ - if (proc[slot].p_nr >= 0) - dump_regions(slot); -} diff --git a/minix/fs/procfs/proto.h b/minix/fs/procfs/proto.h index c9dcde0f5..7e73d4dfa 100644 --- a/minix/fs/procfs/proto.h +++ b/minix/fs/procfs/proto.h @@ -23,6 +23,7 @@ int getdents_hook(struct inode *inode, cbdata_t cbdata); ssize_t read_hook(struct inode *inode, char *ptr, size_t len, off_t off, cbdata_t cbdata); int rdlink_hook(struct inode *inode, char *ptr, size_t max, cbdata_t cbdata); +pid_t pid_from_slot(int slot); void out_of_inodes(void); /* util.c */ diff --git a/minix/fs/procfs/tree.c b/minix/fs/procfs/tree.c index d6a673aec..aae250f7c 100644 --- a/minix/fs/procfs/tree.c +++ b/minix/fs/procfs/tree.c @@ -2,36 +2,27 @@ #include "inc.h" -typedef struct proc ixfer_proc_t; -typedef struct fproc ixfer_fproc_t; -typedef struct mproc ixfer_mproc_t; - -ixfer_proc_t proc[NR_PROCS + NR_TASKS]; -ixfer_mproc_t mproc[NR_PROCS]; -ixfer_fproc_t fproc[NR_PROCS]; +struct minix_proc_list proc_list[NR_PROCS]; static int nr_pid_entries; /* - * Return whether the given slot is in use by a process. + * Return a PID for the given slot, or 0 if the slot is not in use. */ -static int -slot_in_use(int slot) +pid_t +pid_from_slot(int slot) { - /* - * For kernel tasks, check only the kernel slot. Tasks do not have a - * PM/VFS process slot. - */ + /* All kernel tasks are always present.*/ if (slot < NR_TASKS) - return (proc[slot].p_rts_flags != RTS_SLOT_FREE); + return (pid_t)(slot - NR_TASKS); + + /* For regular processes, check the process list. */ + if (proc_list[slot - NR_TASKS].mpl_flags & MPLF_IN_USE) + return proc_list[slot - NR_TASKS].mpl_pid; + else + return 0; - /* For regular processes, check only the PM slot. Do not check the - * kernel slot, because that would skip zombie processes. The PID - * check should be redundant, but if it fails, procfs could crash. - */ - return ((mproc[slot - NR_TASKS].mp_flags & IN_USE) && - mproc[slot - NR_TASKS].mp_pid != 0); } /* @@ -47,8 +38,8 @@ check_owner(struct inode * node, int slot) get_inode_stat(node, &stat); - return (stat.uid == mproc[slot - NR_TASKS].mp_effuid && - stat.gid == mproc[slot - NR_TASKS].mp_effgid); + return (stat.uid == proc_list[slot - NR_TASKS].mpl_uid && + stat.gid == proc_list[slot - NR_TASKS].mpl_gid); } /* @@ -68,8 +59,8 @@ make_stat(struct inode_stat * stat, int slot, int index) stat->uid = SUPER_USER; stat->gid = SUPER_USER; } else { - stat->uid = mproc[slot - NR_TASKS].mp_effuid; - stat->gid = mproc[slot - NR_TASKS].mp_effgid; + stat->uid = proc_list[slot - NR_TASKS].mpl_uid; + stat->gid = proc_list[slot - NR_TASKS].mpl_gid; } stat->size = 0; @@ -88,72 +79,18 @@ dir_is_pid(struct inode *node) } /* - * Get the process table from the kernel. Check the magic number in the table - * entries. - */ -static int -update_proc_table(void) -{ - int r, slot; - - if ((r = sys_getproctab(proc)) != OK) return r; - - for (slot = 0; slot < NR_PROCS + NR_TASKS; slot++) { - if (proc[slot].p_magic != PMAGIC) { - printf("PROCFS: system version mismatch!\n"); - - return EINVAL; - } - } - - return OK; -} - -/* - * Get the process table from PM. Check the magic number in the table entries. - */ -static int -update_mproc_table(void) -{ - int r, slot; - - r = getsysinfo(PM_PROC_NR, SI_PROC_TAB, mproc, sizeof(mproc)); - if (r != OK) return r; - - for (slot = 0; slot < NR_PROCS; slot++) { - if (mproc[slot].mp_magic != MP_MAGIC) { - printf("PROCFS: PM version mismatch!\n"); - - return EINVAL; - } - } - - return OK; -} - -/* - * Get the process table from VFS. + * Get the process listing from the MIB service. */ static int -update_fproc_table(void) -{ - - return getsysinfo(VFS_PROC_NR, SI_PROC_TAB, fproc, sizeof(fproc)); -} - -/* - * Get the process tables from the kernel, PM, and VFS. - */ -static int -update_tables(void) +update_list(void) { + const int mib[] = { CTL_MINIX, MINIX_PROC, PROC_LIST }; + size_t size; int r; - if ((r = update_proc_table()) != OK) return r; - - if ((r = update_mproc_table()) != OK) return r; - - if ((r = update_fproc_table()) != OK) return r; + size = sizeof(proc_list); + if (__sysctl(mib, __arraycount(mib), proc_list, &size, NULL, 0) != 0) + printf("ProcFS: unable to obtain process list (%d)\n", -errno); return OK; } @@ -168,7 +105,7 @@ init_tree(void) { int i, r; - if ((r = update_tables()) != OK) + if ((r = update_list()) != OK) return r; /* @@ -232,20 +169,14 @@ construct_pid_dirs(void) /* * If the process slot is not in use, delete the associated - * inode. + * inode. Otherwise, get the process ID. */ - if (!slot_in_use(i)) { + if ((pid = pid_from_slot(i)) == 0) { delete_inode(node); continue; } - /* Otherwise, get the process ID. */ - if (i < NR_TASKS) - pid = (pid_t)(i - NR_TASKS); - else - pid = mproc[i - NR_TASKS].mp_pid; - /* * If there is an old entry, see if the pid matches the current * entry, and the owner is still the same. Otherwise, delete @@ -262,7 +193,7 @@ construct_pid_dirs(void) /* Second pass: add new entries. */ for (i = 0; i < NR_PROCS + NR_TASKS; i++) { /* If the process slot is not in use, skip this slot. */ - if (!slot_in_use(i)) + if ((pid = pid_from_slot(i)) == 0) continue; /* @@ -276,7 +207,7 @@ construct_pid_dirs(void) if (i < NR_TASKS) pid = (pid_t)(i - NR_TASKS); else - pid = mproc[i - NR_TASKS].mp_pid; + pid = proc_list[i - NR_TASKS].mpl_pid; /* Add the entry for the process slot. */ snprintf(name, PNAME_MAX + 1, "%d", pid); @@ -360,7 +291,7 @@ construct_pid_entries(struct inode * parent, char * name) assert(slot >= 0 && slot < NR_TASKS + NR_PROCS); /* If this process is already gone, delete the directory now. */ - if (!slot_in_use(slot)) { + if (pid_from_slot(slot) == 0) { delete_inode(parent); return; @@ -434,7 +365,7 @@ lookup_hook(struct inode * parent, char * name, cbdata_t __unused cbdata) now = getticks(); if (last_update != now) { - update_tables(); + update_list(); last_update = now; } @@ -473,7 +404,7 @@ getdents_hook(struct inode * node, cbdata_t __unused cbdata) { if (node == get_root_inode()) { - update_tables(); + update_list(); construct_pid_dirs(); } else if (dir_is_pid(node)) diff --git a/minix/include/minix/endpoint.h b/minix/include/minix/endpoint.h index 74ba24053..0cf3f3bb7 100644 --- a/minix/include/minix/endpoint.h +++ b/minix/include/minix/endpoint.h @@ -2,8 +2,6 @@ #ifndef _MINIX_ENDPOINT_H #define _MINIX_ENDPOINT_H 1 -#ifdef _MINIX_SYSTEM - #include #include #include @@ -70,6 +68,4 @@ #define _ENDPOINT_P(e) \ ((((e)+MAX_NR_TASKS) & (_ENDPOINT_GENERATION_SIZE - 1)) - MAX_NR_TASKS) -#endif /* _MINIX_SYSTEM */ - #endif diff --git a/minix/include/minix/procfs.h b/minix/include/minix/procfs.h index 7194da7d6..a382ea647 100644 --- a/minix/include/minix/procfs.h +++ b/minix/include/minix/procfs.h @@ -1,7 +1,8 @@ #ifndef _MINIX_PROCFS_H #define _MINIX_PROCFS_H -/* The compatibility model is as follows. The current format should be retained +/* + * The compatibility model is as follows. The current format should be retained * for as long as possible; new fields can be added at the end of the line, * because ps/top only read as much as they know of from the start of the line. * Once fields (really) have to be removed, or the whole line becomes too big @@ -9,7 +10,7 @@ * increased PSINFO_VERSION at the beginning. That way, older ps/top copies * will not misinterpret the new fields, but rather fail cleanly. */ -#define PSINFO_VERSION 0 +#define PSINFO_VERSION 1 /* Process types. */ #define TYPE_TASK 'T' @@ -18,23 +19,8 @@ /* General process states. */ #define STATE_SLEEP 'S' -#define STATE_WAIT 'W' #define STATE_ZOMBIE 'Z' #define STATE_RUN 'R' #define STATE_STOP 'T' -/* PM sleep states. */ -#define PSTATE_NONE '-' -#define PSTATE_WAITING 'W' -#define PSTATE_SIGSUSP 'S' - -/* VFS block states. */ -#define FSTATE_NONE '-' -#define FSTATE_PIPE 'P' -#define FSTATE_LOCK 'L' -#define FSTATE_POPEN 'O' -#define FSTATE_SELECT 'S' -#define FSTATE_TASK 'T' -#define FSTATE_UNKNOWN '?' - #endif /* _MINIX_PROCFS_H */ diff --git a/minix/include/minix/sysctl.h b/minix/include/minix/sysctl.h index 839392ea0..ebd1bfda9 100644 --- a/minix/include/minix/sysctl.h +++ b/minix/include/minix/sysctl.h @@ -4,6 +4,7 @@ /* MINIX3-specific sysctl(2) extensions. */ #include +#include /* Special values. */ #define SYSCTL_NODE_FN ((sysctlfn)0x1) /* node is function-driven */ @@ -26,6 +27,7 @@ */ #define MINIX_TEST 0 #define MINIX_MIB 1 +#define MINIX_PROC 2 /* * These identifiers, under MINIX_TEST, are used by test87 to test the MIB @@ -50,4 +52,44 @@ #define MIB_NODES 1 #define MIB_OBJECTS 2 +/* Identifiers for subnodes of MINIX_PROC. */ +#define PROC_LIST 1 +#define PROC_DATA 2 + +/* Structure used for PROC_LIST. Not part of the ABI. Used by ProcFS only. */ +struct minix_proc_list { + uint32_t mpl_flags; /* process flags (MPLF_) */ + pid_t mpl_pid; /* process PID */ + uid_t mpl_uid; /* effective user ID */ + gid_t mpl_gid; /* effective group ID */ +}; +#define MPLF_IN_USE 0x01 /* process slot is in use */ +#define MPLF_ZOMBIE 0x02 /* process is a zombie */ + +/* Structure used for PROC_DATA. Not part of the ABI. Used by ProcFS only. */ +struct minix_proc_data { + endpoint_t mpd_endpoint; /* process endpoint */ + uint32_t mpd_flags; /* procses flags (MPDF_) */ + endpoint_t mpd_blocked_on; /* blocked on other process, or NONE */ + uint32_t mpd_priority; /* current process priority */ + uint32_t mpd_user_time; /* user time, in clock ticks */ + uint32_t mpd_sys_time; /* system time, in clock ticks */ + uint64_t mpd_cycles; /* cycles spent by the process */ + uint64_t mpd_kipc_cycles; /* cycles spent on kernel IPC */ + uint64_t mpd_kcall_cycles; /* cycles spent on kernel calls */ + uint32_t mpd_nice; /* nice value */ + char mpd_name[16]; /* short process name */ +}; +#define MPDF_SYSTEM 0x01 /* process is a system service */ +#define MPDF_ZOMBIE 0x02 /* process is a zombie */ +#define MPDF_RUNNABLE 0x04 /* process is runnable */ +#define MPDF_STOPPED 0x08 /* process is stopped */ + +/* + * Expose sysctl(2) to system services (ProcFS in particular), so as to avoid + * including the CTL_USER subtree handling of sysctl(3) as well. + */ +int __sysctl(const int *, unsigned int, void *, size_t *, const void *, + size_t); + #endif /* !_MINIX_SYSCTL_H */ diff --git a/minix/lib/libminc/Makefile b/minix/lib/libminc/Makefile index 804c8b358..7b545093b 100644 --- a/minix/lib/libminc/Makefile +++ b/minix/lib/libminc/Makefile @@ -289,7 +289,7 @@ CLEANFILES+= ${f:C/\.o/.bc/} mmap.o nanosleep.o open.o pread.o pwrite.o read.o sbrk.o \ select.o setuid.o sigprocmask.o stack_utils.o stat.o stime.o \ svrctl.o syscall.o _ucontext.o umask.o unlink.o wait4.o write.o \ - kill.o + kill.o __sysctl.o ${f} ${f:C/\.o/.bc/}: ${LIBMINIXCDIR}/sys/${f:C/\.o/.c/} OBJS+= ${f} CLEANFILES+= ${f} diff --git a/minix/servers/mib/mib.h b/minix/servers/mib/mib.h index 089628435..e9c598aef 100644 --- a/minix/servers/mib/mib.h +++ b/minix/servers/mib/mib.h @@ -276,6 +276,10 @@ ssize_t mib_kern_proc2(struct mib_call *, struct mib_node *, struct mib_oldp *, struct mib_newp *); ssize_t mib_kern_proc_args(struct mib_call *, struct mib_node *, struct mib_oldp *, struct mib_newp *); +ssize_t mib_minix_proc_list(struct mib_call *, struct mib_node *, + struct mib_oldp *, struct mib_newp *); +ssize_t mib_minix_proc_data(struct mib_call *, struct mib_node *, + struct mib_oldp *, struct mib_newp *); /* subtree modules */ void mib_kern_init(struct mib_node *); diff --git a/minix/servers/mib/minix.c b/minix/servers/mib/minix.c index dbcbdcdcf..683d95063 100644 --- a/minix/servers/mib/minix.c +++ b/minix/servers/mib/minix.c @@ -52,6 +52,15 @@ static struct mib_node mib_minix_mib_table[] = { "dynamically allocated MIB objects"), }; +static struct mib_node mib_minix_proc_table[] = { +/* 1*/ [PROC_LIST] = MIB_FUNC(_P | _RO | CTLTYPE_STRUCT, 0, + mib_minix_proc_list, "list", + "Process list"), +/* 2*/ [PROC_DATA] = MIB_FUNC(_P | _RO | CTLTYPE_NODE, 0, + mib_minix_proc_data, "data", + "Process data"), +}; + static struct mib_node mib_minix_table[] = { #if MINIX_TEST_SUBTREE /* 0*/ [MINIX_TEST] = MIB_NODE(_RW | CTLFLAG_HIDDEN, @@ -60,6 +69,8 @@ static struct mib_node mib_minix_table[] = { #endif /* MINIX_TEST_SUBTREE */ /* 1*/ [MINIX_MIB] = MIB_NODE(_P | _RO, mib_minix_mib_table, "mib", "MIB service information"), +/* 2*/ [MINIX_PROC] = MIB_NODE(_P | _RO, mib_minix_proc_table, + "proc", "Process information for ProcFS"), }; /* diff --git a/minix/servers/mib/proc.c b/minix/servers/mib/proc.c index 1c031872a..277497ff0 100644 --- a/minix/servers/mib/proc.c +++ b/minix/servers/mib/proc.c @@ -1167,3 +1167,119 @@ mib_kern_proc_args(struct mib_call * call, struct mib_node * node __unused, assert(off <= oldlen); return off; } + +/* + * Implementation of CTL_MINIX MINIX_PROC PROC_LIST. + */ +ssize_t +mib_minix_proc_list(struct mib_call * call __unused, + struct mib_node * node __unused, struct mib_oldp * oldp, + struct mib_newp * newp __unused) +{ + struct minix_proc_list mpl[NR_PROCS]; + struct minix_proc_list *mplp; + struct mproc *mp; + unsigned int mslot; + + if (oldp == NULL) + return sizeof(mpl); + + if (!update_tables()) + return EINVAL; + + memset(&mpl, 0, sizeof(mpl)); + + mplp = mpl; + mp = mproc_tab; + + for (mslot = 0; mslot < NR_PROCS; mslot++, mplp++, mp++) { + if (!(mp->mp_flags & IN_USE) || mp->mp_pid <= 0) + continue; + + mplp->mpl_flags = MPLF_IN_USE; + if (mp->mp_flags & (TRACE_ZOMBIE | ZOMBIE)) + mplp->mpl_flags |= MPLF_ZOMBIE; + mplp->mpl_pid = mp->mp_pid; + mplp->mpl_uid = mp->mp_effuid; + mplp->mpl_gid = mp->mp_effgid; + } + + return mib_copyout(oldp, 0, &mpl, sizeof(mpl)); +} + +/* + * Implementation of CTL_MINIX MINIX_PROC PROC_DATA. + */ +ssize_t +mib_minix_proc_data(struct mib_call * call, struct mib_node * node __unused, + struct mib_oldp * oldp, struct mib_newp * newp __unused) +{ + struct minix_proc_data mpd; + struct proc *kp; + int kslot, mslot = 0; + unsigned int mflags; + pid_t pid; + + /* + * It is currently only possible to retrieve the process data for a + * particular PID, which must be given as the last name component. + */ + if (call->call_namelen != 1) + return EINVAL; + + pid = (pid_t)call->call_name[0]; + + if (!update_tables()) + return EINVAL; + + /* + * Unlike the CTL_KERN nodes, we use the ProcFS semantics here: if the + * given PID is negative, it is a kernel task; otherwise, it identifies + * a user process. A request for PID 0 will result in ESRCH. + */ + if (pid < 0) { + if (pid < -NR_TASKS) + return ESRCH; + + kslot = pid + NR_TASKS; + assert(kslot < NR_TASKS); + } else { + if ((mslot = get_mslot(pid)) == NO_SLOT) + return ESRCH; + + kslot = NR_TASKS + mslot; + } + + if (oldp == NULL) + return sizeof(mpd); + + kp = &proc_tab[kslot]; + + mflags = (pid > 0) ? mproc_tab[mslot].mp_flags : 0; + + memset(&mpd, 0, sizeof(mpd)); + mpd.mpd_endpoint = kp->p_endpoint; + if (mflags & PRIV_PROC) + mpd.mpd_flags |= MPDF_SYSTEM; + if (mflags & (TRACE_ZOMBIE | ZOMBIE)) + mpd.mpd_flags |= MPDF_ZOMBIE; + else if ((mflags & TRACE_STOPPED) || RTS_ISSET(kp, RTS_P_STOP)) + mpd.mpd_flags |= MPDF_STOPPED; + else if (proc_is_runnable(kp)) + mpd.mpd_flags |= MPDF_RUNNABLE; + mpd.mpd_blocked_on = P_BLOCKEDON(kp); + mpd.mpd_priority = kp->p_priority; + mpd.mpd_user_time = kp->p_user_time; + mpd.mpd_sys_time = kp->p_sys_time; + mpd.mpd_cycles = kp->p_cycles; + mpd.mpd_kipc_cycles = kp->p_kipc_cycles; + mpd.mpd_kcall_cycles = kp->p_kcall_cycles; + if (kslot >= NR_TASKS) { + mpd.mpd_nice = mproc_tab[mslot].mp_nice; + strlcpy(mpd.mpd_name, mproc_tab[mslot].mp_name, + sizeof(mpd.mpd_name)); + } else + strlcpy(mpd.mpd_name, kp->p_name, sizeof(mpd.mpd_name)); + + return mib_copyout(oldp, 0, &mpd, sizeof(mpd)); +} diff --git a/minix/usr.bin/mtop/mtop.c b/minix/usr.bin/mtop/mtop.c index cafa384ab..858d9e8da 100644 --- a/minix/usr.bin/mtop/mtop.c +++ b/minix/usr.bin/mtop/mtop.c @@ -1,6 +1,5 @@ /* Author: Ben Gras 17 march 2006 */ -/* Modified for ProcFS by Alen Stojanov and David van Moolenbroek */ #define _MINIX_SYSTEM 1 @@ -78,7 +77,7 @@ struct proc { u64_t p_cpucycles[CPUTIMENAMES]; int p_priority; endpoint_t p_blocked; - time_t p_user_time; + clock_t p_user_time; vir_bytes p_memory; uid_t p_effuid; int p_nice; @@ -87,15 +86,14 @@ struct proc { struct proc *proc = NULL, *prev_proc = NULL; -static void parse_file(pid_t pid) +static void +parse_file(pid_t pid) { char path[PATH_MAX], name[256], type, state; - int version, endpt, effuid; - unsigned long cycles_hi, cycles_lo; + int version, endpt; FILE *fp; struct proc *p; int slot; - int i; sprintf(path, "%d/psinfo", pid); @@ -119,8 +117,9 @@ static void parse_file(pid_t pid) slot = SLOT_NR(endpt); - if(slot < 0 || slot >= nr_total) { - fprintf(stderr, "top: unreasonable endpoint number %d\n", endpt); + if (slot < 0 || slot >= nr_total) { + fprintf(stderr, "top: unreasonable endpoint number %d\n", + endpt); fclose(fp); return; } @@ -135,48 +134,19 @@ static void parse_file(pid_t pid) p->p_endpoint = endpt; p->p_pid = pid; - if (fscanf(fp, " %255s %c %d %d %llu %*u %lu %lu", - name, &state, &p->p_blocked, &p->p_priority, - &p->p_user_time, &cycles_hi, &cycles_lo) != 7) { - + if (fscanf(fp, " %255s %c %d %d %u %*u %"PRIu64" %"PRIu64" %"PRIu64 + " %lu %d %u", + name, &state, &p->p_blocked, &p->p_priority, &p->p_user_time, + &p->p_cpucycles[0], &p->p_cpucycles[1], &p->p_cpucycles[2], + &p->p_memory, &p->p_nice, &p->p_effuid) != 11) { fclose(fp); return; } - strncpy(p->p_name, name, sizeof(p->p_name)-1); - p->p_name[sizeof(p->p_name)-1] = 0; + strlcpy(p->p_name, name, sizeof(p->p_name)); if (state != STATE_RUN) p->p_flags |= BLOCKED; - p->p_cpucycles[0] = make64(cycles_lo, cycles_hi); - p->p_memory = 0L; - - if (!(p->p_flags & IS_TASK)) { - int j; - if ((j=fscanf(fp, " %lu %*u %*u %*c %*d %*u %u %*u %d %*c %*d %*u", - &p->p_memory, &effuid, &p->p_nice)) != 3) { - - fclose(fp); - return; - } - - p->p_effuid = effuid; - } else p->p_effuid = 0; - - for(i = 1; i < CPUTIMENAMES; i++) { - if(fscanf(fp, " %lu %lu", - &cycles_hi, &cycles_lo) == 2) { - p->p_cpucycles[i] = make64(cycles_lo, cycles_hi); - } else { - p->p_cpucycles[i] = 0; - } - } - - if ((p->p_flags & IS_TASK)) { - if(fscanf(fp, " %lu", &p->p_memory) != 1) { - p->p_memory = 0; - } - } p->p_flags |= USED; @@ -558,8 +528,15 @@ static void showtop(int cputimemode, int r) } get_procs(); - if (prev_proc == NULL) + if (prev_proc == NULL) { + /* + * A delay short enough to be unnoticable but long enough to + * allow for accumulation of sufficient data for the initial + * display not to show wildly inaccurate numbers. + */ + usleep(100000); get_procs(); + } if((nloads = getloadavg(loads, NLOADS)) != NLOADS) { fprintf(stderr, "getloadavg() failed - %d loads\n", nloads); @@ -694,7 +671,7 @@ int main(int argc, char *argv[]) putchar('\r'); return 0; break; - case 'o': + case ORDERKEY: order++; if(order > ORDER_HIGHEST) order = 0; -- 2.44.0