Using Spin to Help Teach Concurrent Programming

Similar documents
The SPIN Model Checker

The Spin Model Checker : Part I/II

Distributed Systems Programming (F21DS1) SPIN: Formal Analysis I

Patrick Trentin Formal Methods Lab Class, Feb 26, 2016

Patrick Trentin Formal Methods Lab Class, March 03, 2017

SPIN: Introduction and Examples

MP 6 Modeling in Promela and SPIN

Interprocess Communication By: Kaushik Vaghani

Proving Dekker with SPIN and PROMELA

The SPIN Model Checker

Tool demonstration: Spin

Chapter 5: Process Synchronization. Operating System Concepts Essentials 2 nd Edition

INF5140: Specification and Verification of Parallel Systems

Operating Systems. Designed and Presented by Dr. Ayman Elshenawy Elsefy

The Design of a Distributed Model Checking Algorithm for Spin

Process Synchronization

Introduction to Linear-Time Temporal Logic. CSE 814 Introduction to LTL

Roadmap. Readers-Writers Problem. Readers-Writers Problem. Readers-Writers Problem (Cont.) Dining Philosophers Problem.

A Tutorial on Model Checker SPIN

Chapter 6: Synchronization. Operating System Concepts 8 th Edition,

SPIN, PETERSON AND BAKERY LOCKS

CS153: Deadlock. Chengyu Song. Slides modified from Harsha Madhyvasta, Nael Abu-Ghazaleh, and Zhiyun Qian

Chapter 6: Process Synchronization. Operating System Concepts 8 th Edition,

Synchronization. CS 475, Spring 2018 Concurrent & Distributed Systems

Computer Lab 1: Model Checking and Logic Synthesis using Spin (lab)

HSF-SPIN User Manual

The Spin Model Checker : Part I. Moonzoo Kim KAIST

Distributed Systems Programming (F21DS1) SPIN: Simple Promela INterpreter

Formal Methods for Software Development

Roadmap. Tevfik Ko!ar. CSC Operating Systems Fall Lecture - XI Deadlocks - II. Louisiana State University

Chapters 5 and 6 Concurrency

Roadmap. Bounded-Buffer Problem. Classical Problems of Synchronization. Bounded Buffer 1 Semaphore Soln. Bounded Buffer 1 Semaphore Soln. Tevfik Ko!

Process Synchronization

Model Requirements and JAVA Programs MVP 2 1

Formal Modeling for Persistence Checking of Signal Transition Graph Specification with Promela

PROCESS SYNCHRONIZATION

Synchronization Principles

Frequently asked questions from the previous class survey

Deadlock and Monitors. CS439: Principles of Computer Systems September 24, 2018

Network Protocol Design and Evaluation

College of Computer & Information Science Spring 2010 Northeastern University 26 January 2010

CSE 153 Design of Operating Systems

Algorithmic Verification. Algorithmic Verification. Model checking. Algorithmic verification. The software crisis (and hardware as well)

CS370 Operating Systems

CS477 Formal Software Development Methods / 32

Formal Specification and Verification

SWEN-220 Mathematical Models of Software. Process Synchronization Critical Section & Semaphores

Synchronization. Race Condition. The Critical-Section Problem Solution. The Synchronization Problem. Typical Process P i. Peterson s Solution

Module 6: Process Synchronization. Operating System Concepts with Java 8 th Edition

Silberschatz and Galvin Chapter 6

Process Synchronization

CS370: System Architecture & Software [Fall 2014] Dept. Of Computer Science, Colorado State University

Lecture 3 SPIN and Promela

What is SPIN(Simple Promela Interpreter) Elements of Promela. Material About SPIN. Basic Variables and Types. Typical Structure of Promela Model

