File:  [Atari MiNT] / MiNT / src / proc.c
Revision 1.1.1.6 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:58:46 2018 UTC (8 years, 1 month ago) by root
Branches: mint, MAIN
CVS tags: mint112, HEAD
MiNT 1.12

/*

Copyright 1990,1991,1992 Eric R. Smith.

Copyright 1992,1993,1994 Atari Corporation.

All rights reserved.

*/



/* routines for handling processes */



#include "mint.h"

#include "xbra.h"



static void do_wakeup_things P_((int sr, int newslice));



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];



short time_slice = 2;		/* default; actual value comes from mint.cnf */



#if 0

#define TIME_SLICE	2	/* number of 20ms ticks before process is

				   pre-empted */

#else

#define TIME_SLICE time_slice

#endif



/* 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

 */



PROC *

new_proc()

{

	PROC *p;

	void *pt;



	pt = kmalloc(page_table_size + 16);

	if (!pt) return 0;



	p = (PROC *)kmalloc(SIZEOF(PROC));

	if (!p) {

		kfree(pt);

		return 0;

	}

/* page tables must be on 16 byte boundaries, so we

 * round off by 16 for that; however, we will want to

 * kfree that memory at some point, so we squirrel

 * away the original address for later use

 */

	p->page_table = ROUND16(pt);

	p->pt_mem = pt;

	return p;

}



/*

 * dispose of an old proc

 */



void

dispose_proc(p)

	PROC *p;

{

TRACELOW(("dispose_proc"));

	kfree(p->pt_mem);

	kfree(p);

}



/*

 * create a new process that is (practically) a duplicate of the

 * current one

 */



PROC *

fork_proc()

{

	PROC *p;

	int i;

	FILEPTR *f;

	long_desc *pthold;

	void *ptmemhold;



	if ((p = new_proc()) == 0) {

nomem:

		DEBUG(("fork_proc: insufficient memory"));

		mint_errno = ENSMEM; return 0;

	}



/* child shares most things with parent, but hold on to page table ptr */

	pthold = p->page_table;

	ptmemhold = p->pt_mem;

	*p = *curproc;

	p->page_table = pthold;

	p->pt_mem = ptmemhold;



/* these things are not inherited */

	p->ppid = curproc->pid;

	p->pid = newpid();

	p->sigpending = 0;

	p->nsigs = 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;

	p->itimer[0].interval = 0;

	p->itimer[0].reqtime = 0;

	p->itimer[0].timeout = 0;

	p->itimer[1].interval = 0;

	p->itimer[1].reqtime = 0;

	p->itimer[1].timeout = 0;

	p->itimer[2].interval = 0;

	p->itimer[2].reqtime = 0;

	p->itimer[2].timeout = 0;



	((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;



/* allocate space for memory regions: do it here so that we can fail

 * before we duplicate anything else. The memory regions are

 * actually copied later

 */

	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;

	}



/* copy open handles */

	for (i = MIN_HANDLE; i < MAX_OPEN; i++) {

		if ((f = p->handle[i]) != 0) {

			if (f == (FILEPTR *)1 || f->flags & O_NOINHERIT)

		/* oops, we didn't really want to copy this handle */

				p->handle[i] = 0;

			else

				f->links++;

		}

	}



/* copy root and current directories */

	for (i = 0; i < NUM_DRIVES; i++) {

		dup_cookie(&p->root[i], &curproc->root[i]);

		dup_cookie(&p->curdir[i], &curproc->curdir[i]);

	}



/* jr: copy ploadinfo */

	strncpy(p->cmdlin, curproc->cmdlin, 128);

	strcpy(p->fname, curproc->fname);



/* clear directory search info */

	zero((char *)p->srchdta, NUM_SEARCH * SIZEOF(DTABUF *));

	zero((char *)p->srchdir, SIZEOF(p->srchdir));

	p->searches = 0;



/* copy memory */

	for (i = 0; i < curproc->num_reg; i++) {

		p->mem[i] = curproc->mem[i];

		if (p->mem[i] != 0)

			p->mem[i]->links++;

		p->addr[i] = curproc->addr[i];

	}



/* now that memory ownership is copied, fill in page table */

	init_page_table(p);



/* child isn't traced */

	p->ptracer = 0;

	p->ptraceflags = 0;



	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;

	long_desc *pthold;

	void *ptmemhold;



	rootproc = curproc = new_proc();

	assert(curproc);



	pthold = curproc->page_table;

	ptmemhold = curproc->pt_mem;

	zero((char *)curproc, (long)sizeof(PROC));

	curproc->page_table = pthold;

	curproc->pt_mem = ptmemhold;



	curproc->ppid = -1;		/* no parent */

	curproc->domain = DOM_TOS;	/* TOS domain */

	curproc->sysstack = (long) (curproc->stack+STKSIZE-12);

	curproc->magic = CTXT_MAGIC;

	curproc->memflags = F_PROT_S;	/* default prot mode: super-only */

	((long *)curproc->sysstack)[1] = FRAME_MAGIC;

	((long *)curproc->sysstack)[2] = 0;

	((long *)curproc->sysstack)[3] = 0;



/* NOTE: in main.c this could be changed, later */

	curproc->base = _base;



	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) {

				dup_cookie(&curproc->curdir[i], &dir);

				curproc->root[i] = dir;

		} else {

			curproc->root[i].fs = curproc->curdir[i].fs = 0;

			curproc->root[i].dev = curproc->curdir[i].dev = i;

		}

	}



	init_page_table(curproc);



