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.
-------------------------------------------------------------------------------