Semaphore. Originally called P() and V() wait (S) { while S <= 0 ; // no-op S--; } signal (S) { S++; }

Promela and SPIN. Mads Dam Dept. Microelectronics and Information Technology Royal Institute of Technology, KTH. Promela and SPIN

Lesson 6: Process Synchronization

Chapter 6: Process Synchronization. Module 6: Process Synchronization

CSE 4/521 Introduction to Operating Systems

Chapter 5: Process Synchronization. Operating System Concepts 9 th Edition

A Practical approach on Model checking with Modex and Spin

Concurrency. Chapter 5

Recall from Chapter 1 that a data race is a failure to correctly implement critical sections for non-atomic shared variable accesses.

Midterm Exam #2 Solutions October 25, 2016 CS162 Operating Systems

Chapter 6: Process Synchronization

Testing Concurrent Programs: Model Checking SPIN. Bengt Jonsson

Programming Languages

Operating Systems: William Stallings. Starvation. Patricia Roy Manatee Community College, Venice, FL 2008, Prentice Hall

What is SPIN(Simple Promela Interpreter) Material About SPIN. Elements of Promela. Basic Variables and Types. Typical Structure of Promela Model

Process Management And Synchronization

Process Synchronization

Process Synchronization

Concurrency: Principles of Deadlock. Processes and resources. Concurrency and deadlocks. Operating Systems Fall Processes need resources to run

CHAPTER 6: PROCESS SYNCHRONIZATION

CS 162 Operating Systems and Systems Programming Professor: Anthony D. Joseph Spring Lecture 8: Semaphores, Monitors, & Condition Variables

CS370 Operating Systems

Deadlock and Monitors. CS439: Principles of Computer Systems February 7, 2018

THREADS & CONCURRENCY

Thread Synchronization: Too Much Milk

Introduction to programming with semaphores Fri 1 Sep 2017

More on Synchronization and Deadlock

1. Motivation (Race Condition)

Roadmap. Tevfik Koşar. CSE 421/521 - Operating Systems Fall Lecture - X Deadlocks - I. University at Buffalo. Synchronization structures

Roadmap. Problems with Semaphores. Semaphores. Monitors. Monitor - Example. Tevfik Koşar. CSE 421/521 - Operating Systems Fall 2012

Deadlocks. Copyright : University of Illinois CS 241 Staff 1

Chapter 6: Process Synchronization

EMBEDDED C CODE 17. SPIN Version 4 supports the inclusion of embedded C code into PROMELA models through the following fiv e new primitives:

The Dining Philosophers with Pthreads

Safety and liveness for critical sections

Dining Philosophers, Semaphores

Semaphores. A semaphore is an object that consists of a. methods (e.g., functions): signal and wait. semaphore. method signal. counter.

Concurrency: Deadlock and Starvation. Chapter 6

Multi-Threaded System int x, y, r; int *p, *q, *z; int **a; EEC 421/521: Software Engineering. Thread Interleaving SPIN. Model Checking using SPIN

Chapter 5: Process Synchronization

Chapter 5: Process Synchronization. Operating System Concepts 9 th Edition

CS370 Operating Systems

CS477 Formal Software Development Methods / 39

Resource Allocation. Pradipta De

Automated Reasoning. Model Checking with SPIN (II)

System Correctness. EEC 421/521: Software Engineering. System Correctness. The Problem at Hand. A system is correct when it meets its requirements

Transcription:

Using Spin to Help Teach Concurrent Programming John Regehr May 1, 1998 1 Introduction and Motivation Writing correct concurrent programs is very difficult; race conditions, deadlocks, and livelocks can be hard to discover by inspection and may lurk undetected for long periods of time. The following observation was made about errors that occur in communication protocols; it applies equally well to errors in nondistributed concurrent programs: Such errors, while rare, do occur, and their rareness will make it extremely difficult to catch the flaw in the system. This inadequate scheme will work almost all of the time. This document accepts as a given that the goal of an undergraduate operating systems course is to teach the principles of concurrent programming, as well as the design and implementation of real operating systems. I will argue that the former is very difficult to teach well, and that it can be taught better with the help of a system that can be used to model and prove properties about concurrent programs. 2 What s Wrong with Nachos and Minix Nachos [1], Minix [5], and XINU [2] are operating systems designed to teach operating systems. The premise is that a simplified system will contain the essential structure, without bogging students down with the irrelevant details that pervade a real OS. This logic is sound with respect to teaching operating systems design and implementation; however, it does not carry over to teaching concurrent programming, which the student is usually expected to pick up along the with rest. For example, we find the following quote in [1]: When we first used Nachos, we omitted many of the practice problems we now include, thinking that students would see enough concurrency in the rest of the project. In retrospect, the result was that many students were still making concurrency errors even in the final phase of the project. 1

3 MODELING CONCURRENT SYSTEMS: SPIN 2 The usual procedure is for students to write a number of concurrent programs, test them, and turn them in for grading by inspection or further testing. I believe that this sends a very bad message: that a few (or even many) apparently-correct runs and the inspection of a competent programmer mean that a program is correct. This is simply not true in concurrent programming, an area so difficult that many incorrect results have been published and gone undetected for some time. One way to avoid misleading students about the difficulty of the task is to have them prove that their programs are correct this is the approach advocated by Dijkstra in [3]. To avoid the pain of proving properties of programs by hand, we use a system called Spin, which is designed to prove properties about models of concurrent programs. 3 Modeling Concurrent Systems: Spin Spin [4] allows us create a model of a concurrent system which is simple enough to prove things about, but detailed enough to map easily to a real system. Spin was designed for communication protocol analysis, but works just as well for processes communicating through shared memory. It performs an exhaustive search of the state-space of a group of processes. Although this is PSPACE hard and does not work for large models, the programs under consideration here are well within its reach. The exhaustive-search approach has the useful property that it will either prove some property about the entire state-space, or come up with an execution that violates the property a counterexample. Spin models are written in Promela, a simple C-like language. It has three language constructs specifically designed for validation of programs: an assert statement that can be used to express invariants, three kinds of labels useful for proving deadlock and livelock freedom, and general temporal logic claims. For purposes of this paper we are only interested in the first two, since they allow us to prove all of the properties that we are interested in. Because Spin makes races, deadlocks, and livelocks easily detectable it allows us to completely avoid the unfortunate (but common) situation in which a student implements a solution to (for example) the dining philosophers problem, runs it successfully a few times, and after handing it in is unpleasantly surprised to be graded off for having an incorrect program. Even worse, the grader may fail to notice the error, allowing the student to escape with the feeling that he or she knows how to write a correct concurrent program. This can easily happen since some students inevitably implement convoluted solutions with more semaphores than necessary; these defy analysis and the only solution is to take off points or ask for a resubmission. Spin indirectly deters overly complicated solutions by being unable to prove that they are correct (or incorrect).

4 LESSON PLAN 3 4 Lesson Plan I have created two homework assignments using Spin; presumably they would be used in combination with Nachos or Minix assignments. Students should be introduced to Spin with one or two lectures; there is a large amount of Spin documentation in Holzmann s book [4] and at the Spin web site: http://plan9.bell-labs.com/netlib/spin/whatispin.html 5 Assignment 1: Mutual Exclusion The purpose of this assignment is to introduce you to Spin, and to programming in Promela. You will need access to a machine with Spin and XSpin version 3.2.0 or newer installed. Included in the Spin package is some documentation in HTML; to do this assignment you will want to refer to this and to ch. 5 of Holzmann s book (available in postscript from the Spin homepage). 5.1 A Buggy Mutual Exclusion Algorithm The following program creates two processes, each of which repeatedly executes the critical section. If both processes can be in the critical section at the same time, the assertion will fail. byte in; bool lock; active [2] proctype user() L1: /* wait for lock to be free */ (lock == 0); /* grab the lock */ lock = 1; /* critical section */ in++; assert (in == 1); in--; /* release the lock */ lock = 0; goto L1;

5 ASSIGNMENT 1: MUTUAL EXCLUSION 4 The program clearly contains a race condition, since both processes may see that the lock is free and enter the critical section simultaneously. Verify this using XSpin: under the Run menu, select Set verification parameters. Make sure that Spin is checking for assertion violations, and then do a verification run. Spin will offer to run a guided simulation showing an execution that violates an assertion. Run this simulation; make sure you understand it, and include a copy of its output in your homework. 5.2 Fixing the Problem One way to perform actions atomically in Spin is to simply tell Spin to do so. Modify the program from the last part by putting the code that tests and grabs the lock inside an atomic... block. Run a verification on the modified program to prove that the assertion can never be violated. Include a copy of the modified program and the verification output with your assignment. Note that using atomic, we could eliminate the lock variable and just atomically execute the critical section itself. 5.3 Another Buggy Mutual Exclusion Algorithm Although the atomic construct provides a convenient way to execute a group of operations atomically, real computers have no such primitive. Therefore, there are many algorithms designed to implement mutual exclusion using only basic atomic operations such as memory references. Try to find a situation in which the following algorithm does not guarantee mutual exclusion: byte in; byte x, y, z; active [2] proctype user() byte me = _pid+1; /* me is 1 or 2 */ L1: x = me; L2: if :: (y!= 0 && y!= me) -> goto L1 /* try again */ :: (y == 0 y == me) fi; L3: z = me; L4: if :: (x!= me) -> goto L1 /* try again */ :: (x == me) fi; L5: y = me;

5 ASSIGNMENT 1: MUTUAL EXCLUSION 5 L6: if :: (z!= me) -> goto L1 /* try again */ :: (z == me) fi; L7: /* success */ /* critical section */ in = in+1; assert(in == 1); in = in - 1; goto L1 Now, use Spin to find a counterexample, and include it in what you turn in. Also explain in your own words what went wrong. 5.4 Another Real Algorithm Implement the following 2-process mutual exclusion algorithm (from p. 143 of Silberschatz and Galvin) in Promela without using atomic. i is the id of the running process, and is either 0 or 1. bool flag[2]; bool turn; while (TRUE) flag[i] = TRUE; turn = 1-i; while (flag[1-i] && turn == i-1) /* critical section */ flag[i] = FALSE; Prove that it enforces mutual exclusion; include a copy of your implementation along with Spin output verifying it in what you turn in. Hand in a report with the following clearly labelled items: 1. guided simulation output showing an assertion violation in the first broken algorithm 2. your fixed program 3. verification output showing that the fixed program is correct

6 ASSIGNMENT 2: DINING PHILOSOPHERS 6 4. a counterexample to the second broken mutual exclusion algorithm, and an explanation of what went wrong 5. a Promela implementation of the mutual exclusion algorithm from Silberschatz and Galvin p. 143, and Spin output demonstrating that mutual exclusion is guaranteed 6 Assignment 2: Dining Philosophers In the last assignment, we assumed that the algorithms were correct if they did not violate mutual exclusion. In general, however, this is not enough: a correct concurrent algorithm must also be provably free of deadlocks and livelocks. Spin assumes that a program has deadlocked if there are no runnable processes. To detect livelock, we can mark statements which, when executed, indicate that the program is making progress. Spin can be used to prove that a program must make progress infinitely often, and is therefore free of nonprogress cycles. See Ch. 6 of the Holzmann book for more details. 6.1 A Simple Solution The following Promela program models a solution to the dining philosophers problem. Note that the solution is almost trivially correct since we pick up both chopsticks and begin eating atomically. Also note the technique for verifying that the problem is solved correctly: we start a monitor process for each philosopher that asserts that either it isn t eating, or it is eating, possesses both chopsticks, and neither neighbor is eating. Because the monitors can run at any time, they cause Spin to prove that they are always true. The progress: labels tell Spin that we consider the program to be making progress when philosophers are eating. We use a different label for each philosopher to prove a weak kind of fairness: every philosopher must eat infinitely often (a single progress: label would only ensure that some philosopher must eat infinitely often). #define NUM 5 byte chopstick[num]; byte eating[num]; active [NUM] proctype philo () L1: /* grab both chopsticks atomically */ atomic (chopstick[_pid] == 0 && chopstick[(_pid+1)%num] == 0); chopstick[_pid] = 1; chopstick[(_pid+1)%num] = 1;

6 ASSIGNMENT 2: DINING PHILOSOPHERS 7 eating[_pid] = 1; printf ("%d eating\n", _pid); /* eating */ if :: (_pid == 0) -> progress0: skip; :: (_pid == 1) -> progress1: skip; :: (_pid == 2) -> progress2: skip; :: (_pid == 3) -> progress3: skip; :: (_pid == 4) -> progress4: skip; fi; /* it s not really necessary to release them atomically */ atomic eating[_pid] = 0; chopstick[_pid] = 0; chopstick[(_pid+1)%num] = 0; goto L1; active [NUM] proctype monitor () assert ((!eating[_pid-num]) (eating[_pid-num] && chopstick[_pid-num] && chopstick[(_pid-num+1)%num] &&!eating[(_pid-num+1)%num] &&!eating[(_pid-num-1+num)%num]) ); Use Spin to verify that this program violates no assertions, and that it contains no deadlocks or non-progress cycles. 6.2 Better Solutions On p. 160, Silberschatz and Galvin suggest three solutions to the dining philosopher problem. The program in part 1 uses a critical section to pick up both chopsticks atomically. Modify the program to model the other two solutions; that is, (1) to allow at most 4 philosophers at the table at a time, and (2) to

7 CONCLUSIONS 8 have even- and odd-numbered philosophers pick up the chopsticks in different orders. Do not modify the monitor processes; use them, with Spin, to prove that your solutions are correct, and that they contain no deadlocks or non-progress cycles; include the Spin output in what you hand in. WARNING: The more complicated your solutions are, the less likely they are to work, and the longer Spin will take to verify them. If you think your solutions are as simple as you can make them and Spin still runs out of memory or fails to terminate, you may reduce the number of philosophers to 4 (indicate on your output that you have done so). Hand in a report with the following clearly labelled items: 1. two dining philosopher solutions, commented to the point that it is obvious what is going on 2. Spin output showing that your solutions are correct (that they do not violate any assertions, and contains no deadlocks or livelocks); you should run two verifications on each of the two programs 7 Conclusions Using Spin to teach concurrent programming has some obvious benefits: students need not worry about handing in an incorrect program since Spin will tell them if it works or not. When programs don t work, Spin will give a counterexample that the student can examine. Spin encourages experimentation because it is fast and easy to run. Finally, since they are forced to formally specify the correctness requirements for their programs, students will presumably gain a better understanding of what it means to write a correct concurrent program. A Solutions to Assignment 1 Guided simulation output showing an assertion violation: preparing trail, please wait...done 1: proc 1 (user) line 9 "pan_in" (state 1) [((lock==0))] 2: proc 0 (user) line 9 "pan_in" (state 1) [((lock==0))] 3: proc 1 (user) line 12 "pan_in" (state 2) [lock = 1] 4: proc 1 (user) line 15 "pan_in" (state 3) [in = (in+1)] 5: proc 0 (user) line 12 "pan_in" (state 2) [lock = 1] 6: proc 0 (user) line 15 "pan_in" (state 3) [in = (in+1)] Spin: line 16 "pan_in", Error: assertion violated #processes: 2 7: proc 1 (user) line 16 "pan_in" (state 4) 7: proc 0 (user) line 16 "pan_in" (state 4) 2 processes created

A SOLUTIONS TO ASSIGNMENT 1 9 Exit-Status 0 The fixed program: byte in; bool lock; active [2] proctype user() L1: /* atomically, wait for lock to be free and grab it */ atomic (lock == 0); lock = 1; /* critical section */ in++; assert (in == 1); in--; /* release the lock */ lock = 0; goto L1; Results of the successful verification run: (Spin Version 3.2.0 -- 8 April 1998) + Partial Order Reduction Full statespace search for: never-claim - (not selected) assertion violations + cycle checks - (disabled by -DSAFETY) invalid endstates + State-vector 16 byte, depth reached 5, errors: 0 9 states, stored 2 states, matched 11 transitions (= stored+matched) 2 atomic steps hash conflicts: 1 (resolved) (max size 2^19 states) 2.542 memory usage (Mbyte)

A SOLUTIONS TO ASSIGNMENT 1 10 Counterexample: preparing trail, please wait...done 1: proc 1 (user) line 7 "pan_in" (state 1) [x = me] 2: proc 1 (user) line 10 "pan_in" (state 4) [(((y==0) (y==me)))] 3: proc 1 (user) line 12 "pan_in" (state 7) [z = me] 4: proc 1 (user) line 15 "pan_in" (state 10) [((x==me))] 5: proc 0 (user) line 7 "pan_in" (state 1) [x = me] 6: proc 0 (user) line 10 "pan_in" (state 4) [(((y==0) (y==me)))] 7: proc 1 (user) line 17 "pan_in" (state 13) [y = me] 8: proc 1 (user) line 20 "pan_in" (state 16) [((z==me))] 9: proc 1 (user) line 25 "pan_in" (state 19) [in = (in+1)] 10: proc 0 (user) line 12 "pan_in" (state 7) [z = me] 11: proc 0 (user) line 15 "pan_in" (state 10) [((x==me))] 12: proc 0 (user) line 17 "pan_in" (state 13) [y = me] 13: proc 0 (user) line 20 "pan_in" (state 16) [((z==me))] 14: proc 0 (user) line 25 "pan_in" (state 19) [in = (in+1)] Spin: line 26 "pan_in", Error: assertion violated #processes: 2 15: proc 1 (user) line 26 "pan_in" (state 20) 15: proc 0 (user) line 26 "pan_in" (state 20) 2 processes created Exit-Status 0 The mutex algorithm from S&G p. 143: #define TRUE 1 #define FALSE 0 bool flag[2]; bool turn; int in; active [2] proctype user () L1: flag[_pid] = TRUE; turn = 1-_pid; (flag[1-_pid] == FALSE turn!= 1-_pid); /* critical section */ in++; assert (in == 1); in--;

B SOLUTIONS TO ASSIGNMENT 2 11 flag[_pid] = FALSE; goto L1; Spin output: Spin Version 3.2.0 -- 8 April 1998) + Partial Order Reduction Full statespace search for: never-claim - (not selected) assertion violations + cycle checks - (disabled by -DSAFETY) invalid endstates + State-vector 24 byte, depth reached 23, errors: 0 38 states, stored 27 states, matched 65 transitions (= stored+matched) 0 atomic steps hash conflicts: 7 (resolved) (max size 2^19 states) 2.542 memory usage (Mbyte) B Solutions to Assignment 2 Spin output omitted, but these programs were proven to be correct. This program only allows N 1 philosophers at the table at a time: #define NUM 5 bool chopstick[num]; bool eating[num]; byte at_table; active [NUM] proctype philo () L1: /* we can only sit down if the number of sitting philosophers will be NUM-1 or less */ atomic (at_table < (NUM-1)); at_table++;

