diff --git a/Makefile b/Makefile index 2584e4a..a8de518 100644 --- a/Makefile +++ b/Makefile @@ -125,6 +125,7 @@ UPROGS=\ $U/_ln\ $U/_ls\ $U/_mkdir\ + $U/_ps\ $U/_rm\ $U/_sh\ $U/_stressfs\ diff --git a/docs/getprocs.md b/docs/getprocs.md new file mode 100644 index 0000000..55e2b62 --- /dev/null +++ b/docs/getprocs.md @@ -0,0 +1,72 @@ +# GETPROCS(2) System Calls Manual GETPROCS(2) + +## NAME +getprocs - get information about active processes + +## SYNOPSIS +```c +#include + +int getprocs(struct proc_data *pd, int max); +``` + +## DESCRIPTION +The `getprocs()` function retrieves information about currently active processes in the system. It populates an array of `struct proc_data` structures with details about each process, up to a maximum number specified by the caller. + +The `struct proc_data` is defined as follows: + +```c +struct proc_data { + int pid; /* Process ID */ + int ppid; /* Parent process ID */ + int state; /* Process state */ + int sz; /* Size of process memory */ + char name[16]; /* Name of the process */ +}; +``` + +The function takes two arguments: +- `pd`: A pointer to an array of `struct proc_data` where the process information will be stored. +- `max`: The maximum number of processes to retrieve information for up to NPROC processes + +## RETURN VALUE +On success, `getprocs()` returns the number of processes for which information was retrieved. This number is less than or equal to `max`. + +On error, -1 is returned. + +## ERRORS +The `getprocs()` function will fail if: +- `pd` is NULL. +- `max` is less than or equal to 0. + +## NOTES +- If `max` is greater than `NPROC` (the maximum number of processes in the system), it will be capped at `NPROC`. +- The function only retrieves information for processes that are not in the `UNUSED` state. +- The `ppid` field will be 0 if the process has no parent (i.e., for the init process). +- The process name is safely copied and null-terminated. + +## EXAMPLE +```c +#include + +#define MAX_PROCS 64 + +int main() { + struct proc_data pd[MAX_PROCS]; + int count = getprocs(pd, MAX_PROCS); + + if (count == -1) { + printf("Error calling getprocs\n"); + exit(1); + } + + for (int i = 0; i < count; i++) { + printf("PID: %d, PPID: %d, Name: %s\n", pd[i].pid, pd[i].ppid, pd[i].name); + } + + exit(0); +} +``` + +## SEE ALSO +fork(2), exec(2), wait(2), exit(2) diff --git a/docs/ps.md b/docs/ps.md new file mode 100644 index 0000000..5982c6d --- /dev/null +++ b/docs/ps.md @@ -0,0 +1,64 @@ +# PS(1) User Commands PS(1) + +## NAME +ps - report process status + +## SYNOPSIS +``` +ps [-h] +``` + +## DESCRIPTION +The `ps` utility displays information about active processes in the system. Without any options, it shows a list of all processes including their PID, PPID, state, size, and name. + +## OPTIONS +-h +: Display help information and exit. + +## OUTPUT +The `ps` command displays the following information for each process: + +PID +: The process ID. + +PPID +: The parent process ID. + +STATE +: The current state of the process. Possible states are: + - unused: Process slot is not in use + - used: Process is allocated but not fully initialized + - sleep: Process is sleeping + - runble: Process is ready to run + - run: Process is currently running + - zombie: Process has terminated but not yet been cleaned up + +SIZE +: The size of the process memory in bytes. + +NAME +: The name of the process. + +## EXIT STATUS +The `ps` utility exits 0 on success, and 1 if an error occurs. + +## EXAMPLES +To display information about all processes: +``` +$ ps +``` + +To display help information: +``` +$ ps -h +``` + +## NOTES +- The `ps` command relies on the `getprocs` system call to retrieve process information. +- The maximum number of processes that can be displayed is limited by the `NPROC` constant defined in the system. + +## SEE ALSO +getprocs(2), fork(2), exec(2) + +## BUGS +If an invalid state is encountered, it will be displayed as "???". diff --git a/kernel/defs.h b/kernel/defs.h index a3c962b..8a881a3 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -8,6 +8,7 @@ struct spinlock; struct sleeplock; struct stat; struct superblock; +struct proc_data; // bio.c void binit(void); @@ -106,6 +107,7 @@ void yield(void); int either_copyout(int user_dst, uint64 dst, void *src, uint64 len); int either_copyin(void *dst, int user_src, uint64 src, uint64 len); void procdump(void); +int getprocs(struct proc_data*, int max); // swtch.S void swtch(struct context*, struct context*); diff --git a/kernel/proc.c b/kernel/proc.c index 031eeb5..c334e8d 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -688,3 +688,50 @@ procdump(void) printf("\n"); } } + +int +getprocs(struct proc_data *pd, int max) + /** + * Retrieves information about running processes and stores it in an array. + * + * @param pd Pointer to an array of proc_data structures where process information will be stored. + * @param max Maximum number of processes to retrieve information for. + * + * @return The number of processes for which information was retrieved, or -1 if input parameters are invalid. + * + * @details This function iterates through the process table and collects information about + * running processes. It stores this information in the provided array of proc_data + * structures. The function will retrieve information for up to 'max' processes or + * until it reaches the end of the process table (NPROC), whichever comes first. + * + * @note The function uses copyout to safely copy data to user space. + * + * @warning This function assumes that the caller has allocated sufficient memory for the + * proc_data array to hold information for 'max' processes. + **/ +{ + struct proc *p; + struct proc_data tpd; + int count = 0; + + if (!pd || max <= 0) { + return -1; + } + + max = (max > NPROC) ? NPROC : max; + + for(p = proc; p < &proc[NPROC] && count < max; p++) { + if (p->state == UNUSED) { + continue; + } + tpd.pid = p->pid; + tpd.ppid = p->parent ? p->parent->pid : 0; + tpd.state = p->state; + tpd.sz = p->sz; + safestrcpy(tpd.name, p->name, sizeof(tpd.name)); + copyout(myproc()->pagetable, (uint64)&pd[count], (char *)&tpd, sizeof(struct proc_data)); + count++; + } + + return count; +} diff --git a/kernel/proc.h b/kernel/proc.h index d021857..871446f 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -105,3 +105,12 @@ struct proc { struct inode *cwd; // Current directory char name[16]; // Process name (debugging) }; + +// procstate data used by getprocs +struct proc_data { + int pid; + int ppid; + int state; + uint sz; + char name[16]; +}; diff --git a/kernel/syscall.c b/kernel/syscall.c index ed65409..f6ea699 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -101,31 +101,33 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_getprocs(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. static uint64 (*syscalls[])(void) = { -[SYS_fork] sys_fork, -[SYS_exit] sys_exit, -[SYS_wait] sys_wait, -[SYS_pipe] sys_pipe, -[SYS_read] sys_read, -[SYS_kill] sys_kill, -[SYS_exec] sys_exec, -[SYS_fstat] sys_fstat, -[SYS_chdir] sys_chdir, -[SYS_dup] sys_dup, -[SYS_getpid] sys_getpid, -[SYS_sbrk] sys_sbrk, -[SYS_sleep] sys_sleep, -[SYS_uptime] sys_uptime, -[SYS_open] sys_open, -[SYS_write] sys_write, -[SYS_mknod] sys_mknod, -[SYS_unlink] sys_unlink, -[SYS_link] sys_link, -[SYS_mkdir] sys_mkdir, -[SYS_close] sys_close, +[SYS_fork] sys_fork, +[SYS_exit] sys_exit, +[SYS_wait] sys_wait, +[SYS_pipe] sys_pipe, +[SYS_read] sys_read, +[SYS_kill] sys_kill, +[SYS_exec] sys_exec, +[SYS_fstat] sys_fstat, +[SYS_chdir] sys_chdir, +[SYS_dup] sys_dup, +[SYS_getpid] sys_getpid, +[SYS_sbrk] sys_sbrk, +[SYS_sleep] sys_sleep, +[SYS_uptime] sys_uptime, +[SYS_open] sys_open, +[SYS_write] sys_write, +[SYS_mknod] sys_mknod, +[SYS_unlink] sys_unlink, +[SYS_link] sys_link, +[SYS_mkdir] sys_mkdir, +[SYS_close] sys_close, +[SYS_getprocs] sys_getprocs }; void diff --git a/kernel/syscall.h b/kernel/syscall.h index bc5f356..15bf85c 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -1,22 +1,23 @@ // System call numbers -#define SYS_fork 1 -#define SYS_exit 2 -#define SYS_wait 3 -#define SYS_pipe 4 -#define SYS_read 5 -#define SYS_kill 6 -#define SYS_exec 7 -#define SYS_fstat 8 -#define SYS_chdir 9 -#define SYS_dup 10 -#define SYS_getpid 11 -#define SYS_sbrk 12 -#define SYS_sleep 13 -#define SYS_uptime 14 -#define SYS_open 15 -#define SYS_write 16 -#define SYS_mknod 17 -#define SYS_unlink 18 -#define SYS_link 19 -#define SYS_mkdir 20 -#define SYS_close 21 +#define SYS_fork 1 +#define SYS_exit 2 +#define SYS_wait 3 +#define SYS_pipe 4 +#define SYS_read 5 +#define SYS_kill 6 +#define SYS_exec 7 +#define SYS_fstat 8 +#define SYS_chdir 9 +#define SYS_dup 10 +#define SYS_getpid 11 +#define SYS_sbrk 12 +#define SYS_sleep 13 +#define SYS_uptime 14 +#define SYS_open 15 +#define SYS_write 16 +#define SYS_mknod 17 +#define SYS_unlink 18 +#define SYS_link 19 +#define SYS_mkdir 20 +#define SYS_close 21 +#define SYS_getprocs 22 diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 1de184e..e8611ee 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -89,3 +89,24 @@ sys_uptime(void) release(&tickslock); return xticks; } + +uint64 +sys_getprocs(void) { + /** + * Retrieves information about running processes and stores it in an array. + * + * @param pd Pointer to an array of proc_data structures where process information will be stored. + * arg[0] retrieved via argaddr + * + * @param max Maximum number of processes to retrieve information for. + * arg[1] retrieved via agrint + * + * @return The number of processes for which information was retrieved, or -1 if input parameters are invalid. + * + */ + struct proc_data *pd; + int max; + argaddr(0, (uint64 *)&pd); + argint(1, &max); + return getprocs(pd, max); +} diff --git a/user/ps.c b/user/ps.c new file mode 100644 index 0000000..050a809 --- /dev/null +++ b/user/ps.c @@ -0,0 +1,72 @@ +/** + * @file ps.c + * @brief Implementation of a simple 'ps' command for xv6. + * + * This program reports the status of processes in the system. + * -h for minimal help + */ + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "kernel/param.h" +#include "user/user.h" + +// NPROC defined in param.h +#define MAX_PROCS NPROC +#define NELEM(x) (sizeof(x)/sizeof((x)[0])) + + +struct proc_data { + int pid; + int ppid; + int state; + int sz; + char name[16]; +}; + +int main (int argc, char *argv[]) { + // copied from procdump + static char *states[] = { + "unused", + "used", + "sleep ", + "runble", + "run ", + "zombie" + }; + char *state; + struct proc_data pd[MAX_PROCS]; + + if(argc == 2 && strcmp(argv[1], "-h") == 0){ + printf("ps -- report process status\n"); + printf("usage: ps [-h]\n"); + printf("-h: print this helpful information\n"); + exit(0); + } else if (argc == 2) { + printf("Invalid parameter, ps -h for help\n"); + exit(1); + } + + int tot = getprocs(pd, MAX_PROCS); + if (tot < 0) { + printf("getprocs failed\n"); + exit(1); + } + + printf("%d processes are running\n", tot); + printf("PID\tPPID\tSTATE\tSIZE\tNAME\n"); + + int i; + for (i = 0; i < tot; i++) { + if(pd[i].state >= 0 && pd[i].state < NELEM(states) && states[pd[i].state]) { + state = states[pd[i].state]; + } + else { + state = "???"; + } + printf("%d\t%d\t%s\t%d\t%s\n", pd[i].pid, pd[i].ppid, state, pd[i].sz, pd[i].name); + + } + exit(0); +} diff --git a/user/user.h b/user/user.h index 2e6fc55..79b9823 100644 --- a/user/user.h +++ b/user/user.h @@ -1,4 +1,5 @@ struct stat; +struct proc_data; // system calls int fork(void); @@ -22,6 +23,7 @@ int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); +int getprocs(struct proc_data *, int); // ulib.c int stat(const char*, struct stat*); diff --git a/user/usertests.c b/user/usertests.c index 7d3e9bc..1acc804 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -2571,6 +2571,65 @@ badarg(char *s) exit(0); } +// test to make sure getprocs returns the right number of procs + struct proc_data { + int pid; + int ppid; + int state; + int sz; + char name[16]; + }; + +void +getprocstest(char *s) +{ + struct proc_data pd_array[NPROC]; + + int tot_procs = getprocs(pd_array, NPROC); + printf("%s: getprocs test1 returned %d procs\n", s, tot_procs); + if(tot_procs < 0){ + printf("%s: getprocs test1 failed\n", s); + exit(1); + } + + if (tot_procs != 4) { + printf("%s: invalid results for getprocs test1, expected 4 procs received %d\n", s, tot_procs); + exit(1); + } + + tot_procs = getprocs(pd_array, 1); // test to find 1 proc + printf("%s: getprocs test2 returned %d procs\n", s, tot_procs); + if(tot_procs < 0){ + printf("%s: getprocs test2 failed\n", s); + exit(1); + } + + if (tot_procs != 1) { + printf("%s: invalid results for getprocs test2, expected 1 procs received %d\n", s, tot_procs); + exit(1); + } + + tot_procs = getprocs(pd_array, NPROC + 1); + printf("%s: getprocs test3 returned %d procs\n", s, tot_procs); + if(tot_procs < 0){ + printf("%s: getprocs test3 failed\n", s); + exit(1); + } + + if (tot_procs != 4) { + printf("%s: invalid results for getprocs test3, expected 4 procs received %d\n", s, tot_procs); + exit(1); + } + + tot_procs = getprocs(pd_array, 0); + if (tot_procs >= 0) { + printf("%s: invalid results for getprocs test4, expected error received %d\n", s, tot_procs); + exit(1); + } + + printf("%s: getprocs test OK\n", s); +} + struct test { void (*f)(char *); char *s; @@ -2635,6 +2694,7 @@ struct test { {sbrklast, "sbrklast"}, {sbrk8000, "sbrk8000"}, {badarg, "badarg" }, + {getprocstest, "getprocstest"}, { 0, 0}, }; diff --git a/user/usys.pl b/user/usys.pl index 01e426e..54ce97f 100755 --- a/user/usys.pl +++ b/user/usys.pl @@ -36,3 +36,4 @@ sub entry { entry("sbrk"); entry("sleep"); entry("uptime"); +entry("getprocs");