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 ); } }
void _init( void ) { pcb_t *pcb; /* ** BOILERPLATE CODE - taken from basic framework ** ** Initialize interrupt stuff. */ __init_interrupts(); // IDT and PIC initialization // Ignore the 0x2A interrupt which happens when removing or inserting a // flash drive. __install_isr( 0x2A, _ignore_isr ); /* ** Console I/O system. */ c_io_init(); c_clearscreen(); #ifdef ISR_DEBUGGING_CODE c_setscroll( 0, 7, 99, 99 ); c_puts_at( 0, 6, "================================================================================" ); #endif /* ** 20123-SPECIFIC CODE STARTS HERE */ /* ** Initialize various OS modules ** ** Note: the clock, SIO, and syscall modules also install ** their ISRs. */ c_puts( "Module init: " ); _q_init(); // must be first _pcb_init(); _stack_init(); _sio_init(); _sys_init(); _sched_init(); _clock_init(); _pci_init(); _disk_init(); _net_init(); c_puts( "\n" ); c_puts("Launching the shell. Please be patient\n"); __delay(1000); c_clearscreen(); /* ** Create the initial system ESP ** ** This will be the address of the next-to-last ** longword in the system stack. */ _system_esp = ((uint32_t *) ( (&_system_stack) + 1)) - 2; /* ** Create the initial process ** ** Code mostly stolen from _sys_fork(); if that routine ** changes, SO MUST THIS!!! */ // allocate a PCB and stack pcb = _create_process( NULL ); if( pcb == NULL ) { _kpanic( "_init", "init() creation failed", FAILURE ); } // initialize the stack with the standard context pcb->context = _create_stack( pcb->stack ); if( pcb->context == NULL ) { _kpanic( "_init", "init() stack setup failed", FAILURE ); } // define the entry point for init() pcb->context->eip = (uint32_t) init; // set up various PCB fields pcb->pid = pcb->ppid = PID_INIT; // next PID is initially 1 pcb->prio = PRIO_HIGH; pcb->children = 1000; // remember this PCB for use in reparenting orphan processes _init_pcb = pcb; // make it the first process _schedule( pcb ); _dispatch(); /* ** Turn on the SIO receiver (the transmitter will be turned ** on/off as characters are being sent) */ _sio_enable( SIO_RX ); /* ** END OF 20123-SPECIFIC CODE ** ** Finally, report that we're all done. */ c_puts( "System initialization complete.\n" ); }
void _zombify( pcb_t *pcb ) { estatus_t *esptr; pid_t *pidptr; pid_t ppid; pcb_t *parent, *p2; int i; key_t key; status_t stat; // mark the process as no longer runnable pcb->state = ZOMBIE; // find the parent ppid = pcb->ppid; for( i = 0; i < N_PCBS; ++i ) { if( _pcbs[i].pid == ppid && _pcbs[i].state != FREE ) { parent = &_pcbs[i]; break; } } /* ** If we didn't find a parent, or if the parent was ** already unrunnable (zombied, killed), reparent this ** process to the init process */ if( i >= N_PCBS || _pcbs[i].state >= FIRST_DEAD_STATE ) { ppid = pcb->ppid = PID_INIT; parent = _init_pcb; } /* ** At this point, parent points to the parent's PCB, and ppid ** contains the parent's PID. ** ** If the parent is on the wait() queue, we'll awaken it and give ** it this child's information. ** ** Otherwise, we need to put this child on the zombie queue. */ if( parent->state == WAITING ) { // look for the parent on the wait queue key.u = ppid; stat = _q_remove_specific( &_waiting, (void **) &p2, key ); if( stat != SUCCESS ) { _kpanic( "_zombify", "parent wait remove status %s", stat ); } // verify that we found the same process if( p2 != parent ) { _pcb_dump( "*** p2: ", p2 ); _pcb_dump( "*** parent: ", parent ); _kpanic( "_zombify", "parent wait deque wrong", FAILURE ); } // OK, we have the right one. // Start by decrementing its "remaining children" // counter if it isn't the init process if( ppid != PID_INIT ) { parent->children -= 1; } // return the child's information to it. RET(parent) = U_SUCCESS; pidptr = (pid_t *) ARG(parent)[1]; *pidptr = pcb->pid; esptr = (estatus_t *) ARG(parent)[2]; *esptr = ARG(pcb)[1]; // schedule the parent (who returns from wait()) _schedule( parent ); // clean up the child process _pcb_cleanup( pcb ); } else { // place this child on the zombie queue, ordered by PID key.u = pcb->pid; pcb->status = ARG(pcb)[1]; stat = _q_insert( &_zombie, (void *)pcb, key ); if( stat != SUCCESS ) { _kpanic( "_zombify", "zombie insert status %s", stat ); } } }
void _init( void ) { Pcb *pcb; // // BOILERPLATE CODE - taken from basic framework // // Initialize interrupt stuff. // __init_interrupts(); // IDT and PIC initialization // // I/O system. // c_io_init(); c_clearscreen(); c_setscroll( 0, 7, 99, 99 ); c_puts_at( 0, 6, "================================================================================" ); c_puts( "Init: " ); // // 20073-SPECIFIC CODE STARTS HERE // // // Initialize various OS modules // _init_queues(); // must be first _init_memory(); _init_processes(); _init_stacks(); _init_sio(); _init_clock(); _init_syscalls(); c_puts( "\n" ); // // Create the initial process // // Code mostly stolen from _sys_fork() and _sys_exec(); // if either of those routines change, SO MUST THIS!!! // // // First, get a PCB and a stack // pcb = _get_pcb(); if( pcb == 0 ) { _kpanic( "_init - can't allocate first pcb" ); } pcb->stack = _get_stack(); if( pcb->stack == 0 ) { _kpanic( "_init - can't allocate first stack" ); } // // Next, set up various PCB fields // pcb->pid = g_next_pid++; pcb->prio = PRI_NORMAL; // // Set up the initial process context. // // See _sys_exec() for an explanation of how this works. // pcb->context = _setup_stack( pcb->stack, (unsigned int) first_main ); // Initialize memory segment. Equals that of the kernel's in the GDT. pcb->seg.mem.offset = 0x0; pcb->seg.mem.length = 0xFFFFFFFF; // Initialize LDT entries for this PCB // This is a "kernel" process, so we will just copy over the // descriptors from the GDT and stick them into this process' LDT. __copy_gdt_entry( &(pcb->seg.ldt.cseg), (GDT_INDEX(GDT_CODE)) ); __copy_gdt_entry( &(pcb->seg.ldt.dseg), (GDT_INDEX(GDT_DATA)) ); // Allocate a slot in the GDT for the LDT descriptor, // and initialize this PCB's LDT register variable. pcb->seg.ldtr = SEL_SETINDEX(_gdt_alloc()) | SEL_GDT | SEL_RPL(0); // Initialize the LDT descriptor located in the GDT __gdt_set_entry( SEL_GETINDEX(pcb->seg.ldtr), (u32_t)&(pcb->seg.ldt), sizeof(ldt_t), ACC_PRES | ACC_DPL(0) | ACC_SYS | SYS_LDT ); // // Give it to the scheduler. // _schedule( pcb ); // // Do it all again for the idle process. // pcb = _get_pcb(); if( pcb == 0 ) { _kpanic( "_init - can't allocate idle pcb" ); } pcb->stack = _get_stack(); if( pcb->stack == 0 ) { _kpanic( "_init - can't allocate idle stack" ); } pcb->pid = g_next_pid++; pcb->prio = PRI_MINIMUM; pcb->context = _setup_stack( pcb->stack, (unsigned int) idle_main ); pcb->seg.mem.offset = 0x0; pcb->seg.mem.length = 0xFFFFFFFF; __copy_gdt_entry( &(pcb->seg.ldt.cseg), (GDT_INDEX(GDT_CODE)) ); __copy_gdt_entry( &(pcb->seg.ldt.dseg), (GDT_INDEX(GDT_DATA)) ); pcb->seg.ldtr = SEL_SETINDEX(_gdt_alloc()) | SEL_GDT | SEL_RPL(0); __gdt_set_entry( SEL_GETINDEX(pcb->seg.ldtr), (u32_t)&(pcb->seg.ldt), sizeof(ldt_t), ACC_PRES | ACC_DPL(0) | ACC_SYS | SYS_LDT ); _schedule( pcb ); // // Dispatch the initial current process // _dispatch(); // // END OF 20073-SPECIFIC CODE // // Finally, report that we're all done. // c_puts( "System initialization complete.\n" ); }
static void _clock_isr( int vector, int code ) { (void)(vector); (void)(code); pcb_t *pcb; // spin the pinwheel ++_pinwheel; if( _pinwheel == (CLOCK_FREQUENCY / 10) ) { _pinwheel = 0; ++_pindex; c_putchar_at( 79, 0, "|/-\\"[ _pindex & 3 ] ); } // increment the system time ++_system_time; /* ** wake up any sleeper whose time has come ** ** we give awakened processes preference over the ** current process (when it is scheduled again) */ while( !_queue_empty(_sleeping) && (uint32_t) _queue_kpeek(_sleeping) <= _system_time ) { // time to wake up! remove it from the queue pcb = (pcb_t *) _queue_remove( _sleeping ); if( pcb == NULL ) { #ifdef DEBUG _kpanic( "_clock_isr", "NULL from sleep queue remove" ); #else c_puts( "*** _clock_isr: NULL from sleep queue\n" ); break; #endif } // and schedule it for dispatch _schedule( pcb ); } // check the current process to see if it needs to be scheduled // sanity check! _current->quantum -= 1; if( _current->quantum < 1 ) { _schedule( _current ); _dispatch(); } #ifdef DUMP_QUEUES // Approximately every 10 seconds, dump the queues, and // print the contents of the SIO buffers. if( (_system_time % SECONDS_TO_TICKS(10)) == 0 ) { c_printf( "Queue contents @%08x\n", _system_time ); _queue_dump( "ready[0]", _ready[0] ); _queue_dump( "ready[1]", _ready[1] ); _queue_dump( "ready[2]", _ready[2] ); _queue_dump( "ready[3]", _ready[3] ); _queue_dump( "sleep", _sleeping ); _sio_dump(); } #endif // tell the PIC we're done __outb( PIC_MASTER_CMD_PORT, PIC_EOI ); }
static void _isr_sio( int vector, int code ) { Pcb *pcb; int eir; int ch; // // Must process all pending events; loop until the EIR // says there's nothing else to do. // for( ; ; ) { // get the "pending event" indicator eir = __inb( UA4_EIR ) & UA4_EIR_INT_PRI_MASK; // process this event switch( eir ) { case UA4_EIR_TX_INT_PENDING: // if there is another character, send it if( g_sending && g_outcount > 0 ) { __outb( UA4_TXD, *g_outnext ); ++g_outnext; --g_outcount; } else { // no more data - reset the output vars g_outcount = 0; g_outlast = g_outnext = g_outbuffer; g_sending = 0; } break; case UA4_EIR_RX_INT_PENDING: // get the character ch = __inb( UA4_RXD ); if( ch == '\r' ) { // map CR to LF ch = '\n'; } // // If there is a waiting process, this must be // the first input character; give it to that // process and awaken the process. // if( g_sio_blocked.front != 0 ) { if( _deque(&g_sio_blocked,(void **)&pcb) != ERR_NONE ) { _kpanic( "sio isr - serial wakeup failed" ); } pcb->context->eax = ch & 0xff; _schedule( pcb ); } else { // // Nobody waiting - add to the input buffer // if there is room, otherwise just ignore it. // if( g_incount < BUF_SIZE ) { *g_inlast++ = ch; ++g_incount; } } break; case UA4_EIR_NO_INT: // nothing to do - tell the PIC we're done __outb( PIC_MASTER_CMD_PORT, PIC_EOI ); return; default: _kpanic( "sio isr - unknown device status" ); } } }