aboutsummaryrefslogtreecommitdiff
path: root/kernel/proc/proc.c
diff options
context:
space:
mode:
authornthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
committernthnluu <nate1299@me.com>2024-01-28 21:20:27 -0500
commitc63f340d90800895f007de64b7d2d14624263331 (patch)
tree2c0849fa597dd6da831c8707b6f2603403778d7b /kernel/proc/proc.c
Created student weenix repository
Diffstat (limited to 'kernel/proc/proc.c')
-rw-r--r--kernel/proc/proc.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/kernel/proc/proc.c b/kernel/proc/proc.c
new file mode 100644
index 0000000..17ff5db
--- /dev/null
+++ b/kernel/proc/proc.c
@@ -0,0 +1,440 @@
+// SMP.1 + SMP.3
+// spinlock + mask interrupts
+#include "config.h"
+#include "errno.h"
+#include "fs/file.h"
+#include "fs/vfs.h"
+#include "fs/vnode.h"
+#include "globals.h"
+#include "kernel.h"
+#include "mm/slab.h"
+#include "util/debug.h"
+#include "util/printf.h"
+#include "util/string.h"
+#include "util/time.h"
+#include <drivers/screen.h>
+#include <fs/vfs_syscall.h>
+#include <main/apic.h>
+
+/*==========
+ * Variables
+ *=========*/
+
+/*
+ * Global variable that maintains the current process
+ */
+proc_t *curproc CORE_SPECIFIC_DATA;
+
+/*
+ * Global list of all processes (except for the idle process) and its lock
+ */
+static list_t proc_list = LIST_INITIALIZER(proc_list);
+
+/*
+ * Allocator for process descriptors
+ */
+static slab_allocator_t *proc_allocator = NULL;
+
+/*
+ * Statically allocated idle process
+ * Each core has its own idleproc, so the idleproc is stored in static memory
+ * rather than in the global process list
+ */
+proc_t idleproc CORE_SPECIFIC_DATA;
+
+/*
+ * Pointer to the init process
+ */
+static proc_t *proc_initproc = NULL;
+
+/*===============
+ * System startup
+ *==============*/
+
+/*
+ * Initializes the allocator for process descriptors.
+ */
+void proc_init()
+{
+ proc_allocator = slab_allocator_create("proc", sizeof(proc_t));
+ KASSERT(proc_allocator);
+}
+
+/*
+ * Initializes idleproc for the current core. Sets initial values for curproc
+ * and curthr.
+ */
+void proc_idleproc_init()
+{
+ proc_t *proc = &idleproc;
+
+ proc->p_pid = 0;
+ list_init(&proc->p_threads);
+ list_init(&proc->p_children);
+ proc->p_pproc = NULL;
+
+ list_link_init(&proc->p_child_link);
+ list_link_init(&proc->p_list_link);
+
+ proc->p_status = 0;
+ proc->p_state = PROC_RUNNING;
+
+ memset(&proc->p_wait, 0, sizeof(ktqueue_t)); // should not be used
+
+ proc->p_pml4 = pt_get();
+ proc->p_vmmap = vmmap_create();
+
+ proc->p_cwd = NULL;
+
+ memset(proc->p_files, 0, sizeof(proc->p_files));
+
+ char name[8];
+ snprintf(name, sizeof(name), "idle%ld", curcore.kc_id);
+ strncpy(proc->p_name, name, PROC_NAME_LEN);
+ proc->p_name[PROC_NAME_LEN - 1] = '\0';
+
+ dbg(DBG_PROC, "created %s\n", proc->p_name);
+ curproc = &idleproc;
+ curthr = NULL;
+}
+
+/*=================
+ * Helper functions
+ *================*/
+
+/*
+ * Gets the next available process ID (pid).
+ */
+static pid_t next_pid = 1;
+static pid_t _proc_getid()
+{
+ pid_t pid = next_pid;
+restart:
+ list_iterate(&proc_list, p, proc_t, p_list_link)
+ {
+ if (p->p_pid == pid)
+ {
+ pid = pid + 1 == PROC_MAX_COUNT ? 1 : pid + 1;
+ if (pid == next_pid)
+ {
+ return -1;
+ }
+ else
+ {
+ goto restart;
+ }
+ }
+ }
+ next_pid = pid + 1 == PROC_MAX_COUNT ? 1 : pid + 1;
+ KASSERT(pid);
+ return pid;
+}
+
+/*
+ * Searches the global process list for the process descriptor corresponding to
+ * a pid.
+ */
+proc_t *proc_lookup(pid_t pid)
+{
+ if (pid == 0)
+ {
+ return &idleproc;
+ }
+ list_iterate(&proc_list, p, proc_t, p_list_link)
+ {
+ if (p->p_pid == pid)
+ {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+/*==========
+ * Functions
+ *=========*/
+
+/*
+ * Creates a new process with the given name.
+ * Returns the newly created process, or NULL on failure.
+ *
+ * Hints:
+ * Use _proc_getid() to get a new pid.
+ * Allocate a new proc_t with the process slab allocator (proc_allocator).
+ * Use pt_create() to create a new page table (p_pml4).
+ * If the newly created process is the init process (i.e. the generated PID
+ * matches the init process's PID, given by the macro PID_INIT), set the
+ * global proc_initproc to the created process.
+ *
+ * There is some setup to be done for VFS and VM - remember to return to this
+ * function! For VFS, clone and ref the files from curproc. For VM, clone the
+ * vmmap from curproc.
+ *
+ * Be sure to free resources appropriately if proc_create() fails midway!
+ */
+proc_t *proc_create(const char *name)
+{
+ NOT_YET_IMPLEMENTED("PROCS: ***none***");
+ return NULL;
+}
+
+/*
+ * Helper for proc_thread_exiting() that cleans up resources from the current
+ * process in preparation for its destruction (which occurs later via proc_destroy()).
+ * Reparents child processes to the init process, or initiates Weenix shutdown
+ * if the current process is the init process.
+ *
+ * Hints:
+ * You won't have much to clean up until VFS and VM -- remember to revisit this
+ * function later!
+ * **VFS/VM** - there may be some repeat code in proc_destroy()). The initial process
+ * does not have a parent process and thus cleans itself up, hence why we need to cleanup
+ * here as well.
+ *
+ * Remember to set the state and status of the process.
+ * The init process' PID is given by PID_INIT.
+ * Use initproc_finish() to shutdown Weenix when cleaning up the init process.
+ */
+void proc_cleanup(long status)
+{
+ NOT_YET_IMPLEMENTED("PROCS: ***none***");
+}
+
+/*
+ * Cleans up the current process and the current thread, broadcasts on its
+ * parent's p_wait, then forces a context switch. After this, the process is
+ * essentially dead -- this function does not return. The parent must eventually
+ * finish destroying the process.
+ *
+ * Hints:
+ * Use proc_cleanup() to clean up the current process. As retval specifies the current
+ * thread's return value, you should pass (long)retval as the status argument to
+ * proc_cleanup().
+ * Remember to set the exit state and return value of the current thread after calling
+ * proc_cleanup(), as this may block and cause the thread's state to be overwritten.
+ * The context switch should be performed by a call to sched_switch().
+ */
+void proc_thread_exiting(void *retval)
+{
+ NOT_YET_IMPLEMENTED("PROCS: ***none***");
+}
+
+/*
+ * Cancels all the threads of proc. This should never be called on curproc.
+ *
+ * Hints:
+ * The status argument should be passed to kthread_cancel() as the retval.
+ */
+void proc_kill(proc_t *proc, long status)
+{
+ NOT_YET_IMPLEMENTED("PROCS: ***none***");
+}
+
+/*
+ * Kills all processes that are not curproc and not a direct child of idleproc (i.e.,
+ * the init process), then kills the current process.
+ *
+ * Hints:
+ * The PID of the idle process is given by PID_IDLE.
+ * Processes should be killed with a status of -1.
+ * Use do_exit() to kill the current process.
+ */
+void proc_kill_all()
+{
+ NOT_YET_IMPLEMENTED("PROCS: ***none***");
+}
+
+/*
+ * Destroy / free everything from proc. Be sure to remember reference counting
+ * when working on VFS.
+ *
+ * In contrast with proc_cleanup() (in which a process begins to clean itself up), this
+ * will be called on proc by some other process to complete its cleanup.
+ * I.e., the process we are destroying should not be curproc.
+ */
+void proc_destroy(proc_t *proc)
+{
+ list_remove(&proc->p_list_link);
+
+ list_iterate(&proc->p_threads, thr, kthread_t, kt_plink)
+ {
+ kthread_destroy(thr);
+ }
+
+#ifdef __VFS__
+ for (int fd = 0; fd < NFILES; fd++)
+ {
+ if (proc->p_files[fd])
+ fput(proc->p_files + fd);
+ }
+ if (proc->p_cwd)
+ {
+ vput(&proc->p_cwd);
+ }
+#endif
+
+#ifdef __VM__
+ if (proc->p_vmmap)
+ vmmap_destroy(&proc->p_vmmap);
+#endif
+
+ dbg(DBG_THR, "destroying P%d\n", proc->p_pid);
+
+ KASSERT(proc->p_pml4);
+ pt_destroy(proc->p_pml4);
+
+ slab_obj_free(proc_allocator, proc);
+}
+
+/*=============
+ * System calls
+ *============*/
+
+/*
+ * Waits for a child process identified by pid to exit. Finishes destroying the
+ * process and optionally returns the child's status in status.
+ *
+ * If pid is a positive integer, tries to clean up the process specified by pid.
+ * If pid is -1, cleans up any child process of curproc that exits.
+ *
+ * Returns the pid of the child process that exited, or error cases:
+ * - ENOTSUP: pid is 0, a negative number not equal to -1,
+ * or options are specified (options does not equal 0)
+ * - ECHILD: pid is a positive integer but not a child of curproc, or
+ * pid is -1 and the process has no children
+ *
+ * Hints:
+ * Use sched_sleep_on() to be notified of a child process exiting.
+ * Destroy an exited process by removing it from any lists and calling
+ * proc_destroy(). Remember to set status (if it was provided) to the child's
+ * status before destroying the process.
+ * If waiting on a specific child PID, wakeups from other exiting child
+ * processes should be ignored.
+ * If waiting on any child (-1), do_waitpid can return when *any* child has exited,
+ * it does not have to return the one that exited earliest.
+ * Which field can you use to determine whether a given process exited?
+ */
+pid_t do_waitpid(pid_t pid, int *status, int options)
+{
+ NOT_YET_IMPLEMENTED("PROCS: ***none***");
+ return 0;
+}
+
+/*
+ * Wrapper around kthread_exit.
+ */
+void do_exit(long status)
+{
+ NOT_YET_IMPLEMENTED("PROCS: ***none***");
+}
+
+/*==========
+ * Debugging
+ *=========*/
+
+size_t proc_info(const void *arg, char *buf, size_t osize)
+{
+ const proc_t *p = (proc_t *)arg;
+ size_t size = osize;
+ proc_t *child;
+
+ KASSERT(NULL != p);
+ KASSERT(NULL != buf);
+
+ iprintf(&buf, &size, "pid: %i\n", p->p_pid);
+ iprintf(&buf, &size, "name: %s\n", p->p_name);
+ if (NULL != p->p_pproc)
+ {
+ iprintf(&buf, &size, "parent: %i (%s)\n", p->p_pproc->p_pid,
+ p->p_pproc->p_name);
+ }
+ else
+ {
+ iprintf(&buf, &size, "parent: -\n");
+ }
+
+ if (list_empty(&p->p_children))
+ {
+ iprintf(&buf, &size, "children: -\n");
+ }
+ else
+ {
+ iprintf(&buf, &size, "children:\n");
+ }
+ list_iterate(&p->p_children, child, proc_t, p_child_link)
+ {
+ iprintf(&buf, &size, " %i (%s)\n", child->p_pid, child->p_name);
+ }
+
+ iprintf(&buf, &size, "status: %ld\n", p->p_status);
+ iprintf(&buf, &size, "state: %i\n", p->p_state);
+
+#ifdef __VFS__
+#ifdef __GETCWD__
+ if (NULL != p->p_cwd)
+ {
+ char cwd[256];
+ lookup_dirpath(p->p_cwd, cwd, sizeof(cwd));
+ iprintf(&buf, &size, "cwd: %-s\n", cwd);
+ }
+ else
+ {
+ iprintf(&buf, &size, "cwd: -\n");
+ }
+#endif /* __GETCWD__ */
+#endif
+
+#ifdef __VM__
+ iprintf(&buf, &size, "start brk: 0x%p\n", p->p_start_brk);
+ iprintf(&buf, &size, "brk: 0x%p\n", p->p_brk);
+#endif
+
+ return size;
+}
+
+size_t proc_list_info(const void *arg, char *buf, size_t osize)
+{
+ size_t size = osize;
+
+ KASSERT(NULL == arg);
+ KASSERT(NULL != buf);
+
+#if defined(__VFS__) && defined(__GETCWD__)
+ iprintf(&buf, &size, "%5s %-13s %-18s %-s\n", "PID", "NAME", "PARENT",
+ "CWD");
+#else
+ iprintf(&buf, &size, "%5s %-13s %-s\n", "PID", "NAME", "PARENT");
+#endif
+
+ list_iterate(&proc_list, p, proc_t, p_list_link)
+ {
+ char parent[64];
+ if (NULL != p->p_pproc)
+ {
+ snprintf(parent, sizeof(parent), "%3i (%s)", p->p_pproc->p_pid,
+ p->p_pproc->p_name);
+ }
+ else
+ {
+ snprintf(parent, sizeof(parent), " -");
+ }
+
+#if defined(__VFS__) && defined(__GETCWD__)
+ if (NULL != p->p_cwd)
+ {
+ char cwd[256];
+ lookup_dirpath(p->p_cwd, cwd, sizeof(cwd));
+ iprintf(&buf, &size, " %3i %-13s %-18s %-s\n", p->p_pid, p->p_name,
+ parent, cwd);
+ }
+ else
+ {
+ iprintf(&buf, &size, " %3i %-13s %-18s -\n", p->p_pid, p->p_name,
+ parent);
+ }
+#else
+ iprintf(&buf, &size, " %3i %-13s %-s\n", p->p_pid, p->p_name, parent);
+#endif
+ }
+ return size;
+}