MTX under Linux
/*********** A Multitasking System ************/
#define NULL 0
#define NPROC 9
#define SSIZE 1024
#define NQUEUE NPROC // at most NPROC ready queues
#define FREE 0 // proc status
#define READY 1
#define DEAD 2
#define SLEEP 3
typedef struct proc{
struct proc *next;
int *ksp; /* in Linux: offset = 4 bytes */
int pid; /* pid = 0 to NPROC-1 */
int status;
int pri; /* scheduling priority */
int ppid; /* parent pid */
int kstack[SSIZE]; // Kmode per process stack
}PROC;
struct rqueue{
int priority; // 0, 1,...NQUEUE-1
PROC *queue; // PROC queue of this priority level
} rqueue[NQUEUE]; // at most NPROC separate ready queues
PROC proc[NPROC], *running, *freeList;
int procsize = sizeof(PROC);
int body();
//#include "io.c" // IMPLEMENT your OWN printf() here
/******** getproc()/putproc() ***********/
PROC *getproc()
{
PROC *p;
p = freeList;
if (p)
freeList = freeList->next;
return(p);
}
putproc(p) PROC *p;
{
p->next = freeList;
freeList = p;
}
int initialize()
{
int i; PROC *p;
printf("MTX initializing ....\n");
for (i=0; i < NPROC; i++){
proc[i].pid = i;
proc[i].status = FREE;
proc[i].next = (PROC *)&proc[(i+1)];
}
proc[NPROC-1].next = NULL;
freeList = &proc[0]; // all PROC are FREE initially
for (i=0; i < NQUEUE; i++){ // initialize the scheduling queues
rqueue[i].priority = i;
rqueue[i].queue = 0;
}
// create P0
p = getproc(); // get a FREE PROC
p->status = READY;
p->pri = 0; /* lowest priority 0 */
p->pid = 0; /* process 0 or P0 */
running = p;
p->ppid = running->pid; /* P0's parent is P0 */
printf("initialization complete\n");
}
/******** enqueue()/dequeue() **********/
// enter a PROC into rqueue[i] by its pri value
enqueue(ptr) PROC *ptr;
{
PROC *p;
int i = ptr->pri;
if (rqueue[i].queue==NULL){
rqueue[i].queue = ptr;
}
else{
p = rqueue[i].queue;
while (p->next){
p = p->next;
}
p->next = ptr;
}
ptr->next = 0;
}
PROC *dequeue(qptr) PROC **qptr;
{
PROC *p = *qptr;
if (p)
*qptr = (*qptr)->next;
return p;
}
int printList(p) PROC *p;
{
while(p){
printf("%d ==> ", p->pid);
p = p->next;
}
printf("NULL\n");
}
int printQ()
{
int i;
for (i=0; i < NQUEUE; i++){
if (rqueue[i].queue){
printf("readyQueue[%d] = ", i);
printList(rqueue[i].queue);
}
}
}
/***********************************************************
kfork() creates a child proc and returns the child pid.
When scheduled to run, the child process resumes to body();
************************************************************/
int kfork()
{
PROC *p;
int i, child;
/*** get a PROC for child process: ***/
if ( (p = getproc()) == NULL){
printf("no more proc\n");
return(-1);
}
/* initialize the new proc and its stack */
p->status = READY;
p->ppid = running->pid;
p->pri = 1; // all of the same priority 1
// clear all SAVed registers on stack
for (i=1; i < 10; i++)
p->kstack[SSIZE-i] = 0;
// fill in resume address
p->kstack[SSIZE-1] = (int)body;
// save stack TOP address in PROC
p->ksp = &(p->kstack[SSIZE - 9]);
enqueue(p);
return(p->pid);
}
int do_switch()
{
printf("P%d switch process\n", running->pid);
tswitch();
}
int do_exit()
{
grave();
}
int do_kfork()
{
int new;
new = kfork();
if (new < 0)
printf("kfork failed\n");
else
printf("P%d return from kfork() : child = %d\n",
running->pid, new);
}
/*****************************************
All proc share the same body function,
as if called by the process itself.
******************************************/
int body()
{
char c;
while(1){
printf("------------------------------------------\n");
printf("I am process P%d My parent=%d\n", running->pid, running->ppid);
printf("******************************************\n");
printf("freeList = "); printList(freeList);
printQ();
printf("******************************************\n");
printf("input a command : [s|q|f] : ");
c = getchar();
printf("%c\n", c);
switch(c){
case 's' : do_switch(); break;
case 'q' : do_exit(); break; /* no return */
case 'f' : do_kfork(); break;
default : break;
}
}
}
char *gasp[NPROC]={
"Oh! I'm dying ....\n",
"Oh! You are killing me ....\n",
"Oh! I'm a goner ....\n",
"Oh! I'm mortally wounded ....\n",
};
int grave(){
PROC *p;
printf("*************************************\n");
printf("Process P%d : %c %s", running->pid, 007, gasp[running->pid % 4]);
printf("*************************************\n");
running->status = DEAD;
putproc(running);
tswitch();
}
/*************** main() ***************/
main()
{
printf("\nWelcome to the MTX Operating System\n");
initialize();
printf("P0 forks P1\n");
kfork();
printf("P0 switches to P1\n");
tswitch();
printf("P0 resumes: all dead, happy ending!\n");
}
/****** scheduler *******************/
int scheduler()
{
PROC *p;
int i;
if (running->status == READY)
enqueue(running);
for (i=NQUEUE-1; i>=0; i--){
running = dequeue(&rqueue[i].queue);
if (running)
break;
}
}
/************** s.s file ********************
.globl running,scheduler,tswitch
tswitch:
save: pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
pushl %ebp
pushl %esi
pushl %edi
pushfl
movl running,%ebx
movl %esp,4(%ebx)
find: call scheduler
resume: movl running,%ebx
movl 4(%ebx),%esp
popfl
popl %edi
popl %esi
popl %ebp
popl %edx
popl %ecx
popl %ebx
popl %eax
ret
*********************************************
========================= DO THESE ==========================
1. Under Linux:
Extract the C code as t.c and the assembly code as s.s
cc t.c s.s
run a.out
=============================================================
ASSIGNMENT #2 DUE : Sept 14, 2011
PART 1: Under Linux
--------------------------------------------------------------------------------
Modify the MTX system to implement the following:
REFERENCES: 1. Bach's book: Chapter 6 Structure of Processes
2. Linux man 2 wait, exit
Add an int event field to the PROC structure.
1. sleep(int event):
A process calls sleep(e) to SLEEP on the event e by
record the event e in its PROC,
change its status to SLEEP and tswitch().
A SLEEPing process becomes READY to run again when it is awakened by
another process via wakeup(e):
2. wakeup(int event):
wakeup() wakes up ALL processes that are SLEEP on the event.
3. Use sleep()/wakeup() to implement
int pid = wait(int *status);
which operates as follows:
(1). Every process has a unique parent. When a process dies, it calls
do_exit(int exit_value) in which it
.dispose of its children (if any). Make sure there is always a
"parent" for any orphaned process, e.g. P1, which cannot die if
there still are other processes.
.record exit_value in its PROC.exitStatus field,
.become a ZOMBIE but does NOT release its PROC,,
.wakeup its parent.
(2). A process calls pid = wait(&status); to wait for a (any) ZOMBIE child.
When it finds a ZOMBIE child, it copies the child's exitStatus value
into status, releases the child's PROC and returns the child's pid.
As usual, it returns -1 if any error.
Turn in a copy of your source files that can be compiled and run under Linux
--------------------------------------------------------------------------------
PART 2 : For REAL PC or PC emulators:
1. Download the files form samples/MTX1:
mtximage : bootable MTX FD image ==> boot up and test run it
(SAME as MTX under Linux)
Source files : s.s, YOUR io.c and t.c ==> generates /boot/mtx
2. Port YOUR work in Part 1 to real MTX,
i.e. add
sleep(), wakeup(), and wait() ==> modify PROC and do_exit()
to MTX for REAL PC.
-------------------------------------------------------------------------------