Tema 2.
Administracion de procesos

2.1 Procesos
Un programa consiste en código, datos y su pila.
Un proceso es un programa en ejecución y administrado por el sistema operativo. Cada 
programa tiene sus propios datos y pila, y el proceso tiene la información sobre el estado
o contexto del programa que se ejecuta.

Desde la perspectiva del sistema operativo Xinu, un proceso ejecuta un segmento de codigo,
teniendo una pila y una prioridad determinada.

La llamada al sistema operativo, que permite crear un proceso, se denomina create.
Dicha llamada recibe los siguientes parametros
	Direccion o apuntador del codigo a ejecutar
	Tamaño en words de la pila
	Prioridad del proceso, mayor que 0
	Nombre del proceso
	argumentos a la funcion
	numero de argumentos
El resultado de esta funcion es el PID del proceso creado

Esta funcion crea un proceso, pero aun no lo pone listo para ejecucion
La llamada resume permite aplicar esto, recibe como parametro el PID del proceso que
debe poner listo para ejecucion

El siguiente codigo muestra el uso de ambas llamadas


#include 
#include 

codigo()
{
	printf("hola Xinu \n");
}
xmain()
{
	int pid=0;
	pid=create(codigo,INITSTK,INITPRIO,"proc 1",0);
	resume(pid);
	xdone();
	return 0;

}

2.2 Listas ligadas de procesos.


El administrador de procesos se encarga manipular los procesos del sistema operativo. 
A dichos procesos los forma en una lista.

A cada proceso se le asigna un identificador de procesos (process id), que lo hace unico
en todo el sistema operativo.

Las listas que maneja el sistema operativo pueden ser colas FIFO, otras ordenadas por una
llave, unas uniligadas o doblemente ligadas.

Se puede crear un estructura de datos con una lista doblemente ligada (cada nodo
apunta a su predecesor y sucesor), cada nodo contiene una lave y la lista tiene una cabeza
y cola (head y tail).

El campo de llave puede quedar determinado por un número entero, de tal manera que el
nodo de la cabeza tiene la llave entera mas pequeña y la cola tiene la llave entera mas
grande.

El sucesor de la cola y el predecesor de la cabeza son nulos. Cuando la lista está vacia,
el sucesor de la cabeza es la cola, y el predecesor de la cola es la cabeza.

El siguiente código en C muestra la implantación en Xinu. (Ver archivo q.h)

/* q.h - firstid, firstkey, isempty, lastkey, nonempty */

/* q structure declarations, constants, and inline procedures           */

#define NQENT           NPROC + NSEM + NSEM + 4 /* for ready & sleep    */

struct qent {			/* one for each process plus two for    */
				/* each list                            */
	int   qkey;		/* key on which the queue is ordered    */
	int   qnext;		/* pointer to next process or tail      */
	int   qprev;		/* pointer to previous process or head  */
};

extern  struct  qent q[];
extern  int     nextqueue;

/* inline list manipulation procedures */

#define isempty(list)   (q[(list)].qnext >= NPROC)
#define nonempty(list)  (q[(list)].qnext < NPROC)
#define firstkey(list)  (q[q[(list)].qnext].qkey)
#define lastkey(tail)   (q[q[(tail)].qprev].qkey)
#define firstid(list)   (q[(list)].qnext)

#define EMPTY   -1              /* equivalent of null pointer           */

La estructura qent representa un nodo de la lista doblemente ligada. El campo qkey
tiene el dato, que basicamente es el identificador de proceso. El campo qnext es el
apuntador al siguiente proceso y qprev al anterior.

La lista tiene un tamaño NQENT, que es igual al numero máximo de procesos NPROC
(definido como 30 en el archivo proc.h) y tambien permite manipular semaforos con un
total de NSEM (45) y espacio para aquellos procesos que estan en estado de listos y
dormidos.

Las macros isempty, nonempty, firstkey y firstid permiten, dando la cabeza
de la lista obtener información sobre la misma y lastkey permite tomar el ultimo elemento
de la lista. 
La cabeza de la lista tiene un campo qkey igual al minimo entero y la cola de la lista
tiene un campo qkey igual al maximo entero.


El siguiente programa, que es un proceso que se inserta en el propio Xinu, muestra el estado
de la cola.

#include 
#include 

