// Acquire the lock. // Loops (spins) until the lock is acquired. // (Because contention is handled by spinning, must not // go to sleep holding any locks.) void acquire(struct spinlock *lock) { if(holding(lock)) panic("acquire"); if(cpus[cpu()].nlock == 0) cli(); cpus[cpu()].nlock++; while(cmpxchg(0, 1, &lock->locked) == 1) ; // Serialize instructions: now that lock is acquired, make sure // we wait for all pending writes from other processors. cpuid(0, 0, 0, 0, 0); // memory barrier (see Ch 7, IA-32 manual vol 3) // Record info about lock acquisition for debugging. // The +10 is only so that we can tell the difference // between forgetting to initialize lock->cpu // and holding a lock on cpu 0. lock->cpu = cpu() + 10; getcallerpcs(&lock, lock->pcs); }
// Exit the current process. Does not return. // An exited process remains in the zombie state // until its parent calls wait() to find out it exited. void exit(void) { struct proc *p; int fd; int rmBrothers=0; if(!is_main_thread(proc)) { // Kill main thread proc->parent->killed=1; if(proc->parent->state==SLEEPING) proc->parent->state=RUNNABLE; //clear_thread(proc); rmBrothers=1; //return; } if(proc == initproc) panic("init exiting"); cprintf("#"); //release(&ptable.lock); if(!holding(&ptable.lock)) { cprintf("[ex%d]", proc->pid); acquire(&ptable.lock); } cprintf("%"); // Pass abandoned proc children to init & // kill all threads beneath (children who are not procs) for(p = ptable.proc; p < &ptable.proc[NPROC]; p++) { if(p->parent == proc) { if(is_main_thread(p) && rmBrothers==0) { p->parent = initproc; if(p->state == ZOMBIE) wakeup1(initproc); } else { clear_thread(p); } } } if(rmBrothers) goto end; // Close all open files. for(fd = 0; fd < NOFILE; fd++) { if(proc->ofile[fd]) { fileclose(proc->ofile[fd]); proc->ofile[fd] = 0; } } iput(proc->cwd); proc->cwd = 0; end: // Parent might be sleeping in wait(). wakeup1(proc->parent); // Jump into the scheduler, never to return. proc->state = ZOMBIE; sched(); panic("zombie exit"); }
//PAGEBREAK: 42 // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: // - choose a process to run // - swtch to start running that process // - eventually that process transfers control // via swtch back to the scheduler. void scheduler(void) { struct proc *p; for(;;){ // Enable interrupts on this processor. sti(); // Loop over process table looking for process to run. acquire(&ptable.lock); #ifndef __ORIGINAL_SCHED__ p = pdequeue(); if(p){ #else for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){ if(p->state != RUNNABLE) continue; #endif // For debug, checking the scheduled process priority cprintf("cpu %d get process %s, process prio = %d\n",cpunum(),p->name,p->priority); // Switch to chosen process. It is the process's job // to release ptable.lock and then reacquire it // before jumping back to us. proc = p; switchuvm(p); p->state = RUNNING; swtch(&cpu->scheduler, proc->context); switchkvm(); // Process is done running for now. // It should have changed its p->state before coming back. proc = 0; } release(&ptable.lock); } } // Enter scheduler. Must hold only ptable.lock // and have changed proc->state. void sched(void) { int intena; if(!holding(&ptable.lock)) panic("sched ptable.lock"); if(cpu->ncli != 1) panic("sched locks"); if(proc->state == RUNNING) panic("sched running"); if(readeflags()&FL_IF) panic("sched interruptible"); intena = cpu->intena; swtch(&proc->context, cpu->scheduler); cpu->intena = intena; }