Multiprogrammed computer emulator with semaphores

dvanhorn <[email protected]>
Newsgroups gmane.org.ballistichelmet.lambda
Message-ID <[email protected]>
Use mocaml as in previous post.

  ./semaphore.ml <file>

This file should be available at:

   http://www.cs.uvm.edu/~dvanhorn/ocaml/os/semaphore.ml

-d

----semaphore.ml----

#!/home/cs/csugrads/dvanhorn/local/bin/mocaml

(**
   Multiprogrammed computer emulator with semaphores

   Copyright (c) 2003 David Van Horn
   Licensed under the Academic Free License version 2.0

   A work of art is a machine with an aesthetic purpose.
   -- James Boyk

   This program simulates the events in a multiprogrammed computer according
   to the following guidelines: Semaphores are used as synchronization
   primitives. The number of semaphores and processes are unbounded. The wait
   and signal operations on the semaphores are atomic.  Scheduling is
   non-preemptive First-Come-First-Served (FCFS).  A process continues to
   execute until a wait operation causes it to sleep on a semaphore.  The
   semaphore implementation associates a list of waiting processes for each
   semaphore (See page 203 of the textbook (6th Ed)).  A counter implements a
   logical clock.  At each increment of the counter the program checks if the
   current counter value (time) matches with an arrival time of any process,
   and enter arriving process(es) automatically into the tail of the ready
   queue.  If a process finishes completely then it leaves the system, and the
   process at the head of the ready queue is allocated the CPU.  A process
   issuing a wait on a semaphore is blocked, and a process at the head of the
   ready queue is allocated the CPU. NB The system remembers the next
   instruction in the process as the point to resume when the process is woken
   up at a later time.

   Simulation continues until all processes finish completely.  Semaphores are
   defined and initialized by a statement of the following format:

   SEM <semaphore number> <initial value>

   An input file includes information for all processes, their arrival times,
   CPU-bursts, wait and signal on semaphores.  Input lines are of six possible
   formats:

   SEM <semaphore number> <initial value>
   BEGIN <process number (<=10)> <arrival time>
   CPU <CPU burst>
   WAIT <semaphore number>
   SIGNAL <semaphore number>
   END /* Process description ends */

   The following is an example:

   SEM 1 0

   BEGIN 1 0
   WAIT 1
   CPU 7
   END

   BEGIN 2 5
   CPU 4
   SIGNAL 1
   END

   The program reports which process executes as there occurs a change.
   Events are reported in the following format (output for above inputs):

   TIME: 0 PRO 1 EXEC
   TIME: 5 PRO 2 EXEC
   TIME: 9 PRO 1 EXEC

   @author David Van Horn
*)

