void _zombify( pcb_t *pcb ) { pcb_t *parent; status_t stat; pid_t pid; key_t key; info_t *info; // sanity check if( pcb == NULL ) { _kpanic( "_zombify", "null pcb", 0 ); } // Locate the parent of this process parent = _pcb_find( pcb->ppid ); if( parent == NULL ) { c_printf( "** zombify(): pid %d ppid %d\n", pcb->pid, pcb->ppid ); _kpanic( "_zombify", "no process parent", 0 ); } // // Found the parent. If it's waiting for this process, // wake it up, give it this process' status, and clean up. // if( parent->state == WAITING ) { // get the address of the info structure from the // parent, and pull out the desired PID info = (info_t *) ARG(parent->context,1); pid = info->pid; // if the parent was waiting for any of its children // or was waiting for us specifically, give it our // information and terminate this process. // // if the parent was waiting for another child, // turn this process into a zombie. if( pid == 0 || pid == _current->pid ) { // pull the parent off the waiting queue key.u = parent->pid; stat = _q_remove_by_key(&_waiting,(void **)&parent,key); if( stat != E_SUCCESS ) { _kpanic( "_zombify", "wait remove status %s", stat ); } // return our PID and our termination status // to the parent info->pid = _current->pid; info->status = ARG(_current->context,1); // clean up this process stat = _stack_free( pcb->stack ); if( stat != E_SUCCESS ) { _kpanic( "_zombify", "stack free status %s", stat ); } stat = _pcb_free( pcb ); if( stat != E_SUCCESS ) { _kpanic( "_zombify", "pcb free status %s", stat ); } // schedule the parent; give it a quick dispatch _schedule( parent, PRIO_MAXIMUM ); return; } } // // Our parent either wasn't waiting, or was waiting for someone // else. Put this process on the zombie queue until our parent // wants us. // key.u = _current->pid; _current->state = ZOMBIE; stat = _q_insert( &_zombie, (void *)_current, key ); if( stat != E_SUCCESS ) { _kpanic( "_zombify", "zombie insert status %s", stat ); } }
pcb_t *_create_process( pcb_t *curr ) { pcb_t *pcb; stack_t *stack; int offset; uint32_t *ptr; // allocate the new structures pcb = _pcb_alloc(); if( pcb == NULL ) { return( NULL ); } stack = _stack_alloc(); if( stack == NULL ) { _pcb_free(pcb); return( NULL ); } /* ** The PCB argument will be NULL if this function is called ** from the system initialization, and non-NULL if called ** from the fork() implementation. ** ** In the former case, we initialize the new data structures for ** a brand-new process. ** ** In the latter case, we replicate the information from the ** existing process whose PCB was passed in. */ if( curr != NULL ) { // called from fork() // duplicate the PCB and stack contents _kmemcpy( (void *) pcb, (void *) curr, sizeof(pcb_t) ); _kmemcpy( (void *) stack, (void *) curr->stack, sizeof(stack_t) ); // update the entries which should be changed in the PCB pcb->pid = _next_pid++; pcb->ppid = curr->pid; pcb->stack = stack; /* ** We duplicated the original stack contents, which ** means that the context pointer and ESP and EBP values ** in the new stack are still pointing into the original ** stack. We need to correct all of these. ** ** We have to change EBP because that's how the compiled ** code for the user process accesses its local variables. ** If we didn't change this, as soon as the new process ** was dispatched, it would start to stomp on the local ** variables in the original process' stack. We also ** have to fix the EBP chain in the child process. ** ** None of this would be an issue if we were doing "real" ** virtual memory, as we would be talking about virtual ** addresses here rather than physical addresses, and all ** processes would share the same virtual address space ** layout. ** ** First, determine the distance (in bytes) between the ** two stacks. This is the adjustment value we must add ** to the three pointers to correct them. Note that this ** distance may be positive or negative, depending on the ** relative placement of the two stacks in memory. */ offset = (void *) (pcb->stack) - (void *) (curr->stack); // modify the context pointer for the new process pcb->context = (context_t *)( (void *) (pcb->context) + offset ); // now, change ESP and EBP in the new process (easy to // do because they're just uint32_t values, not really // pointers) pcb->context->esp += offset; pcb->context->ebp += offset; /* ** Next, we must fix the EBP chain in the new stack. This ** is necessary in the situation where the fork() occurred ** in a nested function call sequence; we fixed EBP, but ** the "saved" EBP in the stack frame is pointing to the ** calling function's frame in the original stack, not the ** new stack. ** ** We are guaranteed that the chain of frames ends at the ** frame for the original process' main() routine, because ** exec() will initialize EBP for the process to 0, and the ** entry prologue code in main() routine will push EBP, ** ensuring a NULL pointer in the chain. */ // start at the current frame ptr = (uint32_t *) pcb->context->ebp; // follow the chain of frame pointers to its end while( *ptr != 0 ) { // update the link from this frame to the previous *ptr += offset; // follow the updated link ptr = (uint32_t *) *ptr; } } else { // called from init pcb->pid = pcb->ppid = PID_INIT; pcb->stack = stack; } // all done - return the new PCB return( pcb ); }