/* 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)

/* init_xbios not happened yet */

		curproc->bconmap = (int) Bconmap(-1);

	else

		curproc->bconmap = 1;



	curproc->logbase = (void *)Logbase();

	curproc->criticerr = *((long ARGS_ON_STACK (**) 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) {

		if (p->slices >= 0) {

			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;

{

	short sr = spl7();

	p->slices = -slices;

	p->curpri = MAX_NICE;

	p->wait_q = READY_Q;

	p->q_next = sys_q[READY_Q];

	sys_q[READY_Q] = p;

	spl(sr);

}



void

fresh_slices(slices)

	int slices;

{

	reset_priorities();

	curproc->slices = 0;

	curproc->curpri = MAX_NICE+1;

	proc_clock = TIME_SLICE+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->slices >= 0) {

		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 ARGS_ON_STACK

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

 */



INLINE static void

do_wakeup_things(sr, newslice)

int sr;

int newslice;

{

/*

 * check for stack underflow, just in case

 */

	auto int foo;

	PROC *p;



	p = curproc;



	if ((sr & 0x700) < 0x500) {

/* skip all this if int level is too high */

		if ( p->pid != 0 &&

		     ((long)&foo) < (long)p->stack + ISTKSIZE + 512 ) {

			ALERT("stack underflow");

			handle_sig(SIGBUS);

		}



/* see if process' time limit has been exceeded */



		if (p->maxcpu) {

			if (p->maxcpu <= p->systime + p->usrtime) {

				DEBUG(("cpu limit exceeded"));

				raise(SIGXCPU);

			}

		}



/*

 * check for alarms and similar time out stuff (see timeout.c)

 */



		checkalarms();

		if (p->sigpending)

			check_sigs();		/* check for signals */

	}



	if (newslice) {

		if (p->slices >= 0) {

			proc_clock = TIME_SLICE;	/* get a fresh time slice */

		} else {

			proc_clock = TIME_SLICE-p->slices; /* slices set by run_next */

			p->curpri = p->pri;

		}

		p->slices = SLICES(p->curpri);

	}

	p->slices = SLICES(p->curpri);

}



static long sleepcond, iwakecond;



/*

 * sleep: returns 1 if no signals have happened since our last sleep, 0

 * if some have

 */



int ARGS_ON_STACK 

sleep(_que, cond)

	int _que;

	long cond;

{

	PROC *p;

	short sr, que = _que & 0xff;

	ulong onsigs = curproc->nsigs;

	extern short kintr;	/* in bios.c */

	int newslice = 1;

#ifndef MULTITOS

#ifdef FASTTEXT

	extern int hardscroll;	/* in fasttext.c */

#endif

#endif



/* save condition, checkbttys may just wake() it right away...

 * note this assumes the condition will never be waked from interrupts

 * or other than thru wake() before we really went to sleep, otherwise

 * use the 0x100 bit like select

 */

	sleepcond = cond;



/*

 * if there have been keyboard interrupts since our last sleep, check for

 * special keys like CTRL-ALT-Fx

 */



	sr = spl7();

	if ((sr & 0x700) < 0x500) {

/* can't call checkkeys if sleep was called with interrupts off  -nox */

		spl(sr);

		(void)checkbttys();

		if (kintr) {

			(void)checkkeys();

			kintr = 0;

		}

		sr = spl7();

		if ((curproc->sigpending & ~(curproc->sigmask)) &&

		    curproc->pid && que != ZOMBIE_Q && que != TSR_Q) {

			spl(sr);

			check_sigs();

			sr = spl7();

			sleepcond = 0;	/* possibly handled a signal, return */

		}

	}



/*

 * kay: If _que & 0x100 != 0 then take curproc->wait_cond != cond as an

 * indicatation that the wakeup has already happend before we actually

 * go to sleep and return immediatly.

 */



	if ((que == READY_Q && !sys_q[READY_Q]) ||

	    ((sleepcond != cond ||

	      (iwakecond == cond && cond) ||

	      (_que & 0x100 && curproc->wait_cond != cond)) &&

	     (!sys_q[READY_Q] || (newslice = 0, proc_clock)))) {

/* we're just going to wake up again right away! */

		iwakecond = 0;

		spl(sr);

		do_wakeup_things(sr, newslice);

		return (onsigs != curproc->nsigs);

	}



/*

 * unless our time slice has expired (proc_clock == 0) and other

 * processes are ready...

 */

	iwakecond = 0;

	if (!newslice)

		que = READY_Q;

	else

		curproc->wait_cond = cond;

	add_q(que, curproc);



/* alright curproc is on que now...  maybe there's an interrupt pending

 * that will wakeselect or signal someone

 */

	spl(sr);

	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);

		sr = spl7();

		if (!sys_q[READY_Q]) {

			p = rootproc;		/* pid 0 */

			rm_q(p->wait_q, p);

			add_q(READY_Q, p);

		}

		spl(sr);

	}



/*

 * 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

 */



/*

 * Loop structure:

 *	while (we haven't picked anybody) {

 *		for (each process) {

 *			if (sleeping off a penalty) {

 *				decrement penalty counter

 *			}

 *			else {

 *				pick this one and break out of both loops

 *			}

 *		}

 *	}

 */

	p = 0;



	while (!p) {

		for (p = sys_q[READY_Q]; p; p = p->q_next) {

			if (p->slices > 0)

				p->slices--;

			else

				break;

		}

	}



	/* p is our victim */



	rm_q(READY_Q, p);



	spl(sr);



	if (save_context(&(curproc->ctxt[CURRENT]))) {

/*

 * restore per-process variables here

 */

#ifndef MULTITOS

#ifdef FASTTEXT

		if (!hardscroll)

			*((void **)0x44eL) = curproc->logbase;

#endif

#endif

		do_wakeup_things(sr, 1);

		return (onsigs != curproc->nsigs);

	}

/*

 * save per-process variables here

 */

#ifndef MULTITOS

#ifdef FASTTEXT

	if (!hardscroll)

		curproc->logbase = *((void **)0x44eL);

#endif

#endif



	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);

	change_context(&(p->ctxt[CURRENT]));

	/* not reached */

	return 0;

}



/*

 * wake(que, cond): wake up all processes on the given queue that are waiting

 * for the indicated condition

 */



INLINE static void

do_wake(que, cond)

	int que;

	long cond;

{

	PROC *p;

top:

	for(p = sys_q[que]; p;) {

		short s = spl7();

		PROC *q;



/* check p is still on the right queue, maybe an interrupt just woke it... */

		if (p->wait_q != que) {

			spl(s);

			goto top;

		}

		q = p;

		p = p->q_next;

		if (q->wait_cond == cond) {

			rm_q(que, q);

			add_q(READY_Q, q);

		}

		spl(s);

	}

}



void ARGS_ON_STACK 

wake(que, cond)

	int que;

	long cond;

{

	if (que == READY_Q) {

		ALERT("wake: why wake up ready processes??");

		return;

	}

	if (sleepcond == cond)

		sleepcond = 0;

	do_wake(que, cond);

}



/*

 * iwake(que, cond, pid): special version of wake() for IO interrupt

 * handlers and such.  the normal wake() would lose when its

 * interrupt goes off just before a process is calling sleep() on the

 * same condition (similar problem like with wakeselect...)

 *

 * use like this:

 *	static ipid = -1;

 *	static volatile sleepers = 0;	(optional, to save useless calls)

 *	...

 *	device_read(...)

 *	{

 *		ipid = curproc->pid;	(p_getpid() for device drivers...)

 *		while (++sleepers, (not ready for IO...)) {

 *			sleep(IO_Q, cond);

 *			if (--sleepers < 0)

 *				sleepers = 0;

 *		}

 *		if (--sleepers < 0)

 *			sleepers = 0;

 *		ipid = -1;

 *		...

 *	}

 *

 * and in the interrupt handler:

 *	if (sleepers > 0) {

 *		sleepers = 0;

 *		iwake(IO_Q, cond, ipid);

 *	}

 *

 * caller is responsible for not trying to wake READY_Q or other nonsense :)

 * and making sure the passed pid is always -1 when curproc is calling

 * sleep() for another than the waked que/condition.

 */



void ARGS_ON_STACK 

iwake(que, cond, pid)

	int que;

	long cond;

	short pid;

{

	if (pid >= 0) {

		short s = spl7();

		if (iwakecond == cond) {

			spl(s);

			return;

		}

		if (curproc->pid == pid && !curproc->wait_q)

			iwakecond = cond;

		spl(s);

	}

	do_wake(que, cond);

}



/*

 * wakeselect(p): wake process p from a select() system call

 * may be called by an interrupt handler or whatever

 */



void ARGS_ON_STACK 

wakeselect(param)

	long param;

{

	PROC *p = (PROC *)param;

	short s;

	extern short select_coll;	/* in dosfile.c */



	s = spl7();	/* block interrupts */

	if(p->wait_cond == (long)wakeselect ||

	   p->wait_cond == (long)&select_coll) {

		p->wait_cond = 0;

	}

	if (p->wait_q == SELECT_Q) {

		rm_q(SELECT_Q, p);

		add_q(READY_Q, p);

	}

	spl(s);

}



/*

 * dump out information about processes

 */



/*

 * kludge alert! In order to get the right pid printed by FORCE, we use

 * curproc as the loop variable.

 *

 * I have changed this function so it is more useful to a user, less to

 * somebody debugging MiNT.  I haven't had any stack problems in MiNT

 * at all, so I consider all that stack info wasted space.  -- AKP

 */



#ifdef DEBUG_INFO

static const char *qstring[] = {

	"run", "ready", "wait", "iowait", "zombie", "tsr", "stop", "select"

};



/* UNSAFE macro for qname, evaluates x 1, 2, or 3 times */

#define qname(x) ((x >= 0 && x < NUM_QUEUES) ? qstring[x] : "unkn")

#endif



#include "loadave.h"



unsigned long uptime = 0;

unsigned long avenrun[3] = {0,0,0};

short uptimetick = 200;

static int number_running;

static int one_min_ptr = 0, five_min_ptr = 0, fifteen_min_ptr = 0;

static unsigned long sum1 = 0, sum5 = 0, sum15 = 0;

static unsigned char one_min[SAMPS_PER_MIN];

static unsigned char five_min[SAMPS_PER_5MIN];

static unsigned char fifteen_min[SAMPS_PER_15MIN];



void

DUMPPROC()

{

#ifdef DEBUG_INFO

	PROC *p = curproc;



	FORCE("Uptime: %ld seconds Loads: %ld %ld %ld Processes running: %d",

		uptime,

		(avenrun[0]*100)/2048 , (avenrun[1]*100)/2048, (avenrun[2]*100/2048),

 		number_running);



	for (curproc = proclist; curproc; curproc = curproc->gl_next) {

	    FORCE("state %s PC: %lx BP: %lx",

		qname(curproc->wait_q),

		curproc->ctxt[SYSCALL].pc,

		curproc->base);

	}

	curproc = p;		/* restore the real curproc */

#endif

}



unsigned long

gen_average(sum, load_ptr, max_size)

	unsigned long *sum;

	unsigned char *load_ptr;

	int max_size;

{

	int old_load, new_load;



	old_load = (int)*load_ptr;

	new_load = number_running;

	*load_ptr = (char)new_load;



	*sum += ((long) (new_load - old_load) * LOAD_SCALE);



	return (*sum / max_size);

}



void

calc_load_average()

{

	PROC *p;



	uptime++;

	uptimetick += 200;



	if (uptime % 5) return;



	number_running = 0;

	

	for (p = proclist; p; p = p->gl_next)

		if (p != rootproc)

			if ((p->wait_q == 0) || (p->wait_q == 1))

				number_running++;



	avenrun[0] = gen_average(&sum1, &one_min[one_min_ptr++],

				 SAMPS_PER_MIN);



	if (one_min_ptr == SAMPS_PER_MIN)

		one_min_ptr = 0;



	avenrun[1] = gen_average(&sum5, &five_min[five_min_ptr++],

				 SAMPS_PER_5MIN);



	if (five_min_ptr == SAMPS_PER_5MIN)

		five_min_ptr = 0;



	avenrun[2] = gen_average(&sum15, &fifteen_min[fifteen_min_ptr++],

				 SAMPS_PER_15MIN);



	if (fifteen_min_ptr == SAMPS_PER_15MIN)

		fifteen_min_ptr = 0;



}




unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.