/* Copyright 1990,1991,1992 Eric R. Smith. All rights reserved. */ /* routines for handling processes */ #include "mint.h" #include "xbra.h" static void do_wakeup_things P_((void)); extern short proc_clock; /* global process variables */ PROC *proclist; /* list of all active processes */ PROC *curproc; /* current process */ PROC *rootproc; /* pid 0 -- MiNT itself */ PROC *sys_q[NUM_QUEUES]; #define TIME_SLICE 1 /* number of 20ms ticks before process is pre-empted */ /* macro for calculating number of missed time slices, based on a * process' priority */ #define SLICES(pri) (((pri) >= 0) ? 0 : -(pri)) extern FILESYS bios_filesys; /* * get a new process struct */ #define NPROC 5 /* get this many PROC structures at a time */ PROC *pfreelist = 0; PROC * new_proc() { PROC *p; #if 0 int i; if (pfreelist == 0) { p = (PROC *)kmalloc(NPROC * SIZEOF(PROC)); if (!p) { ALERT("new_proc: couldn't get a PROC structure"); return 0; } else { for (i = 0; i < NPROC; i++) { p->gl_next = pfreelist; pfreelist = p; p++; } } } p = pfreelist; pfreelist = p->gl_next; p->gl_next = 0; #else p = (PROC *)kmalloc(SIZEOF(PROC)); #endif return p; } /* * dispose of an old proc */ void dispose_proc(p) PROC *p; { #if 0 p->gl_next = pfreelist; pfreelist = p; #else kfree(p); #endif } /* * create a new process that is (practically) a duplicate of the * current one */ PROC * fork_proc() { PROC *p; int i; FILEPTR *f; if (!(p = new_proc())) { nomem: DEBUG("fork_proc: insufficient memory"); mint_errno = ENSMEM; return 0; } *p = *curproc; /* child shares most things with parent... */ /* ... except for these: */ p->ppid = curproc->pid; p->pid = newpid(); p->sigpending = 0; p->sysstack = (long)(p->stack + STKSIZE - 12); p->ctxt[CURRENT].ssp = p->sysstack; p->ctxt[SYSCALL].ssp = (long)(p->stack + ISTKSIZE); p->alarmtim = 0; p->curpri = p->pri; p->slices = SLICES(p->pri); p->starttime = timestamp; p->startdate = datestamp; ((long *)p->sysstack)[1] = FRAME_MAGIC; ((long *)p->sysstack)[2] = 0; ((long *)p->sysstack)[3] = 0; p->usrtime = p->systime = p->chldstime = p->chldutime = 0; /* copy open handles */ for (i = MIN_HANDLE; i < MAX_OPEN; i++) { if ((f = p->handle[i]) != 0) { if (f->flags & O_NOINHERIT) /* oops, we didn't really want to copy this handle */ p->handle[i] = 0; else f->links++; } } /* clear directory search info */ zero((char *)p->srchdta, NUM_SEARCH * SIZEOF(DTABUF *)); /* copy memory */ p->mem = (MEMREGION **) kmalloc(p->num_reg * SIZEOF(MEMREGION *)); if (!p->mem) { dispose_proc(p); goto nomem; } p->addr = (virtaddr *)kmalloc(p->num_reg * SIZEOF(virtaddr)); if (!p->addr) { kfree(p->mem); dispose_proc(p); goto nomem; } for (i = 0; i < curproc->num_reg; i++) { if (p->mem[i] = curproc->mem[i]) /* yes, "=" is right */ p->mem[i]->links++; p->addr[i] = curproc->addr[i]; } p->starttime = Tgettime(); p->startdate = Tgetdate(); p->q_next = 0; p->wait_q = 0; p->gl_next = proclist; proclist = p; /* hook into the process list */ return p; } /* * initialize the process table */ void init_proc() { int i; FILESYS *fs; fcookie dir; extern BASEPAGE **tosbp; /* in main.c */ rootproc = curproc = new_proc(); assert(curproc); zero((char *)curproc, (long)sizeof(PROC)); curproc->ppid = -1; /* no parent */ curproc->domain = DOM_TOS; /* TOS domain */ curproc->sysstack = (long) (curproc->stack+STKSIZE-12); curproc->magic = CTXT_MAGIC; ((long *)curproc->sysstack)[1] = FRAME_MAGIC; ((long *)curproc->sysstack)[2] = 0; ((long *)curproc->sysstack)[3] = 0; curproc->base = (BASEPAGE *) *tosbp; strcpy(curproc->name, "MiNT"); /* get some memory */ curproc->mem = (MEMREGION **)kmalloc(NUM_REGIONS*SIZEOF(MEMREGION *)); curproc->addr = (virtaddr *)kmalloc(NUM_REGIONS*SIZEOF(virtaddr)); assert(curproc->mem && curproc->addr); /* make sure it's filled with zeros */ zero((char *)curproc->addr, NUM_REGIONS * SIZEOF(virtaddr)); zero((char *)curproc->mem, NUM_REGIONS * SIZEOF(MEMREGION *)); curproc->num_reg = NUM_REGIONS; /* get root and current directories for all drives */ for (i = 0; i < NUM_DRIVES; i++) { if ((fs = drives[i]) != 0 && (*fs->root)(i, &dir) == E_OK) { curproc->root[i] = curproc->curdir[i] = dir; } else { curproc->root[i].fs = curproc->curdir[i].fs = 0; curproc->root[i].dev = curproc->curdir[i].dev = i; } } /* Set the correct drive. The current directory we * set later, after all file systems have been loaded. */ curproc->curdrv = Dgetdrv(); proclist = curproc; curproc->umask = 0; /* * some more protection against job control; unless these signals are * re-activated by a shell that knows about job control, they'll have * no effect */ curproc->sighandle[SIGTTIN] = curproc->sighandle[SIGTTOU] = curproc->sighandle[SIGTSTP] = SIG_IGN; /* set up some more per-process variables */ curproc->starttime = Tgettime(); curproc->startdate = Tgetdate(); if (has_bconmap) curproc->bconmap = curbconmap; else curproc->bconmap = 1; curproc->logbase = (void *)Logbase(); curproc->criticerr = *((long (**) P_((long)))0x404L); } /* * reset all process priorities to their base level * called once per second, so that cpu hogs can get _some_ time * slices :-). */ void reset_priorities() { PROC *p; for (p = proclist; p; p = p->gl_next) { p->curpri = p->pri; p->slices = SLICES(p->curpri); } } /* * more priority code stuff: * run_next(p, slices): schedule process "p" to run next, with "slices" * initial time slices; "p" does not actually start running until * the next context switch * fresh_slices(slices): give the current process "slices" more slices in * which to run */ void run_next(p, slices) PROC *p; int slices; /* BUG: currently ignored */ { p->slices = 0; p->curpri = MAX_NICE; p->wait_q = READY_Q; p->q_next = sys_q[READY_Q]; sys_q[READY_Q] = p; } void fresh_slices(slices) int slices; { curproc->slices = 0; curproc->curpri = MAX_NICE+1; proc_clock = slices; } /* * add a process to a wait (or ready) queue. * * processes go onto a queue in first in-first out order */ void add_q(que, proc) int que; PROC *proc; { PROC *q, **lastq; /* "proc" should not already be on a list */ assert(proc->wait_q == 0); assert(proc->q_next == 0); lastq = &sys_q[que]; q = *lastq; while(q) { lastq = &q->q_next; q = *lastq; } *lastq = proc; proc->wait_q = que; if (que != READY_Q) { proc->curpri = proc->pri; /* reward the process */ proc->slices = SLICES(proc->curpri); } } /* * remove a process from a queue */ void rm_q(que, proc) int que; PROC *proc; { PROC *q; PROC *old = 0; assert(proc->wait_q == que); q = sys_q[que]; while (q && q != proc) { old = q; q = q->q_next; } if (q == 0) FATAL("rm_q: unable to remove process from queue"); if (old) old->q_next = proc->q_next; else sys_q[que] = proc->q_next; proc->wait_q = 0; proc->q_next = 0; } /* * preempt(): called by the vbl routine and/or the trap handlers when * they detect that a process has exceeded its time slice and hasn't * yielded gracefully. For now, it just does sleep(READY_Q); later, * we might want to keep track of statistics or something. */ void preempt() { extern short bconbsiz; /* in bios.c */ if (bconbsiz) (void)bflush(); else { /* punish the pre-empted process */ if (curproc->curpri >= MIN_NICE) curproc->curpri -= 1; } sleep(READY_Q, curproc->wait_cond); } /* * sleep(que, cond): put the current process on the given queue, then switch * contexts. Before a new process runs, give it a fresh time slice. "cond" * is the condition for which the process is waiting, and is placed in * curproc->wait_cond */ static void do_wakeup_things() { /* * check for stack underflow, just in case */ auto int foo; if ( curproc->pid != 0 && ((long)&foo) < (long)curproc->stack + ISTKSIZE + 512 ) { ALERT("sleep: stack overflow"); handle_sig(SIGBUS); } /* * check processes for alarms */ checkalarms(); check_time(); /* check for CPU limit exceeded */ check_sigs(); /* check for signals */ proc_clock = TIME_SLICE; /* get a fresh time slice */ curproc->slices = SLICES(curproc->curpri); } void sleep(que, cond) int que; long cond; { PROC *p; short sr; extern short kintr; /* in bios.c */ extern short bconbsiz; #ifdef FASTTEXT extern int hardscroll; /* in fasttext.c */ #endif assert(bconbsiz == 0); /* * if there have been keyboard interrupts since our last sleep, check for * special keys like CTRL-ALT-Fx */ if (kintr) { (void)checkkeys(); kintr = 0; } if (que == READY_Q && !sys_q[READY_Q]) { /* we're just going to wake up again right away! */ do_wakeup_things(); return; } sr = spl7(); add_q(que, curproc); curproc->wait_cond = cond; if (!sys_q[READY_Q]) { /* hmm, no-one is ready to run. might be a deadlock, might not. * first, try waking up any napping processes; if that doesn't work, * run the root process, just so we have someone to charge time * to. */ wake(SELECT_Q, (long)&nap); if (!sys_q[READY_Q]) { p = rootproc; /* pid 0 */ rm_q(p->wait_q, p); add_q(READY_Q, p); } } /* * Walk through the ready list, to find what process should run next. * Lower priority processes don't get to run every time through this * loop; if "p->slices" is positive, it's the number of times that they * will have to miss a turn before getting to run again */ p = 0; while (!p) { for (p = sys_q[READY_Q]; p; p = p->q_next) { if (p->slices > 0) p->slices--; else break; } } rm_q(READY_Q, p); spl(sr); if (save_context(&(curproc->ctxt[CURRENT]))) { if (curproc->q_next) { DEBUG("sleep: why is curproc on a queue???"); } /* * restore per-process variables here */ #ifdef FASTTEXT if (!hardscroll) #endif *((void **)0x44eL) = curproc->logbase; do_wakeup_things(); return; } /* * save per-process variables here */ #ifdef FASTTEXT if (!hardscroll) #endif curproc->logbase = *((void **)0x44eL); curproc->ctxt[CURRENT].regs[0] = 1; curproc = p; proc_clock = TIME_SLICE; /* fresh time */ if ((p->ctxt[CURRENT].sr & 0x2000) == 0) { /* user mode? */ leave_kernel(); } assert(p->magic == CTXT_MAGIC); restore_context(&(p->ctxt[CURRENT])); } /* * wake(que, cond): wake up all processes on the given queue that are waiting * for the indicated condition */ void wake(que, cond) int que; long cond; { PROC *p; if (que == READY_Q) { ALERT("wake: why wake up ready processes??"); return; } top: for(p = sys_q[que]; p; p = p->q_next) { if (p->wait_cond == cond) { rm_q(que, p); add_q(READY_Q, p); goto top; } } } /* * wakeselect(p): wake process p from a select() system call * may be called by an interrupt handler or whatever */ void wakeselect(param) long param; { PROC *p = (PROC *)param; short s; s = spl7(); /* block interrupts */ if(p->wait_cond == (long)&wakeselect) { p->wait_cond = 0; } if (p->wait_q == SELECT_Q) { rm_q(SELECT_Q, p); add_q(READY_Q, p); } spl(s); } /* * check_time(): check to see if process' time limit has been exceeded */ void check_time() { if (curproc->maxcpu) { if (curproc->maxcpu <= curproc->systime + curproc->usrtime) { DEBUG("cpu limit exceeded"); raise(SIGXCPU); } } } /* * dump out information about processes */ /* * kludge alert! In order to get the right pid printed by ALERT, we use * curproc as the loop variable. */ void DUMPPROC() { PROC *p = curproc; for (curproc = proclist; curproc; curproc = curproc->gl_next) { ALERT("queue %d cond %lx PC: %lx USP: %lx SSP: %lx SYSSP: %lx FRAME: %lx", curproc->wait_q, curproc->wait_cond, curproc->ctxt[SYSCALL].pc, curproc->ctxt[SYSCALL].usp, curproc->ctxt[SYSCALL].ssp, curproc->sysstack, ((long *)curproc->sysstack)[2] ); } curproc = p; /* restore the real curproc */ }