extern int rdytail;
extern int rdyhead;
extern int numproc;
extern int currpid;
void imprimircola(void)
{
	int i;

	/*imprimir la cola q*/ 
	for (i=0;i
#include 
#include 

/*------------------------------------------------------------------------
 *  enqueue  --  insert an item at the tail of a list
 *------------------------------------------------------------------------
 */
int enqueue(item, tail)
int item;				/* item to enqueue on a list	*/
int tail;				/* index in q of list tail	*/
{
	struct	qent	*tptr;		/* points to tail entry		*/
	struct	qent	*mptr;		/* points to item entry		*/

	tptr = &q[tail];
	mptr = &q[item];
	mptr->qnext = tail;
	mptr->qprev = tptr->qprev;
	q[tptr->qprev].qnext = item;
	tptr->qprev = item;
	return(item);
}


/*------------------------------------------------------------------------
 *  dequeue  --  remove an item from a list and return it
 *------------------------------------------------------------------------
 */
int dequeue(item)
int item;
{
	struct	qent	*mptr;		/* pointer to q entry for item	*/

	mptr = &q[item];
	q[mptr->qprev].qnext = mptr->qnext;
	q[mptr->qnext].qprev = mptr->qprev;
	return(item);
}

La funcion enqueue, dado un numero de elemento de la lista, arregla los apuntadores para
poner el elemento al final de la lista.
La funcion dequeue elimina un elemento de la lista, simplemente moviendo los apuntadores
del antecesor y sucesor.

enqueue se utiliza para que un proceso que este en espera de un semaforo se quede en
la lista de espera

dequeue se utiliza cuando un proceso se suspende, se elimina de la cola de procesos

El siguiente programa pone a un proceso al final de la cola de listos


#include 
#include 
#include 

extern int rdytail;


void imprimircola(void)
{
	int i;

	/*imprimir la cola q*/ 
	for (i=0;i
#include 
#include 

/*------------------------------------------------------------------------
 *  insert  --  insert a process into a q list in key order
 *------------------------------------------------------------------------
 */
int insert(proc, head, key)
int proc;				/* process to insert		*/
int head;				/* q index of head of list	*/
int key;				/* key to use for this process	*/
{
	int	next;			/* runs through list		*/
	int	prev;

	next = q[head].qnext;
	while (q[next].qkey < key)	/* tail has MAXINT as key	*/
		next = q[next].qnext;
	q[proc].qnext = next;
	q[proc].qprev = prev = q[next].qprev;
	q[proc].qkey  = key;
	q[prev].qnext = proc;
	q[next].qprev = proc;
	return(OK);
}

getfirst - remueve y retorna el primero proceso en la cola de prioridad
getlast - remueve y retorna el ultimo proceso de la lista

(ver archivo getitem.c)

/* getitem.c - getfirst, getlast */

#include 
#include 
#include 

/*------------------------------------------------------------------------
 *  getfirst  --  remove and return the first process on a list
 *------------------------------------------------------------------------
 */
int	getfirst(head)
	int	head;			/* q index of head of list	*/
{
	int	proc;			/* first process on the list	*/

	if ((proc=q[head].qnext) < NPROC)
		return( dequeue(proc) );
	else
		return(EMPTY);
}



/*------------------------------------------------------------------------
 *  getlast  --  remove and return the last process from a list
 *------------------------------------------------------------------------
 */
int	getlast(tail)
	int	tail;			/* q index of tail of list	*/
{
	int	proc;			/* last process on the list	*/

	if ((proc=q[tail].qprev) < NPROC)
		return( dequeue(proc) );
	else
		return(EMPTY);
}

El siguiente programa muestra el uso de la funcion insert, que equivale a poner en la cola de procesos listos 
a los 3 procesos creados.

#include 
#include 
#include 

extern int rdytail;
extern int rdyhead;


void imprimircola(void)
{
	int i;

	/*imprimir la cola q*/ 
	for (i=0;i
#include 
#include 

/*------------------------------------------------------------------------
 *  newqueue  --  initialize a new list in the q structure
 *------------------------------------------------------------------------
 */
int	newqueue()
{
	struct	qent	*hptr;		/* address of new list head	*/
	struct	qent	*tptr;		/* address of new list tail	*/
	int	hindex, tindex;		/* head and tail indexes	*/

	hptr = &q[ hindex=nextqueue++ ];/* nextqueue is global variable	*/
	tptr = &q[ tindex=nextqueue++ ];/*  giving next used q pos.	*/
	hptr->qnext = tindex;
	hptr->qprev = EMPTY;
	hptr->qkey  = MININT;
	tptr->qnext = EMPTY;
	tptr->qprev = hindex;
	tptr->qkey  = MAXINT;
	return(hindex);
}

Se puede observar que el elemento nextqueue se supone inicializado (el sistema operativo lo pone a NPROC) y se
insertan dos entradas para el head y tail. El sistema operativo pone los valores rdyhead  y rdytail en NPROC+1
y NPROC+2


2.6 Bloque de control de procesos

El sistema operativo guarda la informacion de todos los procesos en una estructura de datos denominada la tabla de
procesos, por cada proceso existe una entrada a dicha tabla. 
En cada entrada de la tabla se guarda 
	- el estado del proceso
	- su prioridad
	- semaforo y mensaje si esta usando
	- la direccion del proceso del stack
	- la longitud del stack
	- el numero de argumentos que recibe el proceso
	- el nombre del proceso
	- la direccion inicial del codigo que se ejecuta

En el siguiente archivo de encabezado (proc.h) se muestra la definicion del PCB

/* proc.h - isbadpid */
/* 8088 version */

/* process table declarations and defined constants			*/

#ifndef	NPROC				/* set the number of processes	*/
#define	NPROC		30		/*  allowed if not already done	*/
#endif

/* process state constants */

#define	PRCURR		'\01'		/* process is currently running	*/
#define	PRFREE		'\02'		/* process slot is free		*/
#define	PRREADY		'\03'		/* process is on ready queue	*/
#define	PRRECV		'\04'		/* process waiting for message	*/
#define	PRSLEEP		'\05'		/* process is sleeping		*/
#define	PRSUSP		'\06'		/* process is suspended		*/
#define	PRWAIT		'\07'		/* process is on semaphore queue*/

/* miscellaneous process definitions */

#define	PNMLEN		9		/* length of process "name"	*/
#define	NULLPROC	0		/* id of the null process; it	*/
					/*  is always eligible to run	*/

#define	isbadpid(x)	(x<=0 || x>=NPROC)

/* process table entry */

struct	pentry	{
	char	pstate;			/* process state: PRCURR, etc.	*/
	int	pprio;			/* process priority		*/
	int	psem;			/* semaphore if process waiting	*/
	int	pmsg;			/* message sent to this process	*/
	int	phasmsg;		/* nonzero iff pmsg is valid	*/
	char	*pregs;			/* saved environment		*/
	char	*pbase;			/* base of run time stack	*/
	word	plen;			/* stack length	in bytes	*/
	char	pname[PNMLEN+1];	/* process name			*/
	int	pargs;			/* initial number of arguments	*/
	int	(*paddr)();		/* initial code address		*/
	
	/* Additional user field for the OS labs			*/
	/* Added by Eli Biham and Rivki Matosevitch 18/10/93		*/
	int	user_int1;
	int	user_int2;
	int	user_int3;
	int	user_int4;
	int	user_int5;
	int	user_int6;
	int	user_int7;
	long	user_long1;
	long	user_long2;
	long	user_long3;
};

extern	struct	pentry proctab[];
extern	int	numproc;		/* currently active processes	*/
extern	int	nextproc;		/* search point for free slot	*/
extern	int	currpid;		/* currently executing process	*/


El siguiente programa imprime la tabla de procesos

#include 
#include 

xmain(){
struct pentry *pcurr=0;
int i;
for (i=0;ipstate == PRCURR || pcurr->pstate == PRREADY)
        printf("PID %d Estado %d Prioridad %d Contexto %x Base %x Longitud %d Nombre %s NArgs %d Codigo %x\n", i,
	pcurr->pstate,pcurr->pprio,pcurr->pregs,pcurr->pbase,
	pcurr->plen,pcurr->pname,pcurr->pargs,pcurr->paddr);
}
xdone();
return 0;
}

2.7 Cambio de contexto

Cuando un proceso es seleccionado por el planificador para no ejecutarse, debe de abandonar el CPU. Y el CPU
debe otorgar la ejecución a un proceso que está en la cola de procesos listos (con estado READY).

El cambio o conmutación de contexto consiste en el hecho de conservar el estado del proceso en ejecución y cargar
el estado guardado del nuevo proceso.

El contexto de un proceso incluye el valor de los registros de la CPU, el estado del proceso y la información
sobre la administración de la memoria. Cuando ocurre una conmutación de contexto, el kernel del sistema operativo
guarda el contexto del proceso anterior en su PCB y carga el del nuevo proceso programado para ejecución.
El tiempo que tarda el sistema operativo en aplicar la conmutación es trabajo adicional del kernel y mientras
el sistema no realiza trabajo util mientras se efectua el cambio de contexto. Su velocidad varia de máquina a
máquina, dependiendo de la velocidad de memoria, el numero de registros del CPU que deben de copiarse. La
velocidad hoy en dia va de 1 a 1000 microsegundos.

Algunos procesadores tienen hardware especial para soportar el cambio de contexto.

En el caso de Xinu, el cambio de contexto consiste en guardar el estado del proceso y aplicar un cambio de
pilas. Dicha rutina se escribio en lenguaje ensamblador y es dependiente de la computadora a donde se porte.

Su logica es la siguiente:

1. Recibe dos parametros, el apuntador a la pila (SP) del proceso en el CPU y el apuntador a la pila (SP) del
proceso a ser ubicado en el CPU (esto es, el campo pregs de la entrada del PCB)
2. Se guarda como siempre el registro bp en la pila (push bp)
3. Se asigna el registro sp al registro bp, para empezar a tomar parametros
4. Guarda el registro FLAGS del CPU en la pila (pushf)
5. Desactiva las interrupciones para evitar que algun otra rutina tome control del CPU (cli)
6. Guarda los registros SI,DI en la pila, debido a que las rutinas en lenguaje C asume que dichos registros
no cambian a lo largo de las llamadas a los procedimientos. (push si, push di)

	El estado de la pila hasta el paso 6 es
				
BP Anterior| FLAGS| SI | DI |        |     |Stack Anterior|Nuevo Stack| 

                            ^        ^	   ^	          ^ 
			    |	     |     |	          |
			   SP 	     BP	  BP+4		BP+6    

adonde apunta SP se puede denominar como el estado del proceso guardado.

7. Se guarda en el registro BX la direccion del pila anterior (mov bx,[bp+4])
8. Se guarda en el apuntador de la pila de la rutina (SP) en la celda dada por bx (mov [bx],sp). Esto permite
que el PROCESO ANTERIOR registre en que parte de la rutina que estaba ejecutando se habia quedado, dejando
almacendado en la entrada del PCB la direccion actual de la pila
9. Se guarda en el registro BX la direccion del stack nuevo (mov bx,[bp+6])
10. Se asigna al apuntador de la pila actual (SP) la direccion de la pila nueva (mov sp,[bx]). Esto permite
que de manera inmediata la computadora quede referenciando a otra pila
11. Se desapilan los registros DI, SI, FLAGS y BP (pop di, pop si, popf, pop bp)
12. Se retorna de la rutina ensamblador. Dado que es otra pila distinta, la direccion de retorno sera diferente
a la de la llamada original.


El truco consiste entonces en que en el PCB se almacene la dirección de la pila como quedo en el cambio de
contexto. Cuando el proceso que se quito del CPU necesita ponerse en el mismo otra vez, basta con indicar 
la dirección del apuntador de la pila para recuperar su estado original y volver al punto donde se habia quedado
en ejecución el proceso.


El codigo del cambio de contexto se puede encontar en el archivo ctxsw.asm

; ctxsw.asm - _ctxsw

 	include	dos.asm

	dseg
; null data segment
	endds

	pseg

	public	_ctxsw

;-------------------------------------------------------------------------
; _ctxsw  --  context switch
;-------------------------------------------------------------------------
; void ctxsw(opp,npp)
; char *opp, *npp;
;---------------------------------------------------------------------
; Stack contents upon entry to ctxsw:
;	SP+4 => address of new context stack save area
;       SP+2 => address of old context stack save area
;       SP   => return address
; The addresses of the old and new context stack save areas are
; relative to the DS segment register, which must be set properly 
; to access the save/restore locations.
;
; The saved state consists of the current BP, SI and DI registers,
; and the FLAGS register
;---------------------------------------------------------------------
_ctxsw	proc	near
	push	bp
	mov	bp,sp		; frame pointer
	pushf			; flags save interrupt condition
	cli			; disable interrupts just to be sure
	push	si
	push	di		; preserve registers
	mov	bx,[bp+4]	; old stack save address
	mov	[bx],sp
	mov	bx,[bp+6]	; new stack save address
	mov	sp,[bx]
	pop	di
	pop	si
	popf			; restore interrupt state
	pop	bp
	ret
_ctxsw	endp

	endps

	end


El siguiente programa en C muestra el uso del cambio de contexto

#include 
#include 
#include 

extern int currpid;

struct pentry *pactual;
struct pentry *pnuevo;
codigo1()
{
	printf("cambio a nuevo contexto\n");
        /*cambiar al contexto viejo*/
	ctxsw(&pnuevo->pregs,&pactual->pregs);
	printf("Este mensaje nunca se despliega");
	return 0;
}
xmain()
{
	int pid,i;
	pid=create(codigo1,INITSTK,15,"proc 1",0);
	pactual = &proctab[currpid];
	pnuevo = &proctab[pid]; 
	/*cambiar contexto*/
	printf("viejo contexto %x nuevo contexto %x\n",pactual->pregs,pnuevo->pregs);
	ctxsw(&pactual->pregs,&pnuevo->pregs);
	printf("Recuperando contexto inicial \n");
	printf("viejo contexto %x nuevo contexto %x\n",pactual->pregs,pnuevo->pregs);
	/*un cambio de contexto a si mismo debe dejarlo en un estado sin  cambio*/
	ctxsw(&pactual->pregs,&pactual->pregs);
	printf("Fin de proceso");
	xdone();
	return 0;
}


2.8 Planificación de procesos.

En todo sistema operativo tipicamente existe un solo proceso en estado de ejecucion y los demas en estado de
listo. Los procesos son clasificados al estado de listos (READY) cuando son elegibles para los servicios del CPU
pero no están actualmente ejecutandose. 

Para aplicar el cambio de contexto de un proceso, previamente se debe seleccionar un proceso de aquellos que
están en estado de listo y darle el control del CPU.

El software que implanta la politica usada para seleccionar un proceso entre aquellos que estan listos se 
denomina planificador (scheduler).

La politica que sigue el planificador de Xinu es:
En cualquier tiempo, el proceso que se puede elegir para servicio del CPU es el de más alta prioridad. Entre
aquellos procesos de igual prioridad, la planificación es de tipo round-robin.


La planificación round-robin significa que los procesos que son seleccionados uno despues del otro son aquellos
miembros que fueron llegando de una manera ordenada e insertados según el orden de llegada. 

En la cola de prioridad de Xinu, con la rutina insert asegura este orden. 

Además una planificación round robin no solo se limita a la política a del primero en llegar es el primero en 
ser servido (First Come, First Served,FCFS); también se introduce el concepto de apropiación para poder lograr 
la conmutación de procesos.

La apropiación se da cuando se define una unidad de tiempo, denominada quantum o porción de tiempo, y que
es una medida de cuanto tiempo de procesamiento tiene asignado un CPU antes de que sea elegido para ser quitado
del CPU. El quantum tipicamente puede ser de 10 a 100 milisegundos.

La planifiación Round-Robin mantiene la cola de listo como una cola de prioridad. Los procesos nuevos, que
tipicamente tienen la prioridad más alta, se agregan al final de la lista. El planificador de la CPU toma
el primero proceso de la cola de listo, fija el valor de un temporizador para interrumpir después de que 
transcurra 1 Quantum y despacha el proceso.

Si el proceso ocupa menos de 1 quantum en tiempo de CPU, entrega de manera voluntaria al mismo y por tanto
el planificador pasará al siguiente proceso en la cola de listos. En caso de que ocupe mas de 1 quantum, el
temporizador se apagará y provocará una interrupción que se comunica al sistema operativo. Se ejecuta una 
conmutación de contexto y el proceso será puesto en la cola de prioridades, según la prioridad que tenga.
El planificador pondra en el CPU el siguiente proceso en la cola de listos.

En el caso de Xinu, siempre tiene la referencia al proceso ejecutandos, por medio de la variable global currpid.
Cuando el planificador tiene que aplicar un cambio de contexto, utiliza currpid para indexar la tabla de procesos
y aplicar el criterio de planificación round-robin. 

El planificador solo bajo una condición no puede ser aplicado y es cuando el kernel esta realizando actividades
criticas del sistema y en esta idea Xinu debe tener control total del CPU. Xinu utiliza una variable globlal
denominada pcxflag la cual es 0 cuando no se habilita la planificación (se define en el archivo intmap.asm y
las rutinas que la manejan estan en el archivo xeidi.asm)

El codigo del planificador es aplicado por la rutina resched (archivo resched.c)


/* resched.c - resched */

#include 
#include 
#include 
#include 

/*------------------------------------------------------------------------
 *  resched  --  reschedule processor to highest priority ready process
 *
 * Notes:	Upon entry, currpid gives current process id.
 *		Proctab[currpid].pstate gives correct NEXT state for
 *			current process if other than PRCURR.
 *------------------------------------------------------------------------
 */
int	resched()
{
	register struct	pentry	*optr;	/* pointer to old process entry */
	register struct	pentry	*nptr;	/* pointer to new process entry */

	optr = &proctab[currpid];
	if ( optr->pstate == PRCURR ) {
		/* no switch needed if current prio. higher than next	*/
		/* or if rescheduling is disabled ( pcxflag == 0 )	*/
		if ( sys_pcxget() == 0 || lastkey(rdytail) < optr->pprio )
			return;
		/* force context switch */
		optr->pstate = PRREADY;
		insert(currpid,rdyhead,optr->pprio);
	} else if ( sys_pcxget() == 0 ) {
		kprintf("pid=%d state=%d name=%s",
			currpid,optr->pstate,optr->pname);
		panic("Reschedule impossible in this state");
	}

	/* remove highest priority process at end of ready list */

	nptr = &proctab[ (currpid = getlast(rdytail)) ];
	nptr->pstate = PRCURR;		/* mark it currently running	*/
	preempt = QUANTUM;		/* reset preemption counter	*/
	ctxsw(&optr->pregs,&nptr->pregs);

	/* The OLD process returns here when resumed. */
	return;
}


El planificador manipula una variable global, denominada preempt, que contiene el numero de unidades del
Quantum

El siguiente código muestra el uso de la función resched(), dando 3 procesos con distintas prioridades


#include 
#include 
#include 

extern int rdytail;
extern int rdyhead;


void imprimircola(void)
{
	int i;

	/*imprimir la cola q*/ 
	for (i=0;i
#include 
#include 
#include 

/*------------------------------------------------------------------------
 *  ready  --  make a process eligible for CPU service
 *------------------------------------------------------------------------
 */
int	ready (pid)
	int	pid;			/* id of process to make ready	*/
{
	register struct	pentry	*pptr;

	if (isbadpid(pid))
		return(SYSERR);
	pptr = &proctab[pid];
	pptr->pstate = PRREADY;
	insert(pid,rdyhead,pptr->pprio);
	return(OK);
}


Al siguiente codigo crea un proceso, lo pone en estado de ready via la funcion ready() e imprime
la cola de procesos.

#include 
#include 
#include 

extern int rdytail;
extern int rdyhead;


void imprimircola(void)
{
	int i;

	/*imprimir la cola q*/ 
	for (i=0;i
#include 
#include 

/*------------------------------------------------------------------------
 *  resume  --  unsuspend a process, making it ready; return the priority
 *------------------------------------------------------------------------
 */

SYSCALL resume(pid)
int pid;
{
	int	ps;			/* saved processor status	*/
	struct	pentry	*pptr;		/* pointer to proc. tab. entry	*/
	int	prio;			/* priority to return		*/

	disable(ps);
	if (isbadpid(pid) || (pptr = &proctab[pid])->pstate != PRSUSP) {
		restore(ps);
		return(SYSERR);
	}
	prio = pptr->pprio;
	ready(pid);
	resched();
	restore(ps);
	return(prio);
}

El primer ejemplo de este capitulo muestra el uso de resume()


2.12 Suspensión de un proceso.
Un proceso se puede suspender si está en estado de READY o CURRENT (en ese caso el mismo ordeno su suspension).
Cuando está en estado de READY, se debe eliminar de la cola de procesos listos (usando la función dequeue).
Cuando está en estado de ejecución, CURRENT, se debe invocar al planificador.
En ambos casos, el proceso debe tener un estado distinto, suspendido, por lo que el campo pstate del PCB tiene
el valor constante PRSUSP.
Al aplicar la suspensión de un proceso, también se debe aplicar la activación y desactiviación de interrupciones.

La función que aplica la suspension se denomina suspend, que recibe un pid (archivo suspend.c)
/* suspend.c - suspend */
/* 8086 version */

#include 
#include 
#include 

/*------------------------------------------------------------------------
 *  suspend  --  suspend a process, placing it in hibernation
 *------------------------------------------------------------------------
 */
SYSCALL	suspend(pid)
	int	pid;			/* id of process to suspend	*/
{
	struct	pentry	*pptr;		/* pointer to proc. tab. entry	*/
	int	ps;			/* saved processor status	*/
	int	prio;			/* priority returned		*/

	disable(ps);
	if (isbadpid(pid) || pid==NULLPROC ||
	 ((pptr= &proctab[pid])->pstate!=PRCURR && pptr->pstate!=PRREADY)) {
		restore(ps);
		return(SYSERR);
	}
	if (pptr->pstate == PRREADY) {
		dequeue(pid);
		pptr->pstate = PRSUSP;
	} else {
		pptr->pstate = PRSUSP;
		resched();
	}
	prio = pptr->pprio;
	restore(ps);
	return(prio);
}


El siguiente código muestra como se usa la función suspend de Xinu

#include 
#include 
#include 

extern int rdytail;
extern int rdyhead;
extern int currpid;


void imprimircola(void)
{
	int i;

	/*imprimir la cola q*/ 
	for (i=0;i
#include 
#include 
#include 
#include 

/*------------------------------------------------------------------------
 *  kill  --  kill a process and remove it from the system
 *------------------------------------------------------------------------
 */
SYSCALL kill(pid)
	int	pid;			/* process to kill		*/
{
	struct	pentry	*pptr;		/* points to proc. table for pid*/
	int	ps;			/* saved processor status	*/

	disable(ps);
	if (isbadpid(pid) || (pptr = &proctab[pid])->pstate==PRFREE) {
		restore(ps);
		return(SYSERR);
	}
	if (--numproc == 0)
		xdone();
	freestk(pptr->pbase, pptr->plen);
	switch (pptr->pstate) {

	case PRCURR:	pptr->pstate = PRFREE;	/* suicide */
			resched();

	case PRWAIT:	semaph[pptr->psem].semcnt++;
						/* fall through */
	case PRSLEEP:
	case PRREADY:	dequeue(pid);
						/* fall through */
	default:	pptr->pstate = PRFREE;
	}
	restore(ps);
	return(OK);
}

2.14 Creación de un proceso.

Cuando un proceso se crea, se debe indicar el código a ejecutar, el tamaño sugerido de la pila, su prioridad
inicial, nombre y argumentos.

La creación de un proceso es basicamente llenar la entrada del PCB asignado, con una busqueda previa de una
entrada libre y pedir a las rutinas de manejo de memoria espacio para la pila. Un proceso recien creado 
tiene el estado de suspendido. Llena la dirección del apuntador de la pila e indica que al concluir un proceso
debe ejecutar una rutina que lo finalice y que llama a la rutina kill().

La creación también se hace bajo el contexto de desactivar y activar interrupciones.

El código se encuentra en el archivo create.c, que contiene la funcione create y una función denominada
newpid, que retorna el PID de la entrada del PCB que este libre

/* create.c - create, newpid */

#include 
#include 
#include 
#include 

#define	INITF	0x0200	/* initial flag register - set interrupt flag,	*/
			/* clear direction and trap flags		*/

extern	int	INITRET();	/* location to return upon termination	*/

/*------------------------------------------------------------------------
 *  create  --  create a process to start running a procedure
 *------------------------------------------------------------------------
 */
SYSCALL create(procaddr,ssize,priority,namep,nargs,args)
int (*procaddr)();		/* procedure address			*/
word ssize;			/* stack size in words			*/
short priority;			/* process priority > 0			*/
char *namep;			/* name (for debugging)			*/
int nargs;			/* number of args that follow		*/
int args;			/* arguments (treated like an array)	*/
{
	int	pid;			/* stores new process id	*/
	struct	pentry	*pptr;		/* pointer to proc. table entry */
	int	i;			/* loop variable		*/
	int	*a;			/* points to list of args	*/
	char	*saddr;			/* start of stack address	*/
	int	*sp;			/* stack pointer		*/
	int	ps;			/* saved processor status	*/

	disable(ps);
	ssize = roundew(ssize);
	if ( ssize < MINSTK || priority < 1 ||
		(pid=newpid()) == SYSERR ||
		((saddr=getstk(ssize)) == NULL ) ) {
		restore(ps);
		return(SYSERR);
	}
	numproc++;
	pptr = &proctab[pid];
	pptr->pstate = PRSUSP;
	for (i=0 ; ipname[i] = (*namep ? *namep++ : ' ');
	pptr->pname[PNMLEN]='\0';
	pptr->pprio = priority;
	pptr->phasmsg = 0;		/* no message			*/
	pptr->pbase = saddr;
	pptr->plen = ssize;
	sp = (int *) (saddr+ssize);	/* simulate stack pointer	*/
	sp -= 4;			/* a little elbow room		*/
	pptr->pargs = nargs;
	a = (&args) + nargs;		/* point past last argument	*/
	for ( ; nargs > 0 ; nargs--)	/* machine dependent; copy args	*/
		*(--sp) = *(--a);	/*  onto created process' stack	*/
	*(--sp) = (int)INITRET;		/* push on return address	*/
	*(--sp) = (int)procaddr;	/* simulate a context switch	*/
	--sp ;				/* 1 word for bp		*/
	*(--sp) = INITF;		/* FLAGS value			*/
	sp -= 2;			/* 2 words for si and di	*/
	pptr->pregs = (char *)sp;	/* save for context switch	*/
	pptr->paddr = procaddr;
	restore(ps);
	return(pid);
}

/*------------------------------------------------------------------------
 *  newpid  --  obtain a new (free) process id
 *------------------------------------------------------------------------
 */
LOCAL	newpid()
{
	int	pid;			/* process id to return		*/
	int	i;

	for (i=0 ; i
#include 

/*------------------------------------------------------------------------
 *  userret  --  entered when a process exits by return
 *------------------------------------------------------------------------
 */
userret()
{
	int	pid;

	kill( pid=getpid() );
	kprintf("Fatal system error - unable to kill process %d",pid);
}



2.15 Habilitar y deshabilitar interrupciones

Las rutinas para habilitar y deshabilitar interrupciones se escriben en ensambaldor. Aunque en el código
se manejan como disable() y restore(), son realmente macros definidas en el archivo de encabezado del kernel
(kernel.h) que se presenta a continuación.

/* kernel.h - isodd, disable, restore, enable, pause, halt, xdisable, xrestore */
/* 8086/8 PC-Xinu version - for IBM PC/XT/AT and Clones */

/* Symbolic constants used throughout Xinu */

typedef	char		Bool;		/* Boolean type			*/
typedef unsigned int	word;		/* word type			*/

#define	FALSE		0		/* Boolean constants		*/
#define	TRUE		1
#define	NULL		(char *)0	/* Null pointer for linked lists*/
#define	SYSCALL		int		/* System call declaration	*/
#define	LOCAL		static		/* Local procedure declaration	*/
#define	INTPROC		int		/* Interrupt procedure  	*/
#define	PROCESS		int		/* Process declaration		*/
#define WORD		word		/* 16-bit word			*/ 
#define	MININT		0100000		/* minimum integer (-32768)	*/
#define	MAXINT		0077777		/* maximum integer (+32767)	*/
#define	MINSTK		256		/* minimum process stack size	*/
#define	NULLSTK		256		/* process 0 stack size		*/
#define	OK		 1 		/* returned when system	call ok	*/
#define	SYSERR		-1		/* returned when sys. call fails*/

/* initialization constants */

#define	INITARGC	2		/* initial process argc		*/
#define	INITSTK		512		/* initial process stack	*/
#define	INITPRIO	20		/* initial process priority	*/
#define	INITNAME	"xmain"		/* initial process name		*/
#define	INITRET		userret		/* processes return address	*/
#define	INITREG		0		/* initial register contents	*/
#define	QUANTUM		1		/* clock ticks until preemption	*/

/* misc. utility functions */

#define	isodd(x)	(01&(int)(x))
#define	disable(x)	(x)=sys_disabl() /* save interrupt status	*/
#define	restore(x)	sys_restor(x)	/* restore interrupt status	*/
#define	enable()	sys_enabl()	/* enable interrupts		*/
#define	pause()		sys_wait()	/* machine "wait for interr."	*/
#define	halt()		sys_hlt()	/* halt PC-Xinu			*/
#define xdisable(x)	(x)=sys_xdisabl() /* save int & dosflag status	*/
#define xrestore(x)	sys_xrestor(x)	/* restore int & dosflag status	*/

/* system-specific functions and variables */

extern	int	sys_disabl();		/* return flags & disable ints	*/
extern	void	sys_restor();		/* restore the flag register	*/
extern	void	sys_enabl();		/* enable interrupts		*/
extern	void	sys_wait();		/* wait for an interrupt	*/
extern	void	sys_hlt();		/* halt the processor		*/
extern	int	sys_xdisabl();		/* Return interrupts to MS-DOS	*/
extern	void	sys_restor();		/* Interrupts back to Xinu	*/

/* process management variables */

extern	int	rdyhead, rdytail;
extern	int	preempt;

El codigo ensamblador que aplica la deshabilitacion de las interrupciones de denomina sys_disabl y el
codigo que restaura las interrupciones se denomina sys_restor. Está definido en el archivo eidi.asm y contiene
otras rutinas que posteriormente se estudiaran

; eidi.asm - _sys_disabl, _sys_enabl, _sys_restor, _sys_wait, _sys_hlt

	include	dos.asm		; segment macros

	dseg
; null data segment
	endds

	pseg

	public	_sys_disabl,_sys_restor,_sys_enabl,_sys_wait,_sys_hlt

;-------------------------------------------------------------------------
; _sys_disabl  --  return interrupt status and disable interrupts
;-------------------------------------------------------------------------
; int sys_disabl()
_sys_disabl	proc	near
	pushf			; put flag word on the stack
	cli			; disable interrupts!
	pop	ax		; deposit flag word in return register
	ret
_sys_disabl	endp

;-------------------------------------------------------------------------
; _sys_restor  --  restore interrupt status
;-------------------------------------------------------------------------
; void sys_restor(ps)
; int ps;
_sys_restor	proc	near
	push	bp
	mov	bp,sp		; C calling convenion
	push	[bp+4]
	popf			; restore flag word
	pop	bp
	ret
_sys_restor	endp

;-------------------------------------------------------------------------
; _sys_enabl  --  enable interrupts unconditionally
;-------------------------------------------------------------------------
; void sys_enabl()
_sys_enabl	proc	near
	sti			; enable interrupts
	ret
_sys_enabl	endp

;-------------------------------------------------------------------------
; _sys_wait  --  wait for interrupt
;-------------------------------------------------------------------------
; void sys_wait()
_sys_wait	proc	near
	pushf
	sti			; interrupts must be enabled here
	hlt
	popf
	ret
_sys_wait	endp

;-------------------------------------------------------------------------
; _sys_hlt  --  halt the current program and return to host
;-------------------------------------------------------------------------
; void sys_hlt()
_sys_hlt	proc	near
	mov	ah,4ch		; terminate function
	xor	al,al		; OK return code
	int	21h		; MS-DOS function call
	ret
_sys_hlt	endp

	endps

	end

La rutina sys_disabl, guarda en la pila de la rutina actual el registro FLAGS e invoca a la instrucción cli,
que permite deshabilitar interrupciones. Pone en el registro AX el valor de las banderas, la cual se retorna
a la rutina que le invoca.
La rutina sys_restor restuara via la funcion popf el registro FLAG


2.16 Temporizador.

Cuando se hablo del planificador se mencionó el concepto de un temporizador. Un temporizador es una rutina que
se dispara al momento que recibe una interrupción del reloj de la computadora. Cuando se estudie el manejo del
reloj se verá a mas detalle, pero es interesante mostrar la rutina que es activada por el temporizador.

/* clkint.c - clkint */

#include 
#include 
#include 
#include 

/*------------------------------------------------------------------------
 *  clkint  --  clock service routine
 *  called at every clock tick and when starting the deferred clock
 *------------------------------------------------------------------------
 */
INTPROC clkint(mdevno)
int mdevno;				/* minor device number		*/
{
	int	i;

	tod++;
	if (defclk) {
		clkdiff++;
		return;
	}
	if (slnempty)
		if ( (--*sltop) <= 0 )
			wakeup();
	if ( (--preempt) <= 0 )
		resched();
}

Como se puede ver, la variable global preempt es disminuida en una unidad y si llega a un valor menor o igual
que 0, se pide la planificación de procesos. De esta manera, es posible lograr que exista un proceso ejecutandose
y que se interrumpa despues de un tiempo definido.





    Source: geocities.com/gusdelact/cib5122003

               ( geocities.com/gusdelact)