module Semaphore =
  struct
    open List
    open Printf
    open Str

    (**
       An instruction is either a cpu burst, a wait, or a signal.  A wait or
       signal give a semaphore id.
    *)
    type instruction =
      | Burst of int
      | Signal of int
      | Wait of int
	
    (**
       A process is either the NullProcess or consists of a process number, an
       arrival time, and a list of instructions.
    *)
    type process =
      | Process of int * int * instruction list
      | NullProcess
	
    (**
       A semaphore is constituted by a semaphore number (id), a current value,
       and a list of processes.
    *)
    type semaphore = Semaphore of int * int * process list

    let arrive_now_p t = function
      | Process(_,a,_) when a=t -> true
      | _ -> false
	
    let print_event t p =
      printf "TIME %d PRO %d EXEC\n" t (match p with Process(n,_,_) -> n)

    (**
       The step function transitions from machine configuration to machine
       configuration.  A machine configuration is a 5-tuple consisting of a
       queue of processes that have arrived and are waiting to be swapped into
       the cpu, a process that is currently running on the cpu, the current
       time, a list of semaphores, and a list of process that have yet to
       arrive.
    *)
    let step (q,p,t,s,j) =

      (*
	 We push all processes that are scheduled to arrive at the current
	 time onto the queue.
      *)
      let q = append q (filter (arrive_now_p t) j) in

      (*
	 We remove from the list of processes that have yet to arrive those
	 processes which are scheduled to arrive at the current time.
      *)
      let j = filter (fun x -> (not (arrive_now_p t x))) j in

      match (q,p,t,s) with
      (*
	 If the queue is empty and the null process is currently executing
         (the cpu is idle), we have nothing to do, so increment the time and
         continue.
      *)
      | [], NullProcess, t, s -> [], NullProcess, t+1, s, j

      (*
	 If the queue is non-empty and the null process is currently executing
	 (the cpu is idle), we take the next process from the queue and swap
	 it into the cpu.  This operation does not increment the time.
      *)
      | p::q, NullProcess, t, s ->
	  print_event t p;
	  q, p, t, s, j

      (*
	 If the currently executing process has exhausted its instructions,
         the cpu becomes idle. This operation does not increment the time.
      *)
      | q, Process(n,a,[]), t, s -> q, NullProcess, t, s, j

      (*
	 If the current process issues a wait, when the signaled semaphore has
	 a value less than zero, then the value is decremented and the current
	 process is removed from the cpu and placed in the semaphore.
	 Otherwise the current value is decremented and the process continues
	 to run.
      *)
      | q, Process(n,a,Wait(i)::is), t, s ->
	  let ([sem],s') =
	    partition
	      (function
		| Semaphore(id,_,_) when id = i -> true
		| _ -> false)
	      s
	  in
	  (match sem with
	  |	Semaphore(id,v,ps) when v-1 < 0 ->
	      q, NullProcess, t, Semaphore(id,v-1,Process(n,a,is)::ps)::s', j
	  | Semaphore(id,v,ps) ->
	      q, Process(n,a,is), t, Semaphore(id,v-1,ps)::s', j)
	
      (*
	If the current process issues a signal, when the signaled semaphore
        has a value less than or equal to zero, then the next process in the
        semaphore is placed in the ready queue, the value is incremented and
        the current process continues running.  If the signaled semaphore has
        a positive value, the value and time are incremented and the current
        process continues running.
      *)
      | q, Process(n,a,Signal(i)::is), t, s ->
	  let ([sem],s') =
	    partition
	      (function
		| Semaphore(id,_,_) when id = i -> true
		| _ -> false)
	      s
	  in
	  (match sem with
	  |	Semaphore(id,v,p::ps) when v+1 <= 0 ->
	      q@[p], Process(n,a,is), t, Semaphore(id,v+1,ps)::s', j
	  | Semaphore(id,v,ps) ->
	      q, Process(n,a,is), t, Semaphore(id,v+1,ps)::s', j)
	
      (*
	 If the current process has no burst time remaining, we go to the next
         instruction.  This operation does not increment the time.
      *)
      | q, Process(n,a,Burst(0)::is), t, s -> q, Process(n,a,is), t, s, j

      (*
	If the current process has burst time remaining, we let the cpu
        continue to run the process, decrementing the burst and incrementing
        the time.
      *)
      | q, Process(n,a,Burst(i)::is), t, s ->
	  q, Process(n,a,Burst(i-1)::is), t+1, s, j

    let parse file_name =
      let in_channel = (open_in file_name) in
      let rec loop (ss, ps) =
	try
	  match (split (regexp "[ \t]+") (input_line in_channel)) with
	  | ["SEM"; n; i] ->
	      loop (Semaphore(int_of_string n, int_of_string i, [])::ss, ps)
	  | ["BEGIN"; n; a] ->
	      let instrs =
		let rec lp ins =
		  match (split (regexp "[ \t]+") (input_line in_channel)) with
		  | ["CPU"; b] -> lp ((Burst (int_of_string b))::ins)
		  | ["SIGNAL"; i] -> lp ((Signal (int_of_string i))::ins)
		  | ["WAIT"; i] -> lp ((Wait (int_of_string i))::ins)
		  | ["END"] -> rev ins
		in lp []
	      in
	      loop (ss, Process(int_of_string n, int_of_string a, instrs)::ps)
	  | _ -> loop (ss, ps)
	with End_of_file -> (ss, ps)
      in
      loop ([], [])

    (**
       A machine runs until it is in a final configuration, which is defined
       as a configuration with no processes in the waiting queue, an idle cpu,
       and no processes yet to arrive.
    *)	
    let main file_name =
      let rec loop = function
	| [], NullProcess, _, _, [] -> ()
	| state -> loop (step state)
      in
      let (sem_list, proc_list) = parse file_name in
      loop ([], NullProcess, 0, sem_list, proc_list)

  end
;;

Semaphore.main(Sys.argv.(1));;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.