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/<pid>/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
This commit is contained in:
David van Moolenbroek 2015-11-08 11:56:52 +00:00 committed by Lionel Sambuc
parent b89261ba01
commit 305e366fe4
13 changed files with 348 additions and 428 deletions

View File

@ -1,8 +1,6 @@
#ifndef _PROCFS_GLO_H
#define _PROCFS_GLO_H
#include <minix/param.h>
/* 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 */

View File

@ -2,6 +2,8 @@
#define _PROCFS_INC_H
#include <minix/drivers.h>
#include <minix/param.h>
#include <minix/sysctl.h>
#include <minix/sysinfo.h>
#include <minix/vtreefs.h>
#include <minix/procfs.h>
@ -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"

View File

@ -5,10 +5,6 @@
#include <sys/mman.h>
#include <minix/vm.h>
#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);
@ -35,213 +31,105 @@ is_zombie(int slot)
{
return (slot >= NR_TASKS &&
(mproc[slot - NR_TASKS].mp_flags & (TRACE_ZOMBIE | ZOMBIE)));
(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.
*/
int
get_proc_data(pid_t pid, struct minix_proc_data * mpd)
{
int mib[4] = { CTL_MINIX, MINIX_PROC, PROC_DATA, pid };
size_t oldlen;
oldlen = sizeof(*mpd);
if (__sysctl(mib, __arraycount(mib), mpd, &oldlen, NULL, 0) != 0)
return -errno;
return OK;
}
/*
* 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
pid_psinfo(int i)
pid_psinfo(int slot)
{
int pi, task, state, type, p_state, f_state;
char name[PROC_NAME_LEN+1], *p;
struct minix_proc_data mpd;
struct vm_usage_info vui;
pid_t ppid;
pid_t pid;
uid_t uid;
char *p;
int task, type, state;
pi = i - NR_TASKS;
task = proc[i].p_nr < 0;
if ((pid = pid_from_slot(slot)) == 0)
return;
/* 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;
if (get_proc_data(pid, &mpd) != OK)
return;
task = (slot < NR_TASKS);
/* Get the type of the process. */
if (task)
type = TYPE_TASK;
else if (mproc[i].mp_flags & PRIV_PROC)
else if (mpd.mpd_flags & MPDF_SYSTEM)
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.
/*
* 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.
*/
buf_printf("%d %c %d %s %c %d %d %lu %lu %lu %lu",
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;
/* Get the process's effective user ID. */
if (!task)
uid = proc_list[slot - NR_TASKS].mpl_uid;
else
uid = 0;
/* 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);
/* Spaces in the process name would mess up the output format. */
if ((p = strchr(mpd.mpd_name, ' ')) != NULL)
*p = '\0';
/* 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 */
(int)proc[i].p_endpoint, /* process endpoint */
name, /* process name */
mpd.mpd_endpoint, /* process endpoint */
mpd.mpd_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)
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 */
);
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");
}
/*
* If we allocated memory dynamically during a call to get_frame(), free it up
* here.
*/
static void
put_frame(void)
{
if (frame != s_frame)
free(frame);
}
/*
* 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 (proc[slot].p_nr < 0 || is_zombie(slot))
return FALSE;
/*
* 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().
*/
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 */
nargs = *(size_t *)frame;
if (nargs < 1 ||
sizeof(size_t) + sizeof(char *) * (nargs + 1) > size) {
put_frame();
return FALSE;
}
*basep = base;
*sizep = size;
*nargsp = nargs;
/* The caller now has to called put_frame() to clean up. */
return TRUE;
}
/*
@ -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)];
mib[2] = proc_list[slot - NR_TASKS].mpl_pid;
for (i = 0; i < nargs; i++) {
ptr = (vir_bytes)argv[i] - base;
/* TODO: zero-copy into the main output buffer */
oldlen = sizeof(buf);
/* Check for bad pointers. */
if ((long)ptr < 0L || ptr >= size)
break;
if (__sysctl(mib, __arraycount(mib), buf, &oldlen, NULL, 0) != 0)
return;
len = strlen(&frame[ptr]) + 1;
buf_append(&frame[ptr], len);
}
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];
mib[2] = proc_list[slot - NR_TASKS].mpl_pid;
for (;;) {
/* Make sure there is no buffer overrun. */
if (off + sizeof(char *) > size)
break;
/* TODO: zero-copy into the main output buffer */
oldlen = sizeof(buf);
ptr = (vir_bytes) *envp;
if (__sysctl(mib, __arraycount(mib), buf, &oldlen, NULL, 0) != 0)
return;
/* Stop at the terminating NULL pointer. */
if (ptr == 0L)
break;
ptr -= base;
/* 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++;
}
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);
}

View File

@ -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 */

View File

@ -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.
* Get the process listing from the MIB service.
*/
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.
*/
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))

View File

@ -2,8 +2,6 @@
#ifndef _MINIX_ENDPOINT_H
#define _MINIX_ENDPOINT_H 1
#ifdef _MINIX_SYSTEM
#include <minix/sys_config.h>
#include <minix/com.h>
#include <limits.h>
@ -70,6 +68,4 @@
#define _ENDPOINT_P(e) \
((((e)+MAX_NR_TASKS) & (_ENDPOINT_GENERATION_SIZE - 1)) - MAX_NR_TASKS)
#endif /* _MINIX_SYSTEM */
#endif

View File

@ -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 */

View File

@ -4,6 +4,7 @@
/* MINIX3-specific sysctl(2) extensions. */
#include <sys/sysctl.h>
#include <minix/endpoint.h>
/* 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 */

View File

@ -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}

View File

@ -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 *);

View File

@ -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"),
};
/*

View File

@ -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));
}

View File

@ -1,6 +1,5 @@
/* Author: Ben Gras <beng@few.vu.nl> 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;