We expand the Tiny-Unix kernel to provide multitasking for three tiny-Unix programs. Here, the console output port (COM2) is a shared device, shared between the three processes. As in hw1 and hw2, we use a small output buffer (6 char capacity) accessed via the queue package. As in hw1 and hw2, we allow writers to proceed when all of their string is at least in the buffer. When the output buffer is full, we make processes block in write, giving up the CPU for other processes to use. Also, block in read, and in a new “sleep” system call. No more spin loops in the kernel!
This is a producer-consumer situation, with 3 producers filling the output buffer and one consumer, the output interrupt handler, consuming the characters. As in hw2, the output is interrupt-driven.
/*********************************************************************
*
*
file: proc.h
*
Process structure info
*
*/
#ifndef
PROC_H
#define
PROC_H
/*
number of processes: 3 user processes, 1 proc0 */
#define
NPROC 4
#define
N_SAVED_REGS 7
/* 7 non-scratch CPU registers, saved in this order:
here SAVED_ESP=0, SAVED_EBX=1, etc. */
enum
cpu_regs {SAVED_ESP, SAVED_EBX, SAVED_ESI, SAVED_EDI,
SAVED_EBP, SAVED_EFLAGS, SAVED_PC};
/*
for p_status ( RUN=1, BLOCKED=2, ZOMBIE=3) */
typedef
enum proc_status {RUN = 1, BLOCKED, ZOMBIE} ProcStatus;
/*
for p_waitcode, what the process is waiting for */
typedef
enum proc_wait {TTY0_OUTPUT = 1, TTY1_OUTPUT} WaitCode;
/*
Process Entry */
typedef
struct {
int p_savedregs[N_SAVED_REGS]; /* saved non-scratch registers */
ProcStatus p_status; /* RUN, BLOCKED, or ZOMBIE */
WaitCode p_waitcode; /* valid only if status==BLOCKED: TTY0_OUT,
etc. */
int p_exitval; /* valid only if
status==ZOMBIE */
}
PEntry;
/*
extern these globals here, define them in tunix.c */
extern
PEntry proctab[], *curproc;
#endif
/*PROC_H */
#
asmswtch.s: switch CPU context for multitasking
#
the job to do here:
#
--save CPU registers in previous-process's PEntry
#
--restore CPU registers from next-process's PEntry
#
all this while running using the CPU to do it's own reconfiguration
.globl
_asmswtch
#
asmswtch--process switch
#
(but we don't need to save C scratch reg's, since they are assumed
#
to be smashed by calling this anyway)
#
Call from C: two arguments, pointers to old and new PEntry's:
#
#
asmswtch(oldpentryp, newpentryp)
#
Stack when reach here:
#
%esp--> return pc
#
4(%esp) oldpentryp
#
8(%esp) newpentryp
#
PEntry: saved esp
#
saved ebx
#
saved esi
#
saved edi
#
saved ebp
#
saved eflags
#
saved pc -- i.e., saved eip (where this was called from)
_asmswtch:
movl 4(%esp),%eax # oldpentryp
movl %esp, (%eax) # save %esp at start of PEntry
movl %ebx, 4(%eax) # followed by other non-scratch regs
movl %esi, 8(%eax)
movl %edi, 12(%eax)
movl %ebp, 16(%eax)
movl (%esp), %ecx
movl %ecx, 24(%eax) # save ret addr=top of stack
pushfl
# push eflags on stack
popl 20(%eax) # pop into saved
eflags
movl 8(%esp), %eax # newpentryp
movl (%eax), %esp # new stack pointer
movl 4(%eax), %ebx # restore from saved regs
movl 8(%eax), %esi
movl 12(%eax), %edi
movl 16(%eax), %ebp
pushl 20(%eax) # new eflags--push on
stack
popfl
# restore eflags
movl 24(%eax), %ecx
movl %ecx,(%esp) # restore top of stack=saved pc
ret
# return on newly reinstated stack