diff options
author | Freya Murphy <freya@freyacat.org> | 2025-03-25 17:36:52 -0400 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2025-03-25 17:38:22 -0400 |
commit | 6af21e6a4f2251e71353562d5df7f376fdffc270 (patch) | |
tree | de20c7afc9878422c81e34f30c6b010075e9e69a /kernel/procs.c | |
download | comus-6af21e6a4f2251e71353562d5df7f376fdffc270.tar.gz comus-6af21e6a4f2251e71353562d5df7f376fdffc270.tar.bz2 comus-6af21e6a4f2251e71353562d5df7f376fdffc270.zip |
initial checkout from wrc
Diffstat (limited to 'kernel/procs.c')
-rw-r--r-- | kernel/procs.c | 1136 |
1 files changed, 1136 insertions, 0 deletions
diff --git a/kernel/procs.c b/kernel/procs.c new file mode 100644 index 0000000..96bb3fd --- /dev/null +++ b/kernel/procs.c @@ -0,0 +1,1136 @@ +/* +** @file procs.c +** +** @author CSCI-452 class of 20245 +** +** @brief Process-related implementations +*/ + +#define KERNEL_SRC + +#include <common.h> + +#include <procs.h> +#include <user.h> + +/* +** PRIVATE DEFINITIONS +*/ + +// determine if a queue is empty; assumes 'q' is a valid pointer +#define PCB_QUEUE_EMPTY(q) ((q)->head == NULL) + +/* +** PRIVATE DATA TYPES +*/ + +/* +** PCB Queue structure +** +** Opaque to the rest of the kernel +** +** Typedef'd in the header: typedef struct pcb_queue_s *pcb_queue_t; +*/ +struct pcb_queue_s { + pcb_t *head; + pcb_t *tail; + enum pcb_queue_order_e order; +}; + +/* +** PRIVATE GLOBAL VARIABLES +*/ + +// collection of queues +static struct pcb_queue_s pcb_freelist_queue; +static struct pcb_queue_s ready_queue; +static struct pcb_queue_s waiting_queue; +static struct pcb_queue_s sleeping_queue; +static struct pcb_queue_s zombie_queue; +static struct pcb_queue_s sioread_queue; + +/* +** PUBLIC GLOBAL VARIABLES +*/ + +// public-facing queue handles +pcb_queue_t pcb_freelist; +pcb_queue_t ready; +pcb_queue_t waiting; +pcb_queue_t sleeping; +pcb_queue_t zombie; +pcb_queue_t sioread; + +// pointer to the currently-running process +pcb_t *current; + +// the process table +pcb_t ptable[N_PROCS]; + +// next available PID +uint_t next_pid; + +// pointer to the PCB for the 'init' process +pcb_t *init_pcb; + +// table of state name strings +const char *state_str[N_STATES] = { + [ STATE_UNUSED ] = "Unu", // "Unused" + [ STATE_NEW ] = "New", + [ STATE_READY ] = "Rdy", // "Ready" + [ STATE_RUNNING ] = "Run", // "Running" + [ STATE_SLEEPING ] = "Slp", // "Sleeping" + [ STATE_BLOCKED ] = "Blk", // "Blocked" + [ STATE_WAITING ] = "Wat", // "Waiting" + [ STATE_KILLED ] = "Kil", // "Killed" + [ STATE_ZOMBIE ] = "Zom" // "Zombie" +}; + +// table of priority name strings +const char *prio_str[N_PRIOS] = { + [ PRIO_HIGH ] = "High", + [ PRIO_STD ] = "User", + [ PRIO_LOW ] = "Low ", + [ PRIO_DEFERRED ] = "Def " +}; + +// table of queue ordering name strings +const char *ord_str[N_PRIOS] = { + [ O_FIFO ] = "FIFO", + [ O_PRIO ] = "PRIO", + [ O_PID ] = "PID ", + [ O_WAKEUP ] = "WAKE" +}; + +/* +** PRIVATE FUNCTIONS +*/ + +/** +** Priority search functions. These are used to traverse a supplied +** queue looking for the queue entry that would precede the supplied +** PCB when that PCB is inserted into the queue. +** +** Variations: +** find_prev_wakeup() compares wakeup times +** find_prev_priority() compares process priorities +** find_prev_pid() compares PIDs +** +** Each assumes the queue should be in ascending order by the specified +** comparison value. +** +** @param[in] queue The queue to search +** @param[in] pcb The PCB to look for +** +** @return a pointer to the predecessor in the queue, or NULL if +** this PCB would be at the beginning of the queue. +*/ +static pcb_t *find_prev_wakeup( pcb_queue_t queue, pcb_t *pcb ) { + + // sanity checks! + assert1( queue != NULL ); + assert1( pcb != NULL ); + + pcb_t *prev = NULL; + pcb_t *curr = queue->head; + + while( curr != NULL && curr->wakeup <= pcb->wakeup ) { + prev = curr; + curr = curr->next; + } + + return prev; +} + +static pcb_t *find_prev_priority( pcb_queue_t queue, pcb_t *pcb ) { + + // sanity checks! + assert1( queue != NULL ); + assert1( pcb != NULL ); + + pcb_t *prev = NULL; + pcb_t *curr = queue->head; + + while( curr != NULL && curr->priority <= pcb->priority ) { + prev = curr; + curr = curr->next; + } + + return prev; +} + +static pcb_t *find_prev_pid( pcb_queue_t queue, pcb_t *pcb ) { + + // sanity checks! + assert1( queue != NULL ); + assert1( pcb != NULL ); + + pcb_t *prev = NULL; + pcb_t *curr = queue->head; + + while( curr != NULL && curr->pid <= pcb->pid ) { + prev = curr; + curr = curr->next; + } + + return prev; +} + +/* +** PUBLIC FUNCTIONS +*/ + +// a macro to simplify queue setup +#define QINIT(q,s) \ + q = &q##_queue; \ + if( pcb_queue_reset(q,s) != SUCCESS ) { \ + PANIC( 0, "pcb_init can't reset " # q ); \ + } + +/** +** Name: pcb_init +** +** Initialization for the Process module. +*/ +void pcb_init( void ) { + +#if TRACING_INIT + cio_puts( " Procs" ); +#endif + + // there is no current process + current = NULL; + + // set up the external links to the queues + QINIT( pcb_freelist, O_FIFO ); + QINIT( ready, O_PRIO ); + QINIT( waiting, O_PID ); + QINIT( sleeping, O_WAKEUP ); + QINIT( zombie, O_PID ); + QINIT( sioread, O_FIFO ); + + /* + ** We statically allocate our PCBs, so we need to add them + ** to the freelist before we can use them. If this changes + ** so that we dynamicallyl allocate PCBs, this step either + ** won't be required, or could be used to pre-allocate some + ** number of PCB structures for future use. + */ + + pcb_t *ptr = ptable; + for( int i = 0; i < N_PROCS; ++i ) { + pcb_free( ptr ); + ++ptr; + } +} + +/** +** Name: pcb_alloc +** +** Allocate a PCB from the list of free PCBs. +** +** @param pcb Pointer to a pcb_t * where the PCB pointer will be returned. +** +** @return status of the allocation attempt +*/ +int pcb_alloc( pcb_t **pcb ) { + + // sanity check! + assert1( pcb != NULL ); + + // remove the first PCB from the free list + pcb_t *tmp; + if( pcb_queue_remove(pcb_freelist,&tmp) != SUCCESS ) { + return E_NO_PCBS; + } + + *pcb = tmp; + return SUCCESS; +} + +/** +** Name: pcb_free +** +** Return a PCB to the list of free PCBs. +** +** @param pcb Pointer to the PCB to be deallocated. +*/ +void pcb_free( pcb_t *pcb ) { + + if( pcb != NULL ) { + // mark the PCB as available + pcb->state = STATE_UNUSED; + + // add it to the free list + int status = pcb_queue_insert( pcb_freelist, pcb ); + + // if that failed, we're in trouble + if( status != SUCCESS ) { + sprint( b256, "pcb_free(0x%08x) status %d", (uint32_t) pcb, + status ); + PANIC( 0, b256 ); + } + } +} + +/** +** Name: pcb_zombify +** +** Turn the indicated process into a Zombie. This function +** does most of the real work for exit() and kill() calls. +** Is also called from the scheduler and dispatcher. +** +** @param pcb Pointer to the newly-undead PCB +*/ +void pcb_zombify( register pcb_t *victim ) { + + // should this be an error? + if( victim == NULL ) { + return; + } + + // every process must have a parent, even if it's 'init' + assert( victim->parent != NULL ); + + /* + ** We need to locate the parent of this process. We also need + ** to reparent any children of this process. We do these in + ** a single loop. + */ + pcb_t *parent = victim->parent; + pcb_t *zchild = NULL; + + // two PIDs we will look for + uint_t vicpid = victim->pid; + + // speed up access to the process table entries + register pcb_t *curr = ptable; + + for( int i = 0; i < N_PROCS; ++i, ++curr ) { + + // make sure this is a valid entry + if( curr->state == STATE_UNUSED ) { + continue; + } + + // if this is our parent, just keep going - we continue + // iterating to find all the children of this process. + if( curr == parent ) { + continue; + } + + if( curr->parent == victim ) { + + // found a child - reparent it + curr->parent = init_pcb; + + // see if this child is already undead + if( curr->state == STATE_ZOMBIE ) { + // if it's already a zombie, remember it, so we + // can pass it on to 'init'; also, if there are + // two or more zombie children, it doesn't matter + // which one we pick here, as the others will be + // collected when 'init' loops + zchild = curr; + } + + } + } + + /* + ** If we found a child that was already terminated, we need to + ** wake up the init process if it's already waiting. + ** + ** Note: we only need to do this for one Zombie child process - + ** init will loop and collect the others after it finishes with + ** this one. + ** + ** Also note: it's possible that the exiting process' parent is + ** also init, which means we're letting one of zombie children + ** of the exiting process be cleaned up by init before the + ** existing process itself is cleaned up by init. This will work, + ** because after init cleans up the zombie, it will loop and + ** call waitpid() again, by which time this exiting process will + ** be marked as a zombie. + */ + if( zchild != NULL && init_pcb->state == STATE_WAITING ) { + + // dequeue the zombie + assert( pcb_queue_remove_this(zombie,zchild) == SUCCESS ); + + assert( pcb_queue_remove_this(waiting,init_pcb) == SUCCESS ); + + // intrinsic return value is the PID + RET(init_pcb) = zchild->pid; + + // may also want to return the exit status + int32_t *ptr = (int32_t *) ARG(init_pcb,2); + + if( ptr != NULL ) { + // ******************************************************** + // ** Potential VM issue here! This code assigns the exit + // ** status into a variable in the parent's address space. + // ** This works in the baseline because we aren't using + // ** any type of memory protection. If address space + // ** separation is implemented, this code will very likely + // ** STOP WORKING, and will need to be fixed. + // ******************************************************** + *ptr = zchild->exit_status; + } + + // all done - schedule 'init', and clean up the zombie + schedule( init_pcb ); + pcb_cleanup( zchild ); + } + + /* + ** Now, deal with the parent of this process. If the parent is + ** already waiting, just wake it up and clean up this process. + ** Otherwise, this process becomes a zombie. + ** + ** Note: if the exiting process' parent is init and we just woke + ** init up to deal with a zombie child of the exiting process, + ** init's status won't be Waiting any more, so we don't have to + ** worry about it being scheduled twice. + */ + + if( parent->state == STATE_WAITING ) { + + // verify that the parent is either waiting for this process + // or is waiting for any of its children + uint32_t target = ARG(parent,1); + + if( target == 0 || target == vicpid ) { + + // the parent is waiting for this child or is waiting + // for any of its children, so we can wake it up. + + // intrinsic return value is the PID + RET(parent) = vicpid; + + // may also want to return the exit status + int32_t *ptr = (int32_t *) ARG(parent,2); + + if( ptr != NULL ) { + // ******************************************************** + // ** Potential VM issue here! This code assigns the exit + // ** status into a variable in the parent's address space. + // ** This works in the baseline because we aren't using + // ** any type of memory protection. If address space + // ** separation is implemented, this code will very likely + // ** STOP WORKING, and will need to be fixed. + // ******************************************************** + *ptr = victim->exit_status; + } + + // all done - schedule the parent, and clean up the zombie + schedule( parent ); + pcb_cleanup( victim ); + + return; + } + } + + /* + ** The parent isn't waiting OR is waiting for a specific child + ** that isn't this exiting process, so we become a Zombie. + ** + ** This code assumes that Zombie processes are *not* in + ** a queue, but instead are just in the process table with + ** a state of 'Zombie'. This simplifies life immensely, + ** because we won't need to dequeue it when it is collected + ** by its parent. + */ + + victim->state = STATE_ZOMBIE; + assert( pcb_queue_insert(zombie,victim) == SUCCESS ); + + /* + ** Note: we don't call _dispatch() here - we leave that for + ** the calling routine, as it's possible we don't need to + ** choose a new current process. + */ +} + +/** +** Name: pcb_cleanup +** +** Reclaim a process' data structures +** +** @param pcb The PCB to reclaim +*/ +void pcb_cleanup( pcb_t *pcb ) { + +#if TRACING_PCB + cio_printf( "** pcb_cleanup(0x%08x)\n", (uint32_t) pcb ); +#endif + + // avoid deallocating a NULL pointer + if( pcb == NULL ) { + // should this be an error? + return; + } + + // we need to release all the VM data structures and frames + user_cleanup( pcb ); + + // release the PCB itself + pcb_free( pcb ); +} + +/** +** Name: pcb_find_pid +** +** Locate the PCB for the process with the specified PID +** +** @param pid The PID to be located +** +** @return Pointer to the PCB, or NULL +*/ +pcb_t *pcb_find_pid( uint_t pid ) { + + // must be a valid PID + if( pid < 1 ) { + return NULL; + } + + // scan the process table + pcb_t *p = ptable; + + for( int i = 0; i < N_PROCS; ++i, ++p ) { + if( p->pid == pid && p->state != STATE_UNUSED ) { + return p; + } + } + + // didn't find it! + return NULL; +} + +/** +** Name: pcb_find_ppid +** +** Locate the PCB for the process with the specified parent +** +** @param pid The PID to be located +** +** @return Pointer to the PCB, or NULL +*/ +pcb_t *pcb_find_ppid( uint_t pid ) { + + // must be a valid PID + if( pid < 1 ) { + return NULL; + } + + // scan the process table + pcb_t *p = ptable; + + for( int i = 0; i < N_PROCS; ++i, ++p ) { + assert1( p->parent != NULL ); + if( p->parent->pid == pid && p->parent->state != STATE_UNUSED ) { + return p; + } + } + + // didn't find it! + return NULL; +} + +/** +** Name: pcb_queue_reset +** +** Initialize a PCB queue. We assume that whatever data may be +** in the queue structure can be overwritten. +** +** @param queue[out] The queue to be initialized +** @param order[in] The desired ordering for the queue +** +** @return status of the init request +*/ +int pcb_queue_reset( pcb_queue_t queue, enum pcb_queue_order_e style ) { + + // sanity check + assert1( queue != NULL ); + + // make sure the style is valid + if( style < O_FIRST_STYLE || style > O_LAST_STYLE ) { + return E_BAD_PARAM; + } + + // reset the queue + queue->head = queue->tail = NULL; + queue->order = style; + + return SUCCESS; +} + +/** +** Name: pcb_queue_empty +** +** Determine whether a queue is empty. Essentially just a wrapper +** for the PCB_QUEUE_EMPTY() macro, for use outside this module. +** +** @param[in] queue The queue to check +** +** @return true if the queue is empty, else false +*/ +bool_t pcb_queue_empty( pcb_queue_t queue ) { + + // if there is no queue, blow up + assert1( queue != NULL ); + + return PCB_QUEUE_EMPTY(queue); +} + +/** +** Name: pcb_queue_length +** +** Return the count of elements in the specified queue. +** +** @param[in] queue The queue to check +** +** @return the count (0 if the queue is empty) +*/ +uint_t pcb_queue_length( const pcb_queue_t queue ) { + + // sanity check + assert1( queue != NULL ); + + // this is pretty simple + register pcb_t *tmp = queue->head; + register int num = 0; + + while( tmp != NULL ) { + ++num; + tmp = tmp->next; + } + + return num; +} + +/** +** Name: pcb_queue_insert +** +** Inserts a PCB into the indicated queue. +** +** @param queue[in,out] The queue to be used +** @param pcb[in] The PCB to be inserted +** +** @return status of the insertion request +*/ +int pcb_queue_insert( pcb_queue_t queue, pcb_t *pcb ) { + + // sanity checks + assert1( queue != NULL ); + assert1( pcb != NULL ); + + // if this PCB is already in a queue, we won't touch it + if( pcb->next != NULL ) { + // what to do? we let the caller decide + return E_BAD_PARAM; + } + + // is the queue empty? + if( queue->head == NULL ) { + queue->head = queue->tail = pcb; + return SUCCESS; + } + assert1( queue->tail != NULL ); + + // no, so we need to search it + pcb_t *prev = NULL; + + // find the predecessor node + switch( queue->order ) { + case O_FIFO: + prev = queue->tail; + break; + case O_PRIO: + prev = find_prev_priority(queue,pcb); + break; + case O_PID: + prev = find_prev_pid(queue,pcb); + break; + case O_WAKEUP: + prev = find_prev_wakeup(queue,pcb); + break; + default: + // do we need something more specific here? + return E_BAD_PARAM; + } + + // OK, we found the predecessor node; time to do the insertion + + if( prev == NULL ) { + + // there is no predecessor, so we're + // inserting at the front of the queue + pcb->next = queue->head; + if( queue->head == NULL ) { + // empty queue!?! - should we panic? + queue->tail = pcb; + } + queue->head = pcb; + + } else if( prev->next == NULL ) { + + // append at end + prev->next = pcb; + queue->tail = pcb; + + } else { + + // insert between prev & prev->next + pcb->next = prev->next; + prev->next = pcb; + + } + + return SUCCESS; +} + +/** +** Name: pcb_queue_remove +** +** Remove the first PCB from the indicated queue. +** +** @param queue[in,out] The queue to be used +** @param pcb[out] Pointer to where the PCB pointer will be saved +** +** @return status of the removal request +*/ +int pcb_queue_remove( pcb_queue_t queue, pcb_t **pcb ) { + + //sanity checks + assert1( queue != NULL ); + assert1( pcb != NULL ); + + // can't get anything if there's nothing to get! + if( PCB_QUEUE_EMPTY(queue) ) { + return E_EMPTY_QUEUE; + } + + // take the first entry from the queue + pcb_t *tmp = queue->head; + queue->head = tmp->next; + + // disconnect it completely + tmp->next = NULL; + + // was this the last thing in the queue? + if( queue->head == NULL ) { + // yes, so clear the tail pointer for consistency + queue->tail = NULL; + } + + // save the pointer + *pcb = tmp; + + return SUCCESS; +} + +/** +** Name: pcb_queue_remove_this +** +** Remove the specified PCB from the indicated queue. +** +** We don't return the removed pointer, because the calling +** routine must already have it (because it was supplied +** to us in the call). +** +** @param queue[in,out] The queue to be used +** @param pcb[in] Pointer to the PCB to be removed +** +** @return status of the removal request +*/ +int pcb_queue_remove_this( pcb_queue_t queue, pcb_t *pcb ) { + + //sanity checks + assert1( queue != NULL ); + assert1( pcb != NULL ); + + // can't get anything if there's nothing to get! + if( PCB_QUEUE_EMPTY(queue) ) { + return E_EMPTY_QUEUE; + } + + // iterate through the queue until we find the desired PCB + pcb_t *prev = NULL; + pcb_t *curr = queue->head; + + while( curr != NULL && curr != pcb ) { + prev = curr; + curr = curr->next; + } + + // case prev curr next interpretation + // ==== ==== ==== ==== ============================ + // 1. 0 0 -- *** CANNOT HAPPEN *** + // 2. 0 !0 0 removing only element + // 3. 0 !0 !0 removing first element + // 4. !0 0 -- *** NOT FOUND *** + // 5. !0 !0 0 removing from end + // 6. !0 !0 !0 removing from middle + + if( curr == NULL ) { + // case 1 + assert( prev != NULL ); + // case 4 + return E_NOT_FOUND; + } + + // connect predecessor to successor + if( prev != NULL ) { + // not the first element + // cases 5 and 6 + prev->next = curr->next; + } else { + // removing first element + // cases 2 and 3 + queue->head = curr->next; + } + + // if this was the last node (cases 2 and 5), + // also need to reset the tail pointer + if( curr->next == NULL ) { + // if this was the only entry (2), prev is NULL, + // so this works for that case, too + queue->tail = prev; + } + + // unlink current from queue + curr->next = NULL; + + // there's a possible consistancy problem here if somehow + // one of the queue pointers is NULL and the other one + // is not NULL + + assert1( + (queue->head == NULL && queue->tail == NULL) || + (queue->head != NULL && queue->tail != NULL) + ); + + return SUCCESS; +} + +/** +** Name: pcb_queue_peek +** +** Return the first PCB from the indicated queue, but don't +** remove it from the queue. +** +** @param queue[in] The queue to be used +** +** @return the PCB poiner, or NULL if the queue is empty +*/ +pcb_t *pcb_queue_peek( const pcb_queue_t queue ) { + + //sanity check + assert1( queue != NULL ); + + // can't get anything if there's nothing to get! + if( PCB_QUEUE_EMPTY(queue) ) { + return NULL; + } + + // just return the first entry from the queue + return queue->head; +} + +/* +** Scheduler routines +*/ + +/** +** schedule(pcb) +** +** Schedule the supplied process +** +** @param pcb Pointer to the PCB of the process to be scheduled +*/ +void schedule( pcb_t *pcb ) { + + // sanity check + assert1( pcb != NULL ); + + // check for a killed process + if( pcb->state == STATE_KILLED ) { + // TODO figure out what to do now + return; + } + + // mark it as ready + pcb->state = STATE_READY; + + // add it to the ready queue + if( pcb_queue_insert(ready,pcb) != SUCCESS ) { + PANIC( 0, "schedule insert fail" ); + } +} + +/** +** dispatch() +** +** Select the next process to receive the CPU +*/ +void dispatch( void ) { + + // verify that there is no current process + assert( current == NULL ); + + // grab whoever is at the head of the queue + int status = pcb_queue_remove( ready, ¤t ); + if( status != SUCCESS ) { + sprint( b256, "dispatch queue remove failed, code %d", status ); + PANIC( 0, b256 ); + } + + // set the process up for success + current->state = STATE_RUNNING; + current->ticks = QUANTUM_STANDARD; +} + + +/* +** Debugging/tracing routines +*/ + +/** +** ctx_dump(msg,context) +** +** Dumps the contents of this process context to the console +** +** @param msg[in] An optional message to print before the dump +** @param c[in] The context to dump out +*/ +void ctx_dump( const char *msg, register context_t *c ) { + + // first, the message (if there is one) + if( msg ) { + cio_puts( msg ); + } + + // the pointer + cio_printf( " @ %08x: ", (uint32_t) c ); + + // if it's NULL, why did you bother calling me? + if( c == NULL ) { + cio_puts( " NULL???\n" ); + return; + } + + // now, the contents + cio_printf( "ss %04x gs %04x fs %04x es %04x ds %04x cs %04x\n", + c->ss & 0xff, c->gs & 0xff, c->fs & 0xff, + c->es & 0xff, c->ds & 0xff, c->cs & 0xff ); + cio_printf( " edi %08x esi %08x ebp %08x esp %08x\n", + c->edi, c->esi, c->ebp, c->esp ); + cio_printf( " ebx %08x edx %08x ecx %08x eax %08x\n", + c->ebx, c->edx, c->ecx, c->eax ); + cio_printf( " vec %08x cod %08x eip %08x eflags %08x\n", + c->vector, c->code, c->eip, c->eflags ); +} + +/** +** ctx_dump_all(msg) +** +** dump the process context for all active processes +** +** @param msg[in] Optional message to print +*/ +void ctx_dump_all( const char *msg ) { + + if( msg != NULL ) { + cio_puts( msg ); + } + + int n = 0; + register pcb_t *pcb = ptable; + for( int i = 0; i < N_PROCS; ++i, ++pcb ) { + if( pcb->state != STATE_UNUSED ) { + ++n; + cio_printf( "%2d(%d): ", n, pcb->pid ); + ctx_dump( NULL, pcb->context ); + } + } +} + +/** +** _pcb_dump(msg,pcb) +** +** Dumps the contents of this PCB to the console +** +** @param msg[in] An optional message to print before the dump +** @param pcb[in] The PCB to dump +** @param all[in] Dump all the contents? +*/ +void pcb_dump( const char *msg, register pcb_t *pcb, bool_t all ) { + + // first, the message (if there is one) + if( msg ) { + cio_puts( msg ); + } + + // the pointer + cio_printf( " @ %08x:", (uint32_t) pcb ); + + // if it's NULL, why did you bother calling me? + if( pcb == NULL ) { + cio_puts( " NULL???\n" ); + return; + } + + cio_printf( " %d", pcb->pid ); + cio_printf( " %s", + pcb->state >= N_STATES ? "???" : state_str[pcb->state] ); + + if( !all ) { + // just printing IDs and states on one line + return; + } + + // now, the rest of the contents + cio_printf( " %s", + pcb->priority >= N_PRIOS ? "???" : prio_str[pcb->priority] ); + + cio_printf( " ticks %u xit %d wake %08x\n", + pcb->ticks, pcb->exit_status, pcb->wakeup ); + + cio_printf( " parent %08x", (uint32_t)pcb->parent ); + if( pcb->parent != NULL ) { + cio_printf( " (%u)", pcb->parent->pid ); + } + + cio_printf( " next %08x context %08x pde %08x", (uint32_t) pcb->next, + (uint32_t) pcb->context, (uint32_t) pcb->pdir ); + + cio_putchar( '\n' ); +} + +/** +** pcb_queue_dump(msg,queue,contents) +** +** Dump the contents of the specified queue to the console +** +** @param msg[in] Optional message to print +** @param queue[in] The queue to dump +** @param contents[in] Also dump (some) contents? +*/ +void pcb_queue_dump( const char *msg, pcb_queue_t queue, bool_t contents ) { + + // report on this queue + cio_printf( "%s: ", msg ); + if( queue == NULL ) { + cio_puts( "NULL???\n" ); + return; + } + + // first, the basic data + cio_printf( "head %08x tail %08x", + (uint32_t) queue->head, (uint32_t) queue->tail ); + + // next, how the queue is ordered + cio_printf( " order %s\n", + queue->order >= N_ORDERINGS ? "????" : ord_str[queue->order] ); + + // if there are members in the queue, dump the first few PIDs + if( contents && queue->head != NULL ) { + cio_puts( " PIDs: " ); + pcb_t *tmp = queue->head; + for( int i = 0; i < 5 && tmp != NULL; ++i, tmp = tmp->next ) { + cio_printf( " [%u]", tmp->pid ); + } + + if( tmp != NULL ) { + cio_puts( " ..." ); + } + + cio_putchar( '\n' ); + } +} + +/** +** ptable_dump(msg,all) +** +** dump the contents of the "active processes" table +** +** @param msg[in] Optional message to print +** @param all[in] Dump all or only part of the relevant data +*/ +void ptable_dump( const char *msg, bool_t all ) { + + if( msg ) { + cio_puts( msg ); + } + cio_putchar( ' ' ); + + int used = 0; + int empty = 0; + + register pcb_t *pcb = ptable; + for( int i = 0; i < N_PROCS; ++i ) { + if( pcb->state == STATE_UNUSED ) { + + // an empty slot + ++empty; + + } else { + + // a non-empty slot + ++used; + + // if not dumping everything, add commas if needed + if( !all && used ) { + cio_putchar( ',' ); + } + + // report the table slot # + cio_printf( " #%d:", i ); + + // and dump the contents + pcb_dump( NULL, pcb, all ); + } + } + + // only need this if we're doing one-line output + if( !all ) { + cio_putchar( '\n' ); + } + + // sanity check - make sure we saw the correct number of table slots + if( (used + empty) != N_PROCS ) { + cio_printf( "Table size %d, used %d + empty %d = %d???\n", + N_PROCS, used, empty, used + empty ); + } +} + +/** +** Name: ptable_dump_counts +** +** Prints basic information about the process table (number of +** entries, number with each process state, etc.). +*/ +void ptable_dump_counts( void ) { + uint_t nstate[N_STATES] = { 0 }; + uint_t unknown = 0; + + int n = 0; + pcb_t *ptr = ptable; + while( n < N_PROCS ) { + if( ptr->state < 0 || ptr->state >= N_STATES ) { + ++unknown; + } else { + ++nstate[ptr->state]; + } + ++n; + ++ptr; + } + + cio_printf( "Ptable: %u ***", unknown ); + for( n = 0; n < N_STATES; ++n ) { + cio_printf( " %u %s", nstate[n], + state_str[n] != NULL ? state_str[n] : "???" ); + } + cio_putchar( '\n' ); +} |