B SOLUTIONS TO ASSIGNMENT 2 12 printf ("%d is at the table\n", _pid); atomic (chopstick[_pid] == 0); chopstick[_pid] = 1; atomic (chopstick[(_pid+1)%num] == 0); chopstick[(_pid+1)%num] = 1; eating[_pid] = 1; printf ("%d eating\n", _pid); /* eating */ if :: (_pid == 0) -> progress0: skip; :: (_pid == 1) -> progress1: skip; :: (_pid == 2) -> progress2: skip; :: (_pid == 3) -> progress3: skip; :: (_pid == 4) -> progress4: skip; fi; eating[_pid] = 0; chopstick[_pid] = 0; chopstick[(_pid+1)%num] = 0; at_table--; printf ("%d left the table\n", _pid); goto L1; active [NUM] proctype monitor () assert ((!eating[_pid-num]) (eating[_pid-num] && chopstick[_pid-num] && chopstick[(_pid-num+1)%num] &&!eating[(_pid-num+1)%num] &&!eating[(_pid-num-1+num)%num]) );

B SOLUTIONS TO ASSIGNMENT 2 13 This program has even-numbered philosophers pick up the left chopstick and then the right, and vice-versa for odd-numbered philosophers. #define NUM 4 bool chopstick[num]; bool eating[num]; active [NUM] proctype philo () L1: /* even philosophers pick up left, then right odd philosophers pick up right, then left */ if :: (_pid%2 == 0) -> atomic (chopstick[_pid] == 0); chopstick[_pid] = 1; atomic (chopstick[(_pid+1)%num] == 0); chopstick[(_pid+1)%num] = 1; :: (_pid%2 == 1) -> atomic (chopstick[(_pid+1)%num] == 0); chopstick[(_pid+1)%num] = 1; atomic (chopstick[_pid] == 0); chopstick[_pid] = 1; fi; eating[_pid] = 1; printf ("%d eating\n", _pid); /* eating */ if :: (_pid == 0) -> progress0: skip; :: (_pid == 1) -> progress1: skip; :: (_pid == 2) -> progress2: skip; :: (_pid == 3) -> progress3: skip;

REFERENCES 14 :: (_pid == 4) -> progress4: skip; fi; eating[_pid] = 0; chopstick[_pid] = 0; chopstick[(_pid+1)%num] = 0; goto L1; active [NUM] proctype monitor () assert ((!eating[_pid-num]) (eating[_pid-num] && chopstick[_pid-num] && chopstick[(_pid-num+1)%num] &&!eating[(_pid-num+1)%num] &&!eating[(_pid-num-1+num)%num]) ); References [1] Wayne A. Christopher, Steven J. Procter, and Thomas E. Anderson. The Nachos Instructional Operating System. Computer Science Division, University of California at Berkeley. [2] Douglas E. Comer. Operating Systems Design: The XINU Approach. Prentice Hall, 1984. [3] Edsger W. Dijkstra. On the cruelty of really teaching computing science. Communications of the ACM, 32(12), December 1989. [4] Gerard J. Holzmann. Design and Validation of Computer Protocols. Prentice Hall, 1991. [5] Andrew S. Tannenbaum. Operating Systems: Design and Implementation. Prentice Hall, 1987.