You are on page 1of 105

Chapter 1 Getting Started

Introduction
Welcome to COIT13152, Operating Systems. This chapter has three main aims To make you familiar with how COIT13152 will operate, To revise some pre-requisite knowledge, and To give an overview of the rest of the course. There is a significant amount of reading this week. There are a number of reasons why you should not let the amount of reading stress you out or get you down. First off, it doesn't help at all. So, in the words of Douglas Adams, "Don't Panic". Second, much of the reading this week is a revision of material you will have seen in other courses, particularly courses which introduce how the hardware of a computer works. Third, the remainder of the reading is generally an overview of the material we are going to be covering this semester. So, don't try to memorise all of the material you read this week. Instead, aim to gain a basic understanding of what an operating system is and what it does. Also you should make sure you have a good understanding of how the hardware in a basic computer operates. Lastly it is important that you are familiar with the study resources available for you in COIT13152 (web site, CDROM, online lectures, online animations etc,) and also what you need to do to pass COIT13152.

Objectives
On completion of this chapter you will : be aware of the requirements, resources, assessment and schedule for COIT13152, Operating Systems have an idea about what an operating system is, what it does and why it is important for a computing professional to know about them have an understanding of the history and development of operating systems know what the primary goals of an operating system are have revised material about the hardware of a computer

have gained an overview of the components and possible structure of an operating system.

Resources
To complete the first week of work you will need: Text book chapters 1, 2 and 3 Study guide chapter 1 Course Profile for COIT13152 online lectures 1, 2 and 3 (on the COIT13152 Web site and CD-ROM) an Internet connection and an email address

Why Learn About Operating Systems


Lets start by describing what COIT13152 wont do. COIT13152 will not show you how to use Windows 98/NT, UNIX or any other operating system. COIT13152 will show you: how an OS (operating system) works the algorithms and data structures that make up an OS the problems, solutions and trade offs in designing an OS how an operating system influences you as a computing professional The aim of COIT13152 is for you to ACHIEVE AN UNDERSTANDING OF HOW AN OPERATING SYSTEM WORKS. So why would you want to learn about that? What possible good will it do you as a computing professional to know the details of how virtual memory works or process scheduling? An operating system is an essential part of a computer. Without an operating system the computer won't work. If the operating system is unreliable or inefficient the computer will be unreliable and inefficient and more importantly the people who use the computer will not be able to perform the tasks they need to. This is important because the main task of most computing professionals is to help people use computers to complete tasks as easily as possible. Either by writing programs (software engineering) which enable people to complete their tasks or by maintaining the systems (systems administration) which run these programs. Knowledge of how operating systems work will help you : build software that is efficient and correct Knowledge of how operating systems work can improve

the efficiency of your program. decide which operating system you should purchase for a client There is a wide choice of operating systems each of which is suitable for different purposes. Advertisers and company sales people lie. As a computing professional you should be able understand what they are saying and identify the lies. figure out how to fix a computer that is behaving badly. Picture it, your manager complains that his computer is not very fast. What do you do? The obvious choice, buy a faster CPU may not always make any difference. Buying more RAM might be a better solution. Why? Knowledge of operating systems and virtual memory would help with the answer.

The Course Profile


Before going any further please make sure you have read through the COIT13152 course profile. It contains additional information about this course.
Reading 1.1 Course Profile for COIT13152

Assessment and the Web Site


While the course profile for COIT13152 describes the assessment for COIT13152 it does not include copies of actual assignments you must complete. To obtain copies of the assignments and all the latest information about COIT13152 you need to refer to the COIT13152 website.
Reading 1.2 Home page on web site for COIT13152

Using the Mailing Lists


The main COIT13152 mailing list will be used as a forum for students to ask questions and for announcements from teaching staff about COIT13152. It is important that you subscribe to this list and check email regularly. More importantly if you have general problems with the material in COIT13152 use the mailing list to ask questions. This is probably as good a time as any for you to subscribe to the COIT13152 mailing list. Instructions for doing this are on the web site.

A Gentle Introduction to Operating

Systems
That's enough preparation, let's get stuck into operating systems. The first step taken by the textbook is to provide you with some idea of what an operating system is and what it is supposed to do.
Reading 1.3 Textbook, Chapter 1, Introduction and Section 1.1

It's always difficult to teach operating systems. Operating systems are complex interacting systems that include a number of different systems. Each of these systems influences the other. This makes it difficult to decide how to introduce operating systems. For example, when you introduce process scheduling it is useful to introduce memory management. However, both topics are too complex to introduce together. To add to the complexity of what you will encounter in this subject are two common drawbacks of the operating systems field 1 Same word different meaning or different word same meaning It is common for different operating systems textbooks and operating systems makers to use the same word but mean totally different things. The opposite to this is when they use two different words to mean the same thing. Learning to now the difference and recognise it can be frustrating. 2 It depends In COIT13152 you will be introduced to a number of algorithms or explanations on how certain things work. They don't always work this way. One of the reasons for this is that in explaining these tasks they must be simpilified, otherwise they would be too difficult to learn. Another reason is that in operating systems there is no one right way to do something. There are lots of alternatives which each have the benefits and drawbacks.

What an operating system does


While finding it hard to define what an operating system does the reading offers a number of suggestions including that the operating system is like government. This analogy includes the following two major components: resource manager The operating system manages the resources of the computer system and helps share these resources amongst the objects using the computer. control program The operating system controls access to and operation of some parts of the computer system to ensure reliability and correctness.

What are the operating system's aims


The reading also suggests two primary aims for an operating system convenience for the user Increasingly the aim with computers is to provide ease-ofuse and simplicity for the people using computers to achieve some task. In today's environment this is perhaps the most important goal. efficiency A few years ago (not as long as you might think) the primary aim of most operating systems was not user convenience. Instead, because of the expensive nature of computer hardware, it was efficiency. To get the most bang for the bucks spent on the computer. While no longer a primary aim of an operating system, efficiency is something that must still be considered.

How things change


The point about the change the focus of computing is an important one. Not only just in the study of operating systems but also for computing professionals in general. Technology is no longer the most costly and important consideration. Computer hardware is much cheaper today than before. It is people and their time which is much more expensive. When hardware was expensive and the time of people was cheap computing people and operating systems had to be smart about how to get the most out of the hardware. Now that people are expensive and hardware is cheap computing people and operating systems have to be smart about how to get the most out of people.

Evolution
There are a number of additional goals that people can attribute to an operating system. A common goal today is evolvability, the ability to evolve or adapt to changes in computers. Operating systems are a very complex collection of programs and require a considerable amount of time and energy to create. By their nature operating systems must know about and interact closely with computer hardware. However, computer hardware changes very quickly. These changes mean that an operating system may also have to change and in some cases be completely rewritten. A good operating system can grow to meet these changes with a minimum of effort.

History
Operating systems such as Windows 98/NT and Linux didn't spring straight from the fevered imaginations of modern operating

system designers. Instead they are the culmination of over 50 years experience with computers and operating systems. Actually much of the underlying concepts and theories behind most modern operating systems were first developed in the 1960s. That is how little things have really changed. To gain an understanding of what operating systems are and what they do it is helpful to it know how operating systems evolved into what they are today. You are not expected to memorise the following and be able to quote it back verbatim.
Reading 1.4 Textbook, Chapter 1, Section 1.2 and 1.3

Many of the observations which led to the development of batched and multi-programmed batched system still exist today and are driving existing work. Some of these observations include: the CPU is much faster than input/output (I/O) devices it is more efficient if you can overlap the I/O of one job with the CPU utilisation of another. An important trend in computing is that as computers become cheaper and more powerful you can cater more to the needs of human beings. This is part of what lead to the development of time-sharing and personal computers. A few years ago many of the concepts introduced in COIT13152, Operating Systems, such as virtual memory or multiprogramming, could only be seen on large computers. The personal computers used by students were too small and the operating systems on these computers too primitive to use many of the ideas. This is no longer the case. Windows 98/NT, the most common operating system used by students, include almost all the features discussed in COIT13152. This change means that it is now more important that future computing professionals be familiar with the concepts introduced in COIT13152. As you go through COIT13152 you will see a number of concepts being repeated. One you have seen in readings so far is efficiency versus features. Remember, one of the aims of an operating system is to make efficient use of the resources available in a computer. The limits of the hardware and the desire for efficiency limits the features which are available. As those limits increase (computers get more powerful) new features can be added. Which is why most modern computers provide graphical user interfaces (GUIs).

Alternative Types of Systems


Up until most of your computing experience is with PCs. It is important that you come to a realisation that there is much

more to computing the personal computers. Many of you may end up programming services intended for use by mobile phones and other very non-PC like computers. The following reading introduces you to some of the different types of operating systems that are available.
Reading 1.5 Textbook, Chapter 1, Sections 1.4 through 1.10

Time and technology is again starting to catch up with the material we cover in COIT13152. A few years ago parallel, distributed and real-time systems were so advanced that most students could never see the importance of them. These days you can see them everywhere. parallel systems You can buy computers from Gateway and Dell which have multiple CPUs (central processing units). Multiple CPU computers are now widely used as disk and print servers. distributed systems Electronic commerce (e-commerce) and the growth of the Internet is increasing research and use of distributed systems. In the not too distant future most of you will have Internet agents who wonder around the Internet performing tasks for you, if you haven't already. A fairly well-known computer scientist was overheard saying, "The Web is just another type of distributed system". real-time systems Anyone driving a 1999 model Falcon (or Commodore) has had an interaction with real-time operating systems.

Email Question
The following is an email question asked by a student in a previous offering of COIT13152.
> Would it be a correct to say that MSDOS uses > multiprogramming but not multitasking and that > Unix uses both? :) One of the "joys" of operating systems is that there are so many levels. This of course means there aren't always that many simple questions. The simple answer to your question is: No it would not be correct to say this. The following attempts to explain why it isn't. Also it tries to explain in a little more detail the distinction between

"single"-programming multi-programming

multi-tasking
I doubt that many people have really grasped the difference as explained by the COIT13152 text. I'll start off by trying to explain these terms using operating systems most of you will be familiar with. Single programming Multi-programming Multi-tasking Windows Program Execution When a program is running (we are going to refer to such an entity as a process, well most of the time anyway) it "owns" the CPU. For a program to be actually running (excuting) the CPU is performing instructions which "belong" to that program. The basic instruction/execution cycle is demonstrated by one of the animations available from the COIT13152 website. When does the process do during I/O? Your programs generally want to do one of two things -> -> -> MS-DOS Windows 3.1 (sort of) UNIX, Windows NT, 95/98

execute on the CPU perform input/output (I/O)


Whenever the current process (remember this is the term we're using for a program that is being executed) performs some I/O operation (e.g. wants to write/read to disk, use the printer, display something on the graphics card etc) it stops using the CPU for quite sometime. The reason for this is that most I/O devices are incredibly slow in comparison to the CPU. To give you some idea here is an analogy we have used in the Rocky tutes. The CPU and RAM operates at nano-second speeds. Most common hard-drives operate at millisecond speeds. Most of you probably don't get just how much slower hard-drives are than RAM. To help you understand this lets change the times into something most people are more familiar with. Let's say that the CPU/RAM are as fast as Carl Lewis (a famous American sprinter who won Olympic Gold Medals). This means that they can run the 100 metres in around 10 seconds. So, if we keep the relative speeds, how fast can the hard-drive run the 100 meters? If you do the math you will find that it takes the hard-drive around 115 days to run the 100 meters. This should give you some idea of how

large the gap between RAM and the HDD is. This is a problem. You really don't want the CPU sitting around doing nothing for 115 days while it waits for some information to be read/written to disk. This is very wasteful. Single programming is bad This is exactly what happens in a single programming operating system. Single programming means that there is only ever one program running. This ends up with a lot of wasted CPU time and means that it can take longer to run programs. This is exactly what happens in DOS. DOS, as an operating system, does not provide any support for running more than one program. There are a couple of fiddles you can do which allow a DOS computer to run more than one program but these features are not provided by the operating system. In fact, you can only do this because DOS isn't really an operating system (at least by our definition). DOS does not prevent programs from directly accessing hardware. It is this ability to directly access hardware which allows these "workarounds" to run more than one program. In the end, we'll say that DOS is a singleprogramming operating system. It only supports/allows one program to be running at one time. Multi-programming Solving this inefficency (having the CPU do nothing for 115 days) was the reason for the development of multi-programming. Rather than have the CPU sit around doing nothing while some I/O was being done. A multiprogramming operating system will keep a pool of processes. When the current running process asks to do some I/O it "gives up" the CPU. It is replaced on the CPU with another process. Then when the new running process asks to do some I/O (or some other task which means it doesn't need the CPU) the multiprogramming operating system chooses another process to go onto the CPU. The important point to note here is that a new process is only placed onto the process when the current process "gives up" the CPU. This is a bit like what happens under Windows 3.1. A problem with multi-programming There is a problem with multi-programming. If I

include the following code in my process while ( 1 ) {} what happens? Well this is an endless loop. In a multiprogramming operating system the current running process is only replaced on the CPU when it "gives up" the CPU by doing I/O (or other tasks). Once the endless loop gets going that is never going to happen. My process will hog the CPU and prevent anyone else having a ago. Multi-tasking: the solution Multi-tasking solves this problem. In a multi-programming operating system the current process decides when it will give up the CPU. In a multi-tasking operating system the operating system decides when the current process will give up the CPU. A multi-tasking operating system makes use of the system timer. A clock which generates an interrupt every X time units. When the timer interrupt occurs the operating system wakes up and asks a simple question "Has the current process had enough time on the CPU?" If the answer is yes, then the operating system removes the process from the CPU and replaces it with another on. This replacement happens many times per second. Via this model it is a bit more difficult for the CPU to be hogged.

Another Email Question


> What is the meaning in pg 15 of the text line 1 > multiprocessors can also save money compared to > multiple single systems. What is the meaning of > multiple single systems? I thought multiprocessors > is multiple single systems with many main CPU! The definition of a single system they are referring to here is of a single computer: including CPU, RAM, I/O devices, keyboard, monitor, hard drive etc. Your standard PC is an example of a single system. But it is a single system with on CPU A multi-processor machine is a single system that has many CPUs.

When it refers to multiple single systems it means many single systems, like your PC, all joined together using some sort of network which are working together on the same problem. An example of this is the Beowulf clusters which are being worked on. Check out http://www.beowulf.org/ The idea is similar to the NOW (Network Of Workstation) idea. http://now.cs.berkeley.edu/ Both projects take standard, "off-the-shelf" computers and join them together with special networks. They can be cheaper than a multiprocessor system because they are using commodity equipment. The sheer number of PCs which are sold make them very cheap, especially when compared to large multiprocessor systems which might sell hundreds if they are lucky.

Summary of the Introduction


Reading 1.6 Textbook, Chapter 1, Section 1.11

Exercises
Textbook, Chapter 1, Exercises 1, 2, 4, 5, 8, 11

This brings an end to the section of this study guide chapter that looks at chapter 1 of the textbook. You might find this to be a good place to take a break from study (if you haven't already).

How a computer works


The operating system provides a link between the software you write and use and the hardware of the computer system. This means that the operating system must work closely with, and is influenced by computer hardware. To fully understand how an operating system works it is important that you are familiar with the operation of computer hardware.

But it is revision
Many of you may already have been introduced to how a computer operates - even if you have, please take the time to read through the following readings. Before you can fully understand what an operating system works it is important that you understand how the hardware works. Why is important? Well, the theory is (taken from years of research into teaching and learning by experts) is that you need to

construct a mental model that helps explain how a concept works. If your mental model is faulty then you will find it difficult to understand how a concept works. Since operating systems and hardware are tightly related if you don't understand how computer hardware works you won't be able to fully understand how an operating system works. So what does that mean for me? Don't try to memorise the facts included in the following material. Instead, try to picture in your mind how hardware works. A good test of how well you have grasped this material is to attempt to explain to someone else (who doesn't know) how a computer works.
Reading 1.7 Textbook, Chapter 2, Introduction and Section 2.1

Instruction/Execution Cycle
This is probably as good a place as any to revise the instruction execution cycle. This is the cycle which the CPU of a computer repeats many times a second. In its simplest from the instruction execution cycle includes the following steps fetch the instruction In this step the CPU copies the content of the RAM location at the address contained within the program counter (the PC) into the instruction register (IR). Both the PC and IR are registers on the CPU. execute the instruction In this step the CPU evaluates the instruction now in the IR and performs the appropriate task. increment the PC This isn't really a full-fledged step of the instruction execution cycle, however it is still important. The PC is increment so that it points to the location of the next instruction to execute. check interrupts This is where the hardware checks to see if any interrupts have occurred. This usually entails checking whether or not a particular bit has been set on the CPU. If the bit is set the computer jumps to a particular interrupt handling routine. Generally speaking the hardware provides a small part of the interrupt handling routine with the majority of it provided by the operating system. An animation of the instruction execution process is available on the COIT13152 Web site. Its available under online lecture 2 or via the animations page. The instruction execution cycle discussed here is a very simplified version of the instruction execution cycle. The IE cycle for modern computers is actually much more complex and is designed

to increase the performance of the system. Interrupts generally fall into one of four categories: software Software interrupts are caused by system calls and "traplike" instructions. System calls are how user programs ask the operating system to do some tasks for it. Some examples of those tasks include reading/writing to a disk or getting some memory. hardware Hardware interrupts are caused by I/O devices, generally when they complete some form of I/O. For example, the disk drive has just finished getting some data from the disk. error A range of error conditions, such as divide by 0 or memory access errors, which result in error interrupts. Error interrupts usually result in the current process being killed. timer Time interrupts occur regularly at a set time period and are generated by the system timer. Timer interrupts are heavily used in CPU scheduling which is talked about in the following weeks. Can you see what would happen if the operating system wasn't there to handle interrupts? Think about the tasks you or other computer programmers would have to handle if there wasn't an operating system to handle interrupts.

I/O Structure
A lot of the effort expended by a computer is in input/output (I/O). Especially in this day of graphical user interfaces (GUIs). The operating system has a major role to play in managing the I/O a computer performs. The main reasons for this are speed I/O devices, when compared to the rest of computer hardware, are very, very slow. Something has to balance and schedule the allocation and use of these hardware devices. To manage their working together. variety I/O devices are the most varied forms of computer hardware. They range from disk drives through to keyboards and many other stange devices. These I/O devices vary in terms of speed, amount of information and many other characteristics. Something has to hide this variety, the operating system. In order to understand this role it is important that you first have

an understanding of how I/O works.


Reading 1.8 Textbook, Chapter 2, Section 2.2

Two of the important points to come out of the previous reading, which you will find repeated throughout the semester, are busy waiting is bad Having the CPU loop around for long periods of time doing meaningless work is not an efficient use of the computer's resources. Remember, one of the aims of an operating system is to provide efficient resource management. overlapping I/O and CPU utilisation is good This is again related to efficient resource management. Having I/O devices and the CPU doing work at the same time is making very efficient use of the computer's resources.

Storage Structure
To do any work a computer must be able to store and retrieve information. This is where the computers storage media enter the picture. The wide variety of characteristics of different storage media means that the operating system has quite a task to perform.
Reading 1.9 Textbook, Chapter 2, Section 2.3

Storage Hierarchy
A typical computer has a number of different systems for storing data and programs. The following reading talks about the hierarchy of these systems and what's involved in balancing the characteristics of the levels of this hierarchy to achieve reasonable performance.
Reading 1.10 Textbook, Chapter 2, Section 2.4

Hardware Protection
Throughout the previous readings you have been introduced to the benefits of overlapping I/O with CPU utilisation. Like most things there is a down side to the benefits this provides. One of those problems is the very fact that there are multiple jobs sharing the system. Each of these jobs must be restricted to its own resources to prevent inadvertent (or in the case of attempts to break security, on purpose) intrusions into others. Any form of protection provided by software (i.e. the operating system) can be worked around. To be "really safe" this form of protection must

be provided by the hardware.


Reading 1.11 Textbook, Chapter 2, Section 2.5

Network Structure
This reading gives a brief overview of different types of networks.
Reading 1.12 Textbook, Chapter 2, Section 2.6

Exercises for Chapter 2


Exercises Textbook, Chapter 2, questions 1-10

This is the last use of chapter 2 of the textbook. You might want to use this as a place to take a break.

Operating-System Structures
Operating systems are amongst the largest and most complex collection of algorithms and data structures. Windows 2000, the latest version of Windows NT, has millions of lines of code. It's obvious that a system this large must be broken up into smaller components to make it easier to understand, design, implement and test. The remainder of the reading for this week gives you an overview of the structure of a typical operating system. It does this by introducing you to 3 ways of looking at an operating system services The operating system must provide a set of services to programs. interface To access the operating system services user programs make use of an application programming interface (a set of functions) called system calls. structure How an operating system is designed and structured influences its speed and expandability.

Operating System Components and Services


Even though you may not be aware of it, as a programmer you have made use of a number of the services provided by the operating system. This is one of the reasons why learning about operating systems is important for programmers. If you are aware

of these services and how they are implemented you can make use of these services to make programming simpler and your programs more efficient. The following reading gives you an overview of the standard services that must be provided by most operating systems. The sections in which they are introduced also happen to provide a good overview of what we will be studying over the rest of the semester. These sections are process management, memory management, file management, I/O system and secondary storage management,and protection and security.
Reading 1.13 Textbook, Chapter 3, Introduction and Sections 3.1 and 3.2

System Calls
As you might have concluded from the previous readings the services offered by an operating system are at very low level. So low that people don't interact directly with the operating system. People interact with programs that offer much higher-level services. It is the programs that make use of the services offered by the operating system. Operating services are made available by system calls. System calls are essentially system calls (not exactly but close enough). The set of system calls offered by an operating system define the interface used by programs. This means that the only requirement to run a Windows program is something that implements the collection of system calls (plus a few extras libraries) that a Windows program needs. This set of system calls can be provided by any operating system or even another user program (with some special configuration). More on this after the reading.
Reading 1.14 Textbook, Chapter 3, Section 3.3

As was mentioned prior to the reading the only distinction between different operating systems, that matters to programs (remember, all programs see of the operating system are the set of system calls) are the set of system calls it provides. It is this fact that is making the distinction between one operating system and another increasingly meaningless. It is now possible for a particular operating system to run the programs originally compiled for a completely different operating system.

An example system call interface


In most operating systems each system call is assigned a particular number. These numbers are defined in one of the source code files for the operating system. Figure 1.2 shows a section from a source file from a version of the Linux operating system. Linux

currently has about 163 different system calls. In this section you can see the first 13. Most of these system calls are related to manipulating files, directories and processes. You should be able to make some connection between the Linux system calls in Figure 1.2 and the types of system calls listed in Figure 3.2 of the textbook.
#define #define #define #define #define #define #define #define #define #define #define #define #define SYS_exit SYS_fork SYS_read SYS_write SYS_open SYS_close SYS_waitpid SYS_creat SYS_link SYS_unlink SYS_execve SYS_chdir SYS_time 1 2 3 4 5 6 7 8 9 10 11 12 13

Figure 1.2. Linux system call numbers

A program and its system calls


Even the simplest of programs make heavy use of system calls. One way to see the system calls used by a process running under the Linux operating system is to use the strace command. Figure 1.3 shows a simple C++ program (all it does is print "hello world" onto the screen) and the output of the strace command when running that program.
#include <iostream.h> void main() { cout << "hello world" << endl; }
[david@faile study_guide]$ strace -Tc ./simple execve("./simple", ["./simple"], [/* 26 vars */]) = 0 hello world % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- -----------------------28.55 0.000340 31 11 7 open 23.09 0.000275 92 3 read 17.97 0.000214 19 11 mmap 7.30 0.000087 17 5 mprotect 5.71 0.000068 17 4 3 stat 4.28 0.000051 26 2 munmap 3.36 0.000040 8 5 fstat 2.85 0.000034 34 1 write 2.69 0.000032 8 4 brk 2.18 0.000026 7 4 close 1.01 0.000012 12 1 ioctl 0.50 0.000006 6 1

getpid 0.50 0.000006 6 1 personality ------ ----------- ----------- --------- -----------------------100.00 0.001191 53 10 total

As you can see from the output of the strace command even this simple program used 53 system calls. The following output from the strace command is from running the Netscape Web browser, loading one page and then exiting. You can see how the number of system calls really mounts up. Over 29,000 systems calls just to start Netscape and load a single Web page.
[david@faile david]$ strace -c /usr/local/netscape/netscape execve("/usr/local/netscape/netscape", ["/usr/local/netscape/netscape"], [/* 26 vars */]) = 0 % time seconds usecs/call calls errors syscall --------------------------------------------------------42.92 0.542800 333 1632 write 16.90 0.213711 82 2614 oldselect 10.76 0.136065 37 3701 53 read 6.94 0.087808 570 154 writev 4.58 0.057954 7 8034 gettimeofday 4.06 0.051396 7 7621 sigprocmask 2.56 0.032341 22 1468 brk 2.25 0.028403 3550 8 fsync 1.47 0.018642 6214 3 wait4 1.33 0.016875 83 204 69 stat 1.05 0.013249 97 137 18 open 0.97 0.012251 70 174 171 access 0.93 0.011713 9 1274 ioctl 0.68 0.008547 7 1247 5 lseek 0.35 0.004478 47 95 getdents 0.35 0.004469 1490 3 fork 0.30 0.003819 41 93 mmap 0.29 0.003614 28 131 close 0.24 0.003080 280 11 readv 0.20 0.002555 134 19 9 connect 0.18 0.002258 11 205 2 sigreturn 0.15 0.001900 7 256 time 0.10 0.001323 26 51 munmap 0.09 0.001188 119 10 socket 0.07 0.000850 213 4 1 unlink 0.05 0.000605 7 84 fcntl 0.04 0.000532 18 29 mprotect 0.04 0.000479 10 50 fstat 0.04 0.000470 26 18 lstat 0.02 0.000264 132 2 rename 0.02 0.000218 27 8 pipe 0.02 0.000207 15 14 uname 0.01 0.000168 168 1 symlink

0.01 0.000149 9 17 sigaction 0.01 0.000113 113 1 ftruncate 0.00 0.000035 12 3 fchmod 0.00 0.000026 9 3 geteuid 0.00 0.000025 6 4 getpid 0.00 0.000025 6 4 getuid 0.00 0.000016 8 2 dup2 0.00 0.000014 7 2 getgid 0.00 0.000013 7 2 dup 0.00 0.000012 12 1 setitimer 0.00 0.000009 9 1 personality 0.00 0.000008 8 1 setgid 0.00 0.000007 7 1 setuid 0.00 0.000006 6 1 getegid --------------------------------------------------------100.00 1.264690 29398 328 total

System Programs
Now we come to an area that is causing a great deal of trouble for certain parts of the computer industry, system programs. The following reading attempts to explain system programs.
Reading 1.15 Textbook, Chapter 3, Section 3.4

The question of whether or not user programs are part of the operating system has been around for quite a while. Over the last few years the United States Department of Justice has been seriously examining Microsoft's strategies when it comes to operating systems and user programs. In particular, how Microsoft has apparently been trying to use the spread of its Windows operating systems to gain market share for its Web browser. For the purposes of COIT13152 we will use the more restrictive definition of an operating system. That is, a definition that does not include system programs as part of the operating system. The operating system is the data structures and algorithms that reside under the system call interface.

System Structure and Virtual Machines


Appropriate design of the internals of an operating system is essential to make the task of creating, debugging and maintaining such a large collection of code. The following reading examines some of the details of how operating systems have been

structured. The section on virtual machines leads into how, using Java, it is possible to create programs that are truly architecture-independent and therefore highly portable.
Reading 1.16 Textbook, Chapter 3, Sections 3.5 and 3.6

System Design, Implementation and Generation


The implementation of software which is as large, complex and fast-changing as operating systems over a long period of time by smart people generates a lot of lessons. The following reading introduces a few of these lessons.
Reading 1.17 Textbook, Chapter 3, Sections 3.7, 3.8 and 3.9, pp 78-83

So how does that help? One example is the separation of mechanisms and policies. This is an example of the separation of concerns, a principle that has applications in a number of computing fields. For example it is very useful in the design of web pages. A web site has a number of components content The actual information that the site contains. presentation How the pages actually look. structure How the pages which make up the site are structured. Most web sites do not separate these concerns. Most web pages generally have all these concerns mixed up together. Separating these concerns increases the flexibility of a web page. If content and presentation are separate you can apply different presentations to the same content. For example, you might want to provide different versions of a web page for people with different browsers without having to manually rewrite the pages.

Exercises
Exercises Textbook, Chapter 3, questions 1-11

Summary
Well you've done it. Finally reached the end of this marathon study guide chapter. Relax; take it easy, this is the largest amount

of reading you will have to do for the entire semester for COIT13152, Operating Systems. So what have you learnt? from chapter 1 of the textbook, you will have gained some idea of what an operating system is, what it does and how and why operating systems have developed over the 50 years computers have been around. from chapter 2 of the textbook, you will have revised some details about the operation of computer hardware including the instruction-execution cycle, I/O, interrupts, context switches, the storage hierarchy and hardware protection. from chapter 3 of the textbook, you will have a general overview of an operating system including the components of an operating system and their responsibilities, the services an operating system provides to user programs, the concept of system calls, the difference between system programs and the operating system and how operating systems are designed and implement. Also in here was a look at how operating systems are structured. That was the overview. From next week we start looking at some of the details of the components of an operating system.

Chapter 2 Processes

Introduction
On a modern computer system there can be many programs active at any one time. It's the responsibility of the CPU (Central Processing Unit) to actually execute the instructions for these programs. However, with multiple programs somewhere between start and finish at the same time something has to keep track all these programs. Tracking these programs includes knowing where they are up to, what they are doing and what resources they own. One of the responsibilities of the operating system is to keep track of this information. It does this using processes. Almost all the work (and in some operating systems all the work) performed is done by processes. In this chapter we define what a process is and how an operating system deals with them. The concepts this chapter introduces includes process states During its lifetime a process will move through a number of different states. Each state has its own characteristics and there are limitations on which state transitions make sense. process description The operating system must be able to keep track of processes and the resources they own. This information is stored in a number of operating system data structures including Process Control Blocks (PCBs). operations on processes There are a number of standard operations which are performed with operating systems, including creation, termination and switching. process scheduling Typically there are multiple processes on a computer which much share the computers resources, particularly the Central Processing Unit (CPU). Process scheduling deals with the decisions of how to share those resources amongst processes. threads The concept of a thread is an extension of the process concept and provides a number of benefits. The importance of the process concept to operating systems

means that the next four chapters of the study guide, four weeks of COIT13152, deal with process related concepts.

Objectives
At the end of this chapter you should have a good knowledge of the process concept. Including the different states a process can be in what causes process transitions from one state to another what data structures are used to identify and track processes what is a context switch, what is a process switch, how are these terms related, how are they different what is a PCB, what is it used for what is a thread or lightweight process what advantages threads provide

Resources
Textbook, Chapter 4 Study Guide Chapter 2 Online lecture 4 includes audio and animations

The Process Concept


So what is a process? What are process states? What is a process control block (PCB)? These and other questions are answered by the next reading from the text that introduces the concept of a process.
Reading 2.1 Textbook, Chapter 4, Introduction and Section 4.1

Slide 3 from online lecture 4 includes an animation of Figure 4.1 in the textbook. The understanding of process state transitions is vital; the animation shows how processes move from one process state to another. In COIT13152 this is what we will refer to as a process switch. Later in this chapter you will see how the COIT13152 definition is different (and smaller) than the definition used in the text book. IT IS IMPORTANT THAT YOU UNDERSTAND THE DEFINITION OF PROCESS SWITCH USED IN COIT13152 and that it is slightly different from the definition used in the textbook.

The Operating System and Process States


One important aspect of the animation is that the operating system is responsible for moving processes from one state to another. You will see how later in the study guide. When and how does the operating system run? In the animation you will see Olive, the dinosaur which is COIT13152's "mascot", moving onto the CPU. Olive is a representation of the operating system, or at least parts of the operating system. At the very start of this chapter it was stated that "almost all the work done on a computer is performed by processes". Is the operating system a process? The answer to this question is, "it depends". It depends on the particular operating system you are talking about. Different operating systems are implemented in a different ways. For the operating system in our animation, the answer is that the operating system is not a process. This is represented by the fact that Olive, the representation of the operating system, does not use any of the process queues. In other operating systems there may be a number of different processes which are used to perform individual operating system tasks, including switching processes from one state to another. What this means is, that if the operating system shown in the animation of slide 3 used the "operating system as a process" approach then Olive (the representation of the operating system) would appear in some of the queues when it isn't on the CPU. In the current animation the operating system is not a process. This is why Olive just appears to hang around in no where in particular when she isn't on the CPU.

It depends
This is one of the examples of "it depends" that you will see repeated throughout COIT13152. There are many different operating systems which all perform these tasks in different ways. In COIT13152, because you are just learning about operating systems, we can't introduce all the different ways operating systems perform tasks. It would be too complex. Instead the textbook introduces the concepts in a simplified and generalised way. This approach introduces you to the important concepts with each part of operating systems without overwhelming you with complexity. However, it is important that you realise that very few operating systems will implement processes, CPU schedule or any other operating system related concept in exactly the way discussed in COIT13152.

Process Scheduling and Operations

Process scheduling is one of the major tasks an operating system must perform in its role as resource manager. The aim of process scheduling is to maximise usage of the CPU and other system resources such as I/O devices by switching between processes. The following reading gives a brief overview of process scheduling and some of the events which cause process switching. Remember, the COIT13152 definition of a process switch is slightly different from that used by the textbook. This difference is explained soon.
Reading 2.2 Textbook, Chapter 4, Section 4.2

The number of running processes on a computer always equals the number of CPUs. Remember, the definition of the running state is that the process is actually on the CPU being executed. Only one process can be using a CPU at any one time. So the number of running process equals the number of CPUs. Traditionally most computer systems you will have seen have only the one main CPU. However, the trend today is making computer systems with multiple CPUs much more common, especially as servers for organisations. All the other processes in a system (remember there could be hundreds even thousands) are in queues associated with the other states discussed in Reading 2.2. This means that the processes could be blocked Waiting for some I/O event to occur, i.e. The disk to finish reading some information. ready All ready to execute but just waiting for its turn on the CPU.

Process switch versus context switch


Process and context switching are closely related concepts, but they are not the same, even though the textbook sometimes treats them as the same. It is important that you understand that there is most definitely a difference between a process and a context switch. In COIT13152 you will be expected to know this difference and use it accordingly.

Context
First, let's define what the context actually is. All CPUs contain a number of registers that provide information about the current state of execution. Some of these were mentioned in the previous study guide chapter and include such registers as the program counter, instruction register and processor status word. It is this information that specifies the current execution state of the CPU.

Context Switch
A context switch involves saving the current context of the CPU and replacing it with another context. What this does is saves the current program/process, allows you to run another program/process and then at a later stage return back to the original process by restoring its context. As far as the original program/process knows, nothing interesting has happened. Context switches usually happen whenever there is an interrupt. There are four general types of interrupts, defined in the previous study guide chapter, software, hardware, error and timer. The animation on slide 3 of online lecture #4 includes some very obvious context switches. Whenever Olive, the dinosaur, is placed onto the CPU it performs a context switch. The context of the process that was running on the CPU is saved somewhere in RAM and the context for the operating system (or at least the context for the section of the operating system which will handle the process switch), represented by Olive, is placed onto the CPU. Once Olive has performed the necessary work, such as creating a new process, another context switch occurs, saving Olive's context and placing the context of another process onto the CPU. Context is a hardware concept; it is tied to the CPU of the system. Context switches are performed hundreds, thousands and even millions of times a second and are usually supported by hardware in someway.

Process switch
Process switching is where the current running process is replaced by another process. This occurs in a number of situations including the current running process finishes and moves from the running to the zombie/dead state the current running process requests some I/O and moves from the running to the waiting state the current running process uses up its time on the CPU and moves from the running to the ready state You will have seen examples of this occurring in the animation on slide 3 of lecture 4. Since it involves processes, remember processes are an operating system construct; a process switch relies heavily on operating system code and data structures. The steps involved in a process switch include interrupt occurs All process switches will be initiated by an interrupt. When a process finishes it usually executes a system call (implemented as a software interrupt) called exit which destroys the process. When a processes share of time on the

CPU is finished this is indicated by a timer interrupt. When a process moves from the blocked state to the ready state it is usually because of an I/O interrupt. Moving from running to blocked is usually because of a request to do I/O (a software interrupt). Since the process switch almost always starts with an interrupt, the first thing that happens is a context switch from the current running process to the interrupt handler of the operating system. save the context of the current running process restore the context of the operating system, in particular the interrupt handler The interrupt handler determines which part of the operating system will handle the interrupt. Timer, software and I/O interrupts will be handled by different sections of the operating system. However, some part of these operating system sections will eventually initiate a process switch which uses exactly the same steps. change the PCB of the old running process to represent the change in state The process could be going from running to blocked, zombie, or ready. move the PCB of the old running process to the queue associated with its new state In many cases this will entail simply changing a pointer. Remember, it is important that this process be very efficient so it can be done quickly. Copying large amounts of data (e.g. the PCB) every time there is a process switch just wouldn't make good sense. select another process from the ready queue This is a very important step and one we examine in more detail next week. It is important because it can take a long time (by far the largest portion of a process switch) but also because which process is chosen can directly influence the performance of the system. change the PCB of the chosen process to represent its new state "move" the new process from its current queue to running replace the context of the kernel with that of the new process After this step the new running process starts off from where it left off the last time it executed.

Process and context switch: the relationship


A context switch doesnt mean a process switch occurs. For example, one of the types of interrupts is the timer interrupt. Most computer systems have a timer that generates an interrupt at

regular intervals. What usually happens at a timer interrupt is the following there is a context switch to an interrupt handler (part of the operating system) the interrupt handler decides whether the timer interrupt means that anything should change if it decides, no, then it restores the context of the previously running process. This is an example of where a context switch does not mean a process switch. However a process switch always involves a context switch (perhaps even more than one). If you look through the rough list of steps of what occurs during a process switch you will see that there are a number of context switches within a process switch.

Operations on processes
So far we've looked at the states a process can be in and how a process can move from one state to another. What we haven't done so far is examine how processes are created and terminated. This is where the next reading comes in.
Reading 2.3 Textbook, Chapter 4, Section 4.3

Creating a new process: a program


So how do you create a new process? The previous reading mentioned the system calls fork and execve. The following source code is for a simple UNIX C++ program which displays the unique process identified (PID), remember each process must have a unique identifier so that the operating system knows one from the other, creates a child process and the child process displays its PID.

Explanation
The following is a quick explanation of the lines in the program. Lines 1 and 2 are simple include statements which make sure we have the appropriate include files for the cout (iostream.h), fork (unistd.h) and getpid (unistd.h) functions. Line 3 declares the two integer variables we are going to use to store the process identifiers for the two process we create. Remember, in modern operating systems every process has a unique process identifier, a number. The operating system uses this process identifier to uniquely identify (that's a surprise) each process. In this example we will be creating two process the parent and the child.

Line 4 is were we actually get the process identifier for the parent process. We use the standard UNIX library call getpid which returns the integer identifier. In Line 5 we simply display onto the screen a short message indicating the process identifier for the parent process. Line 6 is the "guts" of this program. It is also the line which will be different from anything most of you will have seen before. This line actually gets "executed" twice. Once in the parent process and once in the child process. The sequence goes something like this the parent process executes the fork library call fork actually returns twice, once for the parent and once for the child. It is the fork library call which actually creates the child process. The new child process is almost identical to the parent process. It will have the same code and a copy of the same data. Even it's context will be a copy of the parent's process. This means that the child is executing the same program as the parent AND is up to the same place (just after the fork). However, there is a slight difference. the parent after fork The fork function actually returns twice. Once for the parent (which called the fork function) and once for the child process (which the fork function created). In the parent process the return value of the fork function will be equal to the process identifier of the new child process. This makes it possible for the program in the parent process to know who its children are. So in our example program the parent process proceeds to execute line 8. the child after fork The return value of the fork function in the child process is equal to 0. So in our example program the child process will proceed to execute line 7.

Line 7 is only ever executed by the child process and all it does is display a message onto the screen that it is the child process. Lines 8 and 9 are only ever executed by the parent process. All they do is to display a message showing that the parent process knows the process identifier of the child process.

1 2 3

#include <iostream.h> #include <unistd.h> void main() { int child_pid, parent_pid; parent_pid = getpid(); cout << "I am the parent with pid " << parent_pid

4 5

<< endl; 6 if ( ( child_pid = fork() ) == 0 ) { // if child_pid == 0 then we are in the child process cout << "I am the child being executed" << endl; } else { // if child_pid != 0 then we are still in parent and // child_pid contains the PID of the child process cout << "I am still the parent, my child had process id of"; cout << child_pid << endl; } }

8 9

Here's an example of the output of this program.


[david@faile guide]$ ./fork
I am the parent with pid 1780 I am still the parent, my child had process id of 1781 I am the child being executed I am still the parent, my child had process id of 1781

Co-operating Processes
The next reading provides an introduction to the very complex area of co-operating processes. We will actually revisit this area in chapters 4, 5, and 6.
Reading 2.4 Textbook, Section 4.4

Interprocess communication
At some stage some processes will wish to communicate with other processes. The following reading provides an overview of some of the ways in which interprocess communication is implemented.
Reading 2.5 Textbook, Chapter 4, Section 4.5

Communication in Client-Server Systems


Client-Server systems are now of ever-increasing importance in the modern world; it is vital therefore that processes can communicate in such systems.

Reading 2.6 Textbook, Chapter 4, Section 4.6

Summary
Well that's an overview of the process concept. Since the process is how the operating system tracks all the work being done within a computer system it is a foundation principle of operating systems. You will find much of the following week's work difficult if you do not fully understand the process concept. Especially the difference between and what happens during a process and a context switch.
Reading 2.7 Textbook, Chapter 4, Section 4.7 Exercises Textbook Chapter 4: 1, 2, 4, 5, 6, 7 and 9

Threads
Until a few years ago the concept of a thread wasn't all that widely implemented. However, over the last few years almost all the modern operating systems, including versions of UNIX and Windows NT, support multi-threading. What is a thread? How is it different from a process? Why are we worried about threads? These are some of the questions answered in the following reading.
Reading 2.8 Textbook Chapter 5

The concept of the process can be represented as having two characteristics resource ownership, and Processes will own files, shared memory, I/O devices and other resources from a computer system. dispatch Basically the location of execution (sometimes known as the thread of execution), the current context and a stack. In summary, this is where the process is currently up to. Up until this section of the study guide we have been talking about the older, heavyweight, process concept. This idea of a process, which encompassed both resource ownership and dispatch, is the traditional process concept which has been used for many years. The move to threads/lightweight processes divides these two

characteristics into two separate entities. Resource ownership remains with some form of process. The unit of dispatch is used to define threads. Normally a process, the resource ownership concept, will be associated with a number of threads which all share the resources of the process. Multi-threading provides a number of benefits including easy sharing of resources All the threads for a single process share the resources of that process. This can be useful in a number of programming problems, such as file and Web servers. efficiency The main reason for this is that it requires less time and resources to switch between threads (basically a context switch) than it does to switch between heavyweight processes (a process switch).

Email Question
> I find the use of the term "user thread" a little > difficult to understand. I would have thought that, if as I > take it the "user" is a person, a "user thread" would be very > slow, because it would have to wait for keyboard input. It is > obvious that the textbook cannot mean this. What the book is talking about is user LEVEL threads and its related concept kernel LEVEL threads The missing LEVEL may be what is throwing you. Kernel level refers to stuff/operations/work which happens within the kernel. This "stuff" will be done with the system mode bit set. When that system mode bit is set we are "in the kernel". User level refers to "stuff" which happens when the system mode bit is NOT set. i.e. We are outside the kernel. It doesn't mean users as in people. User level is where the processes you and I run. Their permission level. One of the advantages of user level threads is that can be a little more efficient because they execute "at the same level" as the process. There is no need to switch to kernel mode.

Chapter 3 CPU Scheduling

Introduction
In its role as resource manager the operating system plays an important part in ensuring the efficiency of a computer. One of the most important resources in a computer is the CPU. CPU scheduling is the way in which the operating system manages the CPU and in multi-programming systems shares it amongst multiple processes. This chapter examines how and why CPU scheduling is used. It also introduces some of the algorithms that are used to perform CPU scheduling. CPU scheduling is how the operating system selects the next process to become the running process during a process switch. Remember, if there is only one processor, there can only be one running process at any instant in time.

Objectives
By the end of this chapter you should be aware of the scheduling algorithms FCFS, round-robin, SJF and the use of feedback queues indefinite blocking (also known as starvation and indefinite postponement), why it occurs and how to solve the problem the difference between an I/O bound and CPU bound process the different criteria used to measure scheduling algorithms the difference between pre-emptive and non-pre-emptive scheduling algorithms

Resources
Textbook, Chapter 6 Study Guide Chapter 3 Online Lecture 5

The Basics
The following reading introduces many of the basic concepts

which drive the design and development of CPU scheduling algorithms.


Reading 3.1 Textbook, Chapter 6, Introduction and Section 6.1

Scheduling Criteria
There are a wide range of algorithms which can be used to implement CPU scheduling. How do you decide which algorithm is the most appropriate for your situation? There are range of criteria which can be used to evaluate the performance of these algorithms. The following reading introduces these criteria.
Reading 3.2 Textbook, Chapter 6, Section 6.2

Scheduling Algorithms
The following reading introduces some of the basic algorithms which can be used to implement CPU scheduling. The actual algorithms used in real operating systems are actually much more complex adaptations of the algorithms introduced in this reading. The important part in this reading is being aware of the relative advantages and disadvantages of each of the algorithms.
Reading 3.3 Textbook, Chapter 6, Sections 6.3 through 6.5

Email Question
> (In) the textbook there is an example of > a preemptive SJF scheduler.... > Process Arrival Time Burst Time > P1 0 8 > P2 1 4 > P3 2 9 > P4 3 5 > > The average waiting time has been calculated as: > ((10-1)+(1-1)+(17-2)+(5-3)) / 4 = 6.5 milliseconds > I understand that for process P1 we calclulate 10-1 > since process P1 started at time=0 and ended at > time=1 and then recommenced at time=10.

> But What I DONT understand is how did we get 1-1 for > P2 in the equation to calculate average time and 17> 2 for P3 and 5-3 for P4. The book has chosen a difficult to understand method for representing this. Waiting time is any time a process is ready to run (i.e. it is in the ready queue) but it can't get onto the CPU because there is another process using the CPU. It is said to be waiting for the CPU. So to calculate the average waiting time you need to figure out how long each process was ready to run, but couldn't. Let's look at each process in this example, one by one. Process 1 arrives at time 0 and executes on the CPU until time 1 from time 1 to time 10 it is waiting to run at time 10 it executes until time 17 the waiting time for process 1 is 10 - 1 = 9 milliseconds Process 2 arrives at time 1 and executes on the CPU until time 5 at this stage it has used up all its burst time the waiting time for process 0 is 0 it never had to wait Process 3 arrives at time 2 but doesn't execute on the CPU until time 17 (it is waiting all this time) at time 17 it executes until time 26 at which time it is finished the waiting time for process 3 is 17 - 2 = 15 milliseconds Process 4 arrives at time 3 but doesn't execute until time 5 at time 5 it executes until time 10 the waiting time for process 4 is 5 - 3 = 2 milliseconds

Algorithm Evaluation
The following reading describes methods which can be used to determine which scheduling algorithm best suits, and some

process scheduling models.


Reading 3.4 Textbook, Chapter 6, Sections 6.6 and 6.7

Deterministic modelling is one of the standard ways of testing understanding of scheduling algorithms. You will notice in many of the past exams for COIT13152 that there is almost always an exam question which is an example of deterministic modelling. Exercises 6.3 and 6.4 from the textbook are examples of deterministic modelling.

Summary
Reading 3.5 Textbook, Chapter 6, Section 6.8

Exercises
Textbook Chapter 6 2, 3, 4, 5, 6, 8, 9, 10

Email Question
> I have a 6.4 (C) > from the > create a pesudo > code for couple of questions about question textbook. Firstly are we supposed to forumla for calculating this or a the algorithm.

Actually neither. As with parts a and b you are meant to "create a deterministic model" of this situation. Which is just a verbose way of saying that you have to simulate the algorithm. So just as you walked through the SJF algorithm in part b in order to calculate the average turn around time. You need to do this again. However, this time the CPU doesn't start executing until time 1.0 when all the processes in this example have arrived. > Secondly in the first part of the question > it mentions that process1 is run at time 0 then > later in the questions it mentions that process1 > and process2 are waiting. I gather from this > process3 has already been processed. Let's take a look at the information about these processes which was given earlier in the question Process P1 P2 P3 Arrived Time 0.0 0.4 1.0 Burst Time 8 4 1

What this stuff means is that Process 1 is going to arrive at time 0 and has a burst time of 8. Since there are no other processes in the system, at this time, then process 1 is placed onto the CPU. It is the shortest job, so it goes first. A bust time of 8 means that process 1 will finish executing at time 8.0. By this stage both process 2 (arriving at 0.4) and process 3 (arriving at 1.0) have arrived. At this time the shortest job will be selected. This will be process 3 because it only has a burst time of 1.

Chapter 4 Concurrency and its Problems

Introduction
Concurrent processes are those that exist at the same time. Concurrency happens all the time in the modern computer world. Everytime you are writing and email message while waiting for a Web page to download you are making use of concurrency. Concurrency is a great advantage but it introduces problems. In this chapter we examine those problems and ways to solve them. This chapter, and the next one, are important and different from other chapters for a number of reasons increasing use of concurrency A few years ago few programmers had to worry about concurrency and the related problems. Today's modern operating systems and other software systems make heavy use of concurrency. It is increasingly likely that you will have to write concurrent programs. the OS perspective Operating systems themselves must solve these concurrency problems otherwise they will not be able to perform correctly. Also it is usual for the operating system to provide some of the tools which can be used to address these problems. more than the OS perspective This and the next chapter involve you in more than thinking about how the operating system implements features. You will be expected to write a number of programs which solve the problems associated with cooperating, concurrent processes. Based on previous experience in COIT13152, the concepts introduced in this chapter are amongst the most difficult you will face this semester. Concurrency problems can be very subtle and hard to reproduce. However, understanding these concepts is somewhat like climbing a hill. It is really difficult to start with, but once you reach the top it is really easy. The moral of this story is don't get frustrated. Stick with it, ask questions and write lots of programs using concurrency. If you do this then eventually it all will become clear. To help you gain more experience with these problems there are a

number of concurrency problems (and sample solutions) included at the end of this chapter. These problems are examples of assignment and exam questions from over the last few years of COIT13152.

Email Message
This is a copy of an email message sent to the COIT13152 mailing list while students were working on the first assignment.
There are a number of concepts in computing that I call brickwall concepts. When you are first learning them it can feel like you are beating your head against a brickwall. Some example brickwall concepts include pointers and recursion. The nice thing about brickwall concepts (trust me, it is a nice thing) is that if you beat your head against the brickwall longer enough you break the brickwall. Once you've done this understanding these concepts will be easy (apart perhaps from the lingering headache). Beating your head against the brickwall is an analogy for reading lots of different explanations, asking questions, thinking about the concepts a lot and attempting a great many practical exercises. Most of you should currently be grappling with another brickwall concept in computing: concurrency. Trust me, if you beat your head against the brickwall enough so that you break the wall down you will understand concurrency and find it quite simple. If you don't do enough to break that wall down you will never really get concurrency. So please, really get stuck in and beat your head against the brickwall a lot. Over the next few days I will be providing additional material in the way of explanations and exercises. Please make use of them.

Objectives
By the end of this chapter you will: be aware of problems with co-operating, concurrent processes know what a race condition is be aware of what a critical section is and why mutual exclusion must be implemented on a critical section be familiar with the difficulties of implementing mutual exclusion with standard programming tools

be able to use tools such as test-and-set instructions and semaphores to implement mutual exclusion and solve problems requiring co-operating, concurrent processes have had an introduction to some of the classical concurrency problems such as readers/writers and the dining philosophers

Resources
Textbook, Chapter 7 Study Guide Chapter 4 Online lecture 6 this lecture includes both audio and animations and can be found on both the COIT13152 Web site and CD-ROM. Optional: BACI is a system you can use to write concurrent programs. It is available from the COIT13152 Web site and CDROM. You may find writing BACI programs help you understand some of the concepts introduced in this chapter.

What is the problem?


So what is the problem? The program below is a simple example of the problem. This program is a BACI program but is based heavily on C++ so hopefully looks familiar. This one program starts three processes. Each processe than proceeds to write to the screen the message "hello from process X" (where X is either A B or C)
void say_hello (char id) { cout << "hello from process " << id << endl; } main() { cobegin { say_hello( 'A'); say_hello('B'); say_hello( 'C'); } cout << " all finished " << endl; }

The problem with this program is that the output of the three processes is always jumbled and always different. For example (the text in bold is what I typed)
$ bainterp simple Executing PCODE ... hello from process hello from process C hello from process A B all finished

$ bainterp simple Executing PCODE ... hello from process B hello from process hello from process A C all finished $ bainterp simple Executing PCODE ... hello from process Chello from process B hello from process A all finished

See how the the output is always jumbled. Since all three processes (A, B and C) are sharing the same resource (the screen) there is a problem. A race condition. A race condition is where the end result of the program is always different depending on which process wins the race. A more generic statement of the problem is: How can two or more processes gain access to a global resource in a SAFE and EFFICIENT manner. The first stage to solving this conflict of interest is to recognise the importance of the global resource. This resource then has to be manipulated with care.
Reading 4.1 Textbook, Chapter 7, Introduction and Section 7.1

Race Conditions
The problem discussed in the previous reading is an example of a race condition. Here's another explanation. Whenever, two or more processes attempt to do a "complex" operation on a global resource, there is a chance for disaster. A complex operation is any operation that modifies the global entity, but is achieved in a series of distinct, separable steps. In the previous section we saw an example of a race condition when three processes are writing quite a large message to the screen. You should be aware that part of the problem is that writing a large message to the screen actually entails a large number of machine instructions. Since there are many instructions it is possible for the process to be interrupted half way through writing the message. This problem can be even more subtle. Consider the simple operation of incrementing a variable in a C++ program:
x := x + 1;

In many computers, this actually consists of three individual and

distinct steps read the variable from memory into a CPU register increment the contents of the register write the contents of the register back into memory. What happens when two processes (e.g. process A and process B) try to carry out this update at approximately the same time? Usually, nothing bad happens and x ends up being two greater than it was (since two processes have incremented it). The problem comes if the three component instructions are interleaved with each other. Consider some of the possible permutations in which these six instructions can be carried out. There are in fact 20 different combinations. The following shows two of those combinations which cause problems. In the following read_A means process A has done the read, inc_B means process B has incremented and so on. read_A; inc_A; read_B; inc_B; write_B; write_A; This results in x being incremented by 1! One of the increments is lost. read_A; read_B; inc_A; inc_A; write_B; write_B; This also results in x being incremented by 1! One of the increments is lost. In fact only 2 of the 20 possible permutations results in the correct answer! The two versions of read_A; inc_A; write_A; read_B; inc_B; write_B; where one process completes before the other starts. These different permutations arise when there is a process switch between any of the instructions, or if two CPUs are executing the three instructions at the same time. Note, despite its name, race conditions normally occur when one process stops! The problems occur when the process stops between determining that it is `safe' to do something and actually doing it. Any resource that could possibly be altered by ANY two or more processes at the same time has to be protected. All such items, be they hard disk, files, printers, ... are called critical resources.

Solving the race condition


As you will see in the following readings you can solve this problem by implementing mutual exclusion. To solve the race condtion for our example BACI program we could implement mutual exclusion on the say_hello function. The end result of this would be that only one process would be able to execute the say_hello function at any one time. At the moment you can have two or three processes trying to display their message at the

same time. This is how we get the jumbled output. We do this by making the say_hello function indivisible or atomic. This means that once a process gets into that function it can't be interrupted by another. This means, on BACI at least, that no other process will enter the function. The simplest way to do this in BACI is to make the say_hello function atomic. To do this you will need to change the source code for race.cm. Change the line
void say_hello ( char id )

to
atomic void say_hello( char id )

Now, if you recompile and execute the program you should see something like the following.
$ bainterp simple Executing PCODE ... hello from process C hello from process B hello from process A all finished $ bainterp simple Executing PCODE ... hello from process C hello from process A hello from process B all finished $ bainterp simple Executing PCODE ... hello from process C hello from process B hello from process A all finished

Can you see how by using the atomic keyword ensures that only one process is ever inside the say_hello function and as a result solves the problem of our jumbled output. We still have the race condition though. Notice how the result is different depending on the speed of execution.

Critical Sections
A critical section is simply a section of code for which it is necessary to implement mutual exclusion. In the BACI program we used in the previous section the say_hello function is the critical section. Mutual exclusion is where access to some resource is limited to a set number of processes. In some instances it may be desired to limit access to the resource to just one process/person. In other instances you may wish to limit access to a set number of processes/people. The following reading introduces the requirements for implementing a critical section and also examines some attempts to solve the critical solution problem.

We've already seen previously one solution to the critical section problem, the use of the atomic keyword in BACI. However, the atomic keyword is not available in every language so we have to look for other solutions. Some of these solutions can be difficult to understand how they do (or don't) work. It may be helpful to listen to the online lecture slides for this section.
Reading 4.2 Textbook, Chapter 7, Section 7.2

Hopefully the attempts to solve the critical section problem, to implement mutual exclusion, have demonstrated to you how difficult it is to correctly write concurrent programs which cooperate or share resources. Simply running the programs and seeing what happens is not sufficient. You may run a set of programs hundreds of times without noticing a problem caused by a race condition. This is one of the reasons you must get into the habit of reading through your programs and testing them on paper.

The Bakery Algorithm


The following is the copy of an email sent to the COIT13152 mailing list during 1999 which attempts to offer further explanation of the bakery algorithm. One of the software-based solutions to the mutual exclusion problem.

Email Question
In a tute I ran today there seemed to be some common problems with understanding the Bakery Algorithm. I'm hoping that the following might make it a little easier. The algorithm is meant to be similar to the approach used in bakeries, delis and other stores where you take a number as you enter the serving person calls out the next lowest number The implementation goes something like this (based on the textbook, (pp 162-163)
choosing : array [0..n-1] of boolean;

This simply means that for each process there is an entry in an array. That entry is either TRUE or FALSE (that's what boolean variables do). The 0..n-1 indicates every process. We are assuming that there are n processes in the system.
Number: array [0..n-1] of integer

Again, we have an array with an element for each process.

What are these things used for


The choosing array is used to indicate whether or not the process is currently choosing its number. The number array is used to contain the number each process has been allocated

What happens
The entry into a critical section for a new process using the bakery algorithm then uses the following steps 1. choose the number of the new process 2. don't enter the critical section until the process's number is the smallest allocated number Hopefully you can now see why it is called the bakery algorithm Isn't this exactly how the "real bakery algorithm" works in normal life?

Choosing the number


Remember the first thing you have to do when you enter the bakery is choose/get your number. In the bakery algorithm this is done by the following bit of code
choosing[i] := true; number[i] := max( number[0],number[1].....number[n-1] ) + 1; choosing[i] := false;

The first step is that the process records that it is about to start choosing. It does this by making choosing[i] equal to TRUE. I is the process's unique number/identifier Now it can choose the number. It does this by finding out what the biggest number any process has currently be allocated. That is what the max( number[0], number[1]...number[n-1] ) function is meant to do. Look at all the numbers which have been allocated to processess and find the biggest one. The number of the new process (i) is then the current biggest number PLUS 1. So it would appear that the number of the new process is always going to be bigger than the previous ones. Actually due to problems with concurrency all you can say is that the number of the new process will be at least the same, i.e never smaller than any of the other numbers which have already been allocated. Once the new process has been allocated it's number it sets choosing[i] to false.

Wait until we have the smallest number


The idea is that each process (person) must then wait until they have the smallest number. That is what the next section of code tests
for j := 0 to n-1 do begin while choosing[j] do no-op; while number[j] <> 0 and (number[j],j) < (number[i],i) do no-op; end;

What this does is it checks each process


for j := 0 to n-1 do

remember that all proesses have a unique id which must be between 0 and n-1. In the following remember that process i is the current process which is asking to enter its critical section process j is the process that process i is currently checking on Process i is checking that all the other processes don't have smaller numbers than it. If process j is currently in the act of choosing its number then loop around doing nothing until it has chosen
while choosing[j] do no-op

The next while loop is a little more complex, mainly because it has this special construct
( number[j], j ) < ( number[i], i )

we can actually re-write this as


( number[j] < number [i] ) || ( number[j] == number[i] ) && ( i < j )

This means that is is only TRUE if the number allocated to process j is less than that allocated to process i OR the number allocated to process j is equal to that allocated to process i AND the process identifier of i is less than j The idea is simply to loop around forever if you find a process which has a lower number than process i and that process isn't process i (remember the for loop checks all process including process i).

Why not do it twice


But what happens if after we've checked processes they arrive and

are allocated a new number. Don't we have to go back and check again? Actually, no. We can be assured that this won't happen as any newly allocated numbers will be at least the same or bigger than the number that has been allocated to our current process.

Hardware solutions
As you can see from the previous reading implementing a solution to the critical section problem that meets the three requirements using standard programming constructs is difficult to do correctly. For the remainder of this week and next week we will look at synchronisation tools that are sometimes provided by hardware, operating systems and programming languages. We start with special hardware instructions.
Reading 4.3 Textbook, Chapter 7, Section 7.3

BACI's support
It is possible to create a Test and Set like function within BACI by using the atomic keyword. You will find a BACI program which implements test and set on the COIT13152 Web site and CD-ROM on the example BACI program page. Take a look at this example program. Compile and run the program to see if it works. Why do we do this? Well it helps provide you with an opportunity to use the test and set instruction.
Question Below this question you will see BACI code for a Test and Set function. This Test and Set function will not work. Can you figure out why? // a non-working Test and Set function written using

// BACI atomic int Test_and_Set (int target) { int test; test = target; target = 1; return target; }

Semaphores
The following reading introduces an important synchronisation tool, semaphores. Semaphores, or close relations to semaphores, are available on many of the modern operating systems.

Reading 4.4 Textbook, Chapter 7, Section 7.4

Actual Implementation
Operating systems that support semaphores will provide system or library calls that implement the necessary semaphore operations (initialise, signal and wait) in a safe manner. These library routines simply act as interfaces to calls into the kernel, where there are special routines that implement one or other of the above low-level mechanisms. Thus they hide the actual details, from the user, and also provide a standard interface across different types of computer, which may implement different low-level mechanisms.

Advantages
Semaphores are simple, quick and easy to use.

Disadvantages
Semaphores leave all of the logic of ensuring that all the different processes behave properly, to the programmer. Every time a process wishes to use a global resource, the programmer has to explicitly protect the critical section with semaphore functions. Ensuring that the right semaphore(s) are called and in the right sequence requires a good understanding of what is going on. Getting the sequencing wrong or simply forgetting to make one semaphore call (or even calling the wrong semaphore) can have disastrous effects. Note that an incorrectly coded program need not be the one that crashes. It could carry on quite happily, while causing other programs to crash. Determining which program is at fault is, consequently, not a trivial matter. Similarly, the problem caused by incorrect logic need not occur every time a program is run. In serial programming, if a program is given exactly the same data every time it is run, then it will perform in exactly the same way every time, including crashing. The same is not true for a parallel program. If the order in which the parallel parts run is slightly different then the error need not occur!

BACI's support for semaphores


BACI includes support for both binary and counting semaphores. This support is provided via two new data types binarysem Used to declare binary semaphores e.g.

binarysem

mutex;

semaphore Used to declare counting semaphores e.g. semaphore empty; BACI also provides three functions which can be used on semaphores initialsem This function is how you set the initial value of a semaphore initialsem( mutex, 1 ); initialsem( empty, 10 ); wait The traditional semaphore wait operation, e.g. wait( mutex ); signal The traditional semaphore signal operation e.g. signal( mutex );

Email Question
> I have a couple of questions from week 4 (I know I am > lagging) > 1) Is there any difference between 'spilocks' and 'busy > waiting' ? Busy waiting is where the CPU is looping around doing nothing useful until a certain event occurs e.g. while ( ! empty ) ; Spinlocks, are a form of semaphore that uses busy waiting to implement its operations, particularly the wait. e.g. wait( S ); while ( S <= 0 ) ; S-So spinlocks use busy waiting. But hang on!! Haven't we been telling you that busy waiting is bad. It is. So why is it okay for some systems to implement semaphores this way? Busy waiting should be avoided, if there isn't a good reason to. One reason might be that putting a process to sleep, blocking a process, will result in a process switch. We all know by now that a process switch takes an awful long time to happen. So if the lock is only going to be held for a little while a bit of busy waiting may be more efficient. Another, slight difference, is that busy waiting with a semaphore is actually implemented as part of the operating system.

Writing Programs with Mutual Exclusion


Here are a few guidelines for writing programs that use semaphores to implement some form of mutual exclusion identify the critical sections, identify the number of processes allowed inside the critical sections, declare an appropriate number of semaphores and initialise them to the correct values surround each critical section with a Wait/Signal pair

For example
As an example of using semaphores using mutual exclusion lets revert back to our say_hello program and go through the steps outlined above. Okay, let's work through the steps Identify the critical section. The critical section is usually wherever the shared resource is used. In this program the shared resource is the screen so the critical section is the instruction cout << "hello from process " << id << endl; Identify the number of processes allowed inside the critical sections. Most of the time you only want 1 process to be inside the critical section. However there are situations where you can allow more than 1. How many processes you allow in depends on how many instances of the shared resource you have. In our example program the shared resource is the screen. Most computers only have one screen so we can only allow 1 process into the critical section. Declare an appropriate number of Semaphores and initialise them semaphore mutex; initialsem( mutex, 1 ); surround each critical section with a wait/signal pair wait( mutex ); signal( mutex ); Performing these tasks leaves us with the following source code for our new program.
//************************** // A simple BACI program used to demonstrate

// implementing mutual exclusion with a semaphore // // Used in study guide for COIT13152 // // Author: David Jones semaphore mutex; void say_hello (char id) { wait( mutex ); cout << "hello from process " << id << endl; signal( mutex ); } main() { initialsem( mutex, 1 ); cobegin { say_hello( 'A'); say_hello('B'); say_hello( 'C'); } cout << " all finished " << endl; }

If you compile and run this program under BACI you should see output something like the following.
[david@faile baci]$ bainterp mutex Executing PCODE ... hello from process C hello from process A hello from process B all finished [david@faile baci]$ bainterp mutex Executing PCODE ... hello from process A hello from process C hello from process B all finished

As you can see it has solve the critical section problem with our sample program.

An Example
It's time for you to put some of this into practice

Process Synchronisation
Process synchronisation is where one or more processes are forced to wait (synchronise) while another process performs some event.

This usually involves a simple form of inter-process communication. Our say_hello program is an example of where you might wish to implement a form of process synchronisation. Even with mutual exclusion implemented, using either the atomic keyword or functions, we still have a race condition of sorts. If you look back at the output of the mutex program (the say_hello program with semaphores implementing mutual exclusion) you can see that the order in which the messages appears is different each time you run it. It's still in a race condition. How would we change the program to make sure that process C was always the last process executed? In other words C should not start until after both B and A have finished. This is an example of process synchronisation. We want process C to synchronise with the end of processes A and B. The first step is to change the program slightly to make it easier. The source code of the new program follows. As you can see the main difference between this example and the previous program is the addition of the functions a, b, and c. These have been added to make it easier to add process synchronsiation.
void say_hello (char id) { cout << "hello from process " << id << endl; } void A( void ) { say_hello( 'A' ); } void B( void ) { say_hello( 'B' ); } void C( void ) { say_hello( 'C' ); } main() { cobegin { A(); B(); C(); } cout << " all finished " << endl; }

Implementing Process Synchronisation


In the case of process synchronisation semaphores are used as a simple form of inter process communication (communication

between processes). It works by the process waiting for an event doing a wait on a semaphore and the process performing the event doing a signal on the same semaphore. The two processes are communicating via a semaphore. A simple recipe for using a semaphore to implement process synchronisation includes the following steps. Identify all the events on which you want to implement process synchronsiation. Declare one semaphore for each event. Initialise the value of the semaphore to 0. This indicates the event has not occurred yet. So any process wanting to wait until the event occurs will block until the event occurs. The processes which are waiting for the event perform a wait operation on the semaphore. The process that carries out the event performs a signal operation for each semaphore waiting for the event. This means that if you have five processes waiting on an event you will need to signal on the semaphore five times.

The Example
We want to modify our say_hello program so that process C is always the last process to display its message. Following the recipe from above we do the following Identify all the events on which you want to implement process synchronsiation. We actually have two events with which we want to synchronise process C with. The two events are process A finishing and process B finishing. Declare one semaphore for each event. We need two semaphores, as always it is a good idea to use identifiers with meaningful names. semaphore A_finish, B_finish; We could actually use either binary or counting semaphores here binarysem A_finish, B_finish; Initialise the value of the semaphore to 0 (this indicates the event has not occurred yet). Pretty straight forward initialsem( A_finish, 0 ); initialsem( B_finish, 0 ); The processes which are waiting for the event perform a wait operation on the semaphore. The process which is waiting on the two events is process C. So at the start of process C we must insert wait( A_finish );

wait( B_finish ); The process that carries out the event performs a signal operation for each semaphore waiting for the event. We have two events (the end of process A and the end of process B) performed by two different process. So we insert the following at the end of process A signal( A_finish ); and insert the following at the end of process B signal( B_finish ); The following contains the full source code for the solution. There is a page on the COIT13152 Web site and CD-ROM which contains this source code. You can obtain that source code and run it from within BACI to make sure it does work. When you execute the program you should see that process C should always be the last process to display its message. Either process A and B will display their message first.
//************************** // A simple BACI program used to demonstrate // process synchronisation using semaphores // // In this example process C must do no work until // both process B and process A finish // // Used in study guide for COIT13152 // // Author: David Jones // we have to keep the mutex semaphore to prevent // the race condition discussed earlier semaphore mutex; semaphore A_finish, B_finish; atomic void say_hello (char id) { wait( mutex ); cout << "hello from process " << id << endl; signal( mutex ); } void A( void ) { say_hello( 'A' ); signal( A_finish ); } void B( void ) { say_hello( 'B' ); signal( B_finish ); } void C( void )

wait( A_finish ); wait( B_finish ); say_hello( 'C' );

} main() { initialsem( mutex, 1 ); initialsem( A_finish, 0 ); initialsem( B_finish, 0 ); cobegin { A(); B(); C(); } cout << " all finished " << endl; } Question (optional) Use semaphores and process synchronisation to modify our simple say_hello program so that process C is always first to display its message.

Classical Problems
To reinforce the problems and difficulty of writing co-operating, concurrent programs it is traditional to look at a number of classical mutual exclusion problems. These problems demonstrate most of the standard uses of mutual exclusion and synchronisation. Why is it important to know about these classical problems? Most of the problems you face in implementing syncrhonisation of mutual exclusion in concurrent programs will be very similar to one of these classical problems. If you understand these, and more importantly understand their solutions, you will find solving your problems much easier.
Reading 4.5 Textbook, Chapter 7, Section 7.5

Summary
The aim of this week was to introduce you to the problems created when you have concurrent processes (processes which are running at the same time) which wish to share resources. One of the common problems created is called a race condition. To solve the problems with co-operating, concurrent processes it is necessary to implement some form of mutual exclusion,

particularly around sections of code called critical sections. Implementing mutual exclusion with standard programming constructs, while possible, is extremely difficult. Instead special primitive or tools are provided by hardware, the operating system or occasionally a programming language. Two of these primitives were introduced this week atomic keyword This is a keyword understood by BACI. It is unlikely to be available in other languages. It makes a function indivisible, i.e. it implements mutual exclusion. special machine instructions Special hardware implemented machine instructions which are guaranteed to be atomic such as test-and-set and swap (sometimes called exchange) semaphores Usually provided by the operating system, semaphores are a special software construct with three valid operations: initialise, wait and signal.

Review Questions
Exercises Textbook, Chapter 7 1, 5, 6, 7, 13

More problems
The following section contains a number of synchronisation problems. You never really understand the problems of concurrency, mutual exclusion and process synchronisation until you have implemented a number of programs which use these concepts. The more you practice the better. Solutions to these problems are included at the end of this chapter.

The bathroom problem


A co-ed dormitory at a boarding school has only the one bathroom to share between all the little boys and girls. The rules that govern who can occupy the rest room can be summarised as follows at most some MAXIMUM number of girls, or at most some MAXIMUM number of boys, but not any combination of boys and girls at the same time girls must be given a higher priority than boys in using the bathroom

boys can only enter the bathroom when there are no girls waiting for or using the bathroom. Assume that MAXIMUM is a an integer constant that can change, before a boy can enter the bathroom he executes a function called boy_wants_to_use_bathroom and when he leaves the bathroom he executes the function boy_leaves_bathroom, before a girl can enter the bathroom she executes a function called girl_wants_to_use_bathroom and when she leaves the bathroom she executes the function girl_leaves_bathroom.
Question In answering the question you should 1. Describe the conditions for boys and girls to enter the bathroom in English. 2. Compute the minimum number of semaphores needed to solve the problem and describe the meaning and purpose of each semaphore in English. 3. Provide the necessary code for the four functions used by the boys and girls using semaphores to enforce the above rules

The bird feeder


A keen bird lover has built a feeding shed for his birds. The shed has many doors spread around the shed. Each door has a button outside and a button inside the door. There is only room for three birds in the shed at any one time. The birds are trained to press the buttons when they wish to enter or when they wish to leave the shed. Also the birds are trained so that only one bird will enter when a door opens. When the buttons are currently pressed the functions listed below are executed.
// Global shared variables int birds_in = 0; bird_wants_to_enter( int button) { if ( birds_in < 3 ) { birds_in++; open_door( button ); } } bird_wants_to_leave( int button )

{ } Question

birds_in--; open_door( button );

The above code is wrong and suffers from a number of problems. List some of the problems that this system might suffer from. Assuming the existence of a class Semaphore that has member functions for all the valid semaphore operations. Modify the above procedures to solve these problems.

Solutions to programming problems


The following are sample solutions to the above problems.

Bathroom
Part A
A boy may enter the bathroom only if there are no girls waiting to enter, there are no girls using the bathroom (girls get preference), the number of boys using the bathroom is less than the maximum allowed in Girls may enter the bathroom only if there are no boys using the toilet the number of girls using the bathroom is less than the maximum allowed in

Part B
The minimum number of semaphores needed is three. We need to use various global variables to keep a track of how many girls/boys are using the bathroom and how many boys/girls are waiting to use the bathroom. These global variables will be used by the various functions to calculate who can and cant be allowed to use the bathroom. Mutual Exclusion To make sure no errors occur access to these global variables must be done in a safe manner. This means we wish to restrict the number of processes accessing or modifying the global variables. Therefore we need one semaphore to implement mutual exclusion on access to the global variables.

Process Synchronisation There will be two cases where we will wish to block particular processes until some event occurs. These include blocking girl process who are not allowed into the bathroom until one of the two conditions outlined in (a) are fulfilled, and blocking boy processes that are not allowed into the bathroom until one of the three conditions outlined in (a) are fulfilled. Two events mean we need two semaphores.

Part C
The source code below is in a pseudo code which is very much like C. It is NOT BACI code.

/* Global Declarations */ Semaphore mutex_sem( 1 ), boys_waiting_sem( 0 ); Semaphore girls_waiting_sem( 0 ); int boys_waiting=0, boys_using=0; int girls_waiting=0, girls_using=0; /***************************** * FILE: boys.c *****************************/ void boy_wants_to_use_bathroom() { /*wait until can get sole use of bathroom data structure */ Mutex_sem.Wait(); /* a boy is allowed in if, there is room, no girls are waiting, and no girls are using */ if ( (boys_using == MAX_BOYS_USING) || girls_waiting || girls_using ) { /* boy not allowed in */ boys_waiting++; /* release hold on data structure */ Mutex_sem.Signal(); /* wait at door for someone to leave */ Boys_waiting_sem.Wait(); } else { /* boy is allowed in */

boys_using++; /* release hold on data structure */ Mutex_sem.Signal(); } /* boys only get to this point if they are allowed to enter the bathroom */ } /******************************************* * void boy_leaves_bathroom( Bathroom *) *******************************************/ void boy_leaves_bathroom() { /* gain sole hold on bath data structure */ Mutex_sem.Wait(); /* let boy leave */ boys_using--; /* check for girls first, they have preference */ /*girls allowed in if :- girls waiting, and no boys using*/ if ( (girls_waiting>0) && (boys_using==0) ) { /* let in as many girls as will fit */ while ( (girls_waiting>0) && (girls_using! =MAX_GIRLS_USING)) { girls_waiting--; girls_using++; girls_waiting_sem.Signal(); } } else if ( (boys_waiting>0) && (girls_waiting==0)) { /* there are some boys waiting and no girls waiting */ /* let a boy in */ /* no need to check it a girl is using, if there was */ /* the bathroom is in an incorrect state, this is /* because a boy has just left, and yet there are /* girls in the bath */ boys_waiting--; boys_using++; boys_waiting_sem.Signal(); } else { /* no-one can come in */ cout << "not letting anyone in. << endl;

} /* release hold on bath data structure */ Mutex_sem.Signal();

/***************************** * FILE: girls.c *****************************/ /* void girl_wants_to_use_bathroom( Bathroom *) * - called when a girl wishes to use bathroom * - they should only be allowed if specified rules are met *******************************/ void girl_wants_to_use_bathroom( Bathroom *bath) { /*wait until can get sole use of bathroom data structure */ Mutex_sem.Wait(); /* a girl should be allowed in if there is room */ /* and if no boys are using */ if ( (girls_using == MAX_GIRLS_USING) || (boys_using>0) ) { /* there isn't room*/ girls_waiting++; /* release hold on data structure */ Mutex_sem.Signal(); /* wait at door for someone to leave */ P( girls_waiting_sem );

} else { /* there is room */ girls_using++;

} }

/* release hold on data structure */ Mutex_sem.Signal();

/******************************************* * void girl_leaves_bathroom( Bathroom *) * - updates shared memory to remove girl from bathroom *******************************************/ void girl_leaves_bathroom( Bathroom *bath ) {

/* gain sole hold on bath data structure */ Mutex_sem.Wait(); /* let girl leave */ girls_using--; /* any girls waiting get preference */ if ( girls_waiting>0 ) { /* let a girl in */ girls_waiting--; girls_using++; girls_waiting_sem.Signal(); } else if ( boys_waiting && !girls_using) { /* boys are waiting, no girls are waiting, no girls are using let as many boys in as possible */ while ( (boys_waiting>0) && (boys_using < MAX_BOYS_USING)) { /* let them in */ boys_waiting--; boys_using++; boys_waiting_sem.Signal(); } } /* release hold on bath data structure */ Mutex_sem.Signal();

Birdfeeder
Part A
Problems with the bird feeder problem include birds must press the button repeatedly if they are not let in, if multiple birds attempt to leave and enter at the same time the update of the birds_in variable will be done incorrectly (a race condition)

Part B
The solution is to use a single counting semaphore to count the number of birds inside the feeder.
// Shared Variables Semaphore space_free(3); bird_wants_to_enter( int button)

{ }

space_free.wait(); open_first_door( button );

bird_wants_to_exit() { open_second_door( button ); space_free.signal(); }

Chapter 5 Process synchronisation: monitors

Introduction
In the last chapter you were introduced to the critical section problem, mutual exclusion, synchronisation and a number of tools including semaphores. In this chapter you will be introduced to a number of higher-level synchronisation tools designed to address some of the problems with lower-level tools such as semaphores. These higher-level tools include critical regions, monitors and atomic transactions. The material covered in this chapter is supplemented by online lecture number 6 which includes audio.

Objectives
The major objectives of this week are to gain an understanding of higher-level solutions to the critical section problem including monitors to understand the principal differences between semaphores and monitors

Resources
Textbook Chapter 7 Study Guide Chapter 5 Online lecture 6 The BACI system (optional)

Critical Regions
The following reading provides an overview of some of the problems with synchronisation tools such as semaphores and then introduces one higher-level solution, critical regions. This reading is optional and the material on critical regions is not examinable.

Reading 5.1 Textbook, Chapter 7, Section 7.6

Monitors
Monitors are a common high-level synchronisation tool which solve some of the problems associate with semaphores. The following reading introduces you to the use and implementation of monitors.
Reading 5.2 Textbook, Chapter 7, Section 7.7

BACI's support for monitors


BACI includes support for monitors. The following program is a modification of the say_hello program which uses BACI monitor support to implement mutual exclusion.
//************************** // A simple BACI program used to demonstrate // the use of monitors to implement mutual exclusion // // Based on programs used in chapters 2, 3, and 4 // of the COIT13152 study guide // // Author: David Jones monitor simple_monitor { void say_hello (char id) { cout << "hello from process " << id << endl; } } void A() { say_hello( 'A' ); } void B() { say_hello( 'B' ); } void C() { say_hello( 'C' ); } main() {

cobegin { A(); B(); C(); } cout << " all finished " << endl;

As you can see, implementing mutual exclusion with a monitor is quite simple. The output of running this program under BACI demonstrates the mutual exclusion has been implemented correctly
[david@faile baci]$ bainterp monitor Executing PCODE ... hello from process A hello from process C hello from process B all finished [david@faile baci]$ bainterp monitor Executing PCODE ... hello from process C hello from process A hello from process B all finished [david@faile baci]$ bainterp monitor Executing PCODE ... hello from process A hello from process C hello from process B all finished

Monitors are actually a much nicer way of implementing mutual exclusion than semaphores. One of the reasons for this is that the code which implements mutual exclusion is all in the one place, in the monitor. With semaphores that code can distributed all over the place in the form of wait and signal semaphore function calls. Additionally it's next to impossible to setup a monitor incorrectly. On the other hand with semaphores it is quite common to do a wait( B ) when you should have done a wait( C ). Simple little mistakes are easy to make with semaphores.

Process Synchronisation with Monitors


Process synchronisation with monitors is implemented in much the same way as it is implemented with semaphores. However, with monitors you use condition variables rather than semaphores. For this reason it is important that you realise the difference between semaphores and condition variables. This is made more difficult because

both semaphores and condition variables both use wait and signal as the valid operations, the purpose of both is somewhat similar, and they are actually quite different. The main difference is the operation of signal. With a semaphore the signal operation actually increments the value of the semaphore. So if there aren't any processes blocked on the semaphore the signal will be "remembered" by the incrementing of the semaphore. The signal function on a monitor's condition variable is different. If there are no processes blocked on the condition variable then the signal function does nothing. The signal is not remembered. In order to remember "empty signals" you have to use some other form of variable. The good part of this is that using other variables within in monitor is simple because we can be assured that mutual exclusion is being implemented. The following code gives an example of this problem. It is a modification of the previous BACI program which uses a condition variable to make sure that process C always goes last (just like the semaphore program in the last chapter). The program is a good example of a number of important monitor related factors. It's explained in more detail below. Only the lines in the monitor are numbered. I assume that by now you are familiar with the rest of the say_hello program.
1 2 3 4 5 6 7 8 9 3 10 monitor simple_monitor { int finish; condition C_wait; void say_hello (char id) { if ( id == 'C' ) { if ( finish != 2 ) waitc( C_wait ); } cout << "hello from process " << id << endl;

if ( id != 'C' ) { 11 finish++; 12 // cout << "id is" << id << " and finish is " << finish << endl; 13 if ( finish == 2 ) 14 signalc( C_wait ); } }

15 16

// initialise the integer values to 0 init { finish = 0; } } void A() { say_hello( 'A' ); } void B() { say_hello( 'B' ); } void C() { say_hello( 'C' ); } main() { cobegin { A(); B(); C(); } cout << " all finished " << endl; }

Line 1 simply declares the monitor. In BACI the monitor declaration must be at the top level. You can't nest monitors. Lines 2 and 3 define the variables we are going to use in the monitor. The integer finish is going to be used to indicate when both process A and process B are finished. To start with finish will be equal to 0. Every time process A or process B finishes say_hello they will increment finish. C_wait is a condition variable. It will be used to block process C until both A and B are finished. Line 5 is the start of the say_hello function. It is implemented within the monitor to ensure mutual exclusion. Since it is declared within the monitor only one process can ever be executing the function at any one time. Line 6 is where we start the process syncrhonisation. If id is equal to C then process C is executing within say_hello. If this is the case we do some extra testing (Lines 7 and 8) before displaying the message. We only want process C to display the message if both process A and B have already finished. This is where Lines 7 and 8 come in. The variable finish will only be equal 2 if both A and B have already finished. If finish is not equal to 2 then process C should block until they have. This is were the waitc function on the condition variable C_wait comes into it. Line 9 simply displays the appropriate message.

Line 10 is the start of the checking which we must do for processes A and B. Remember, once A and B have finished we can let process C start. So after the message we must perform some steps to check whether we can do this. Line 11 increments the variable finish. This is only done for processes A and B. Once finish is equal to 2 this means that it's okay for process C to proceed if it hasn't already blocked. If it has blocked already we will need to wake it up. Line 12, which is commented out is a debugging message which was used during the development of the solution. Placing appropriate cout messages throughout your BACI programs is a major way of debugging . Lines 13 and 14 is where we restart process C if it has already blocked. First we check if it is okay to unblock process C (line 13) by making sure that finish is equal 2. If it is we can then perform a signal on our condition variable, C_wait. Remember, if process C has not already blocked then C_wait will have no processes waiting for the signal. This means the signal does nothing. This is different from the signal operation for a semaphore. Lines 15 and 16 show another unique feature of BACI's support for monitors, the init function. The purpose of this function is to be executed onces (and once only) when the monitor is first used. As the name suggests the init function (it is always called that) is used to initialise variables within the monitor. We use it in our example to make sure that the finish variable is initially set to 0 (implying that neither A or B have finished yet).
Question (Optional) Modify the monitor solution to process syncrhonisation so that process C is always the first process to display its message rather than the last.

OS Synchronisation
This reading is optional and the material on OS synchronisation is not examinable. That is we won't ask you in the final exam to describe how synchronisation is implemented in the Solaris 2 operating system. It is however useful to read to gain insight into how these factors are implemented in a real system.
Reading 5.3 Textbook, Chapter 7, Section 7.8

Atomic Transactions
This reading is optional and the material on atomic transactions

is not examinable. Critical sections and mutual exclusion are very useful and are essential to most modern computer systems. However, they are not enough. What happens if when I deposit some money into my bank account it only gets halfway through and the process dies. What happens? The following reading provides more background on this problem and introduces a number of ways the problems can be dealt with. Those of you who have done any work with databases may be familiar with this topic.
Reading 5.4 Textbook, Chapter 7, Section 7.9

Summary
Reading 5.5 Textbook, Chapter 7, Section 7.10

Review Questions
Exercises Textbook, Chapter 7 11, 12, 13, 14, 16, 17, 18, 19, 20

Chapter 6 Deadlocks

Introduction
The last two chapters have concentrated on solving the critical section problem using mutual exclusion. Implementing mutual exclusion, regardless of the primitive used, helps to contribute to a new problem, deadlock. This chapter examines the problem of deadlock in more detail.

Objectives
By the end of this week you should know what deadlock is be able to describe the four necessary conditions for deadlock to occur be familiar with deadlock avoidance, detection, recovery and prevention mechanisms

Resources
Textbook Chapter 8 Study Guide Chapter 6 Online lecture 7

What is Deadlock?
Deadlock is an extremely serious problem, where two (or more) processes wait forever for the resources they require The following is a simple BACI program which can demonstrate deadlock in action. You dont really need to understand BACI to get the point: all that we are doing here is running two functions first and second. First waits on a semaphore called a, then on b; whereas second waits on b, then a. a nd b could equally well be tape units, or printers, or plotters, or any non-preemptible resource.
semaphore a, b;

void first() { wait( a ); wait( b ); cout << "hello first " << endl; signal( a ); signal( b ); } void second() { wait( b ); wait( a ); cout << "hello second " << endl; signal( a ); signal( b ); } void main() { initialsem( a, 1 ); initialsem( b, 1 ); cobegin { first(); }

second();

Running this program a few times gets output like this


$bainterp simple Executing PCODE ... hello first hello second $bainterp simple Executing PCODE ... hello first hello second $bainterp simple Executing PCODE ... hello first hello second $bainterp simple Executing PCODE ... *** Halt at deadlock 15 in process 2 because of

Process Table Process Active atomic 0 main 0 0 1 first 1 0 2 second 1 0 Global Variables

Suspend -1 1 0

PC 37 4 15

xpc 9 7 8

type name level adr value sem b 0 1 0 sem a 0 0 0 Mainproc Variables Monitor Variables Process Variables Process #1 first Process #2 second Stack for main proc from 6 down to 0 6 -1 0 0 0 0 0 Stacks of concurrent processes Process # 1: first stack from 2405 down to 2401 4 2 0 0 0 Process # 2: second stack from 2605 down to 2601 5 2 0 0 0

In this last execution of the program we have deadlock. As you can see this program is only going to suffer deadlock if a certain sequence of events happens. This sequence of events won't happen very often, but when it does, it crashes the program. This is one of the problems with deadlock. It doesn't happen all the time. In many modern operating systems, the designers are aware that deadlock can occur, but ignore it because it is so difficult to handle or prevent it only happens very rarely and it isn't all that important for most computer systems if it crashes every so often With this program the sequence of events that leads to deadlock goes something like this process first goes first and finishes wait( a ) before there is a process switch At this stage b = 1 and a = 0 now process second runs and finishes wait( b ) before there is a process switch At this stage we now have b = 0 and a = 0 process first resumes and tries to do a wait( b ) Well b = 0 so process first must block because it has done a wait on a semaphore with value 0. Process first won't start again until another process does a signal on the semaphore b. process second resumes and tries to do a wait( a ) Well a = 0 also so process second must also block. This is a simple example of deadlock. Both processes are blocked, they won't do anything more, until they are woken up. However, the only way they can wake up is if the other process does something. But the other process can't do anything because

it is blocked. The following reading describes deadlock in more detail and how the operation of a computer system can lead to deadlock.
Reading 6.1 Textbook, Chapter 8, Introduction and Sections 8.1 and 8.2

BACI provides a rudimentary deadlock detection algorithm. The advantage BACI has over an operating system is that the only processes it is running are the ones associated with a single program and that program is the only one it has to execute. If BACI detects that there are no longer any processes ready to execute, and there are still processes in the system, it will halt with a deadlock message.
Question (Optional) Modify the example BACI program which suffers from deadlock so that instead of two processes which are deadlocked it has three processes which are deadlocked.

Handling Deadlock
The following reading describes some of the methods which can be used to handle deadlock. In particular it looks at deadlock prevention, avoidance, detection and recovery. None of these are without difficulty. Prevention attempts to lay down rules which all processes must follow this can lead to incredible inefficiency and wastage of resources. Avoidance does not lay down set rules, but has a system routine monitoring the current state at all times, in order to judge whether or not to grant resources to processes requesting them. Detection is difficult, and in any case, if necessary, means that we have already encountered deadlock. Recovery deals with what to do when we discover the worst has happened, and is always fraught with problems. Almost always processes have to be cancelled, and often files and databases may have to be restored from backups before continuation is possible.
Reading 6.2 Textbook, Chapter 8, Section 8.3 to 8.7

Summary
Reading 6.3 Textbook, Chapter 8, Section 8.8

Review Questions
Exercises Textbook, Chapter 8 1, 2, 4, 5, 9, 13

Chapter 7 Memory Management

Introduction
One of the most important resources in a computer system is memory. Processes cannot run unless their code and data structures are in RAM. Memory management is amongst the most important, and is one of the most complex, tasks for an operating system. The following two weeks introduce you to the concepts and methods used by operating systems to manage memory. This week concentrates on introducing the basic ideas and methods and acts as the foundation for the more complex material introduced in the next chapter.

Objectives
By the end of this week you should have an understanding of the aims of memory management concepts such as address binding, dynamic loading, dynamic linking, swapping and overlays the relationship between logical and physical addresses simple memory management algorithms such as fixed and dynamic multi-partitioning and the related placement algorithms (first, best and worst fit) external and internal fragmentation the operation of paging, segmentation and combined paging/segmentation

Resources
Textbook, Chapter 9 Study Guide Chapter 7 Online lecture 8

Background
The premise and problem that causes the need for memory management includes an operating system manages many processes the code and data for a process must be in RAM before it can run processes must not be able to access, without permission, the code and data of other processes processes must be able to access and share the code and data of other processes if they have permission there is usually not enough RAM to hold the code and data for all the currently running processes in RAM How memory management deals with these problems is described in the readings over the next couple of weeks. Memory management is about sharing memory so that the largest number of processes can run in the most efficient way. Before going any further, the following reading introduces some of the basic concepts and methods used in memory management.
Reading 7.1 Textbook, Chapter 9, Introduction and Section 9.1

Address binding, i.e. allocating instructions to actual physical locations in RAM, can be done at three different times in the life cycle of a program Compile time Done by the compiler and not that adaptable. Operating system simply loads the program into those locations. Load time Done by the operating system as it is creating the new process, reasonably adaptable. Execute time While the program is being executed hardware performs the address binding (this is called virtual addressing which is explained later).

Dynamic Linking
Many of you should by now have heard or seen while using your Windows system the phrase dynamic link library (DLL). Most modern operating systems (Windows and UNIX) make use of dynamic linking. One of the major reasons for that has been the almost universal use of graphical user interfaces (GUIs). Most GUI programs use the same set of libraries to provide the code necessary to implement the buttons, icons and text boxes which are necessary for a GUI application. These libraries are very

large. If every GUI program was statically linked to these libraries, i.e. had its own copy of the code for the GUI functions, it would be enormous. As a result you would run out of RAM and disk space even quicker than you do now. To give you an example of this take the following very simple C+ + program.
#include <iostream.h> void main() { cout << "hello world" << endl; }

A very simple program. The Gnu C++ compiler, the standard open source compiler on most UNIX systems (also available for Windows), has an option which allows you to specify whether or not you want to use static or dynamic linking. Most modern compilers provide this option. In the following I have compile this simple C++ program using both static and dynamic linking. Then I have a look at the size of the executable program. Do you see the difference?
[david@faile baci]$ g++ simple.cc -o simple [david@faile baci]$ g++ -static simple.cc -o simple_static [david@faile baci]$ ls -l simple simple_static -rwxrwxr-x 1 david david 5356 May 27 08:24 simple -rwxrwxr-x 1 david david 728496 May 27 08:24 simple_static

The static executable is over 700 kilobytes in size, compare to the dynamic executable which is just over 5K. The static executable is 140 times larger than the dynamic executable.

Why static?
If static executables take up so much space then why would you want to use a static executable rather than a dynamic executable? Well the main reason is that dynamic linking is only useful if the person who will run your executable program also has a copy of the dynamic link library(ies) that your program needs in order to run. If I give you a dynamically linked executable you can't run it unless you have the right library. In some cases you may also have problems if you don't have the right version. Many of you will have already suffered problems with versions of dynamic link libraries on Windows.

Logical and physical addresses


The concepts of logical (sometimes called virtual) and physical

memory addresses is central to most of the memory management schemes you will see over the next two weeks. It is important that you understand the fundamental concepts of logical and physical addresses. The basics are The program only ever uses logical addresses for code and data These are very easy to generate and usually start at 0 and go up. The memory management unit (MMU), a piece of hardware, will during execution translate the virtual address into a physical (or real) address in RAM. The advantage of this is that when you produce a program it can assume that it is contiguous (all the code and data is one big block). However, in reality bits of the program can be stored all over the place in RAM (and other places as you will see next week). The hardware will take care of the difficult parts.

Email Question
> In the text ... it says that > one of the simplest methods for memoty allocation is > to divide the memory into a number of fixedsized > partitions that can contain a maximum of one > process. This is called fixed multi-partition memory management. A fairly ancient, simple to implement memory management scheme which is no longer used. However, it is useful to be aware of it to know the evolution of these schemes > This section then goes on to explain how memory is > allocated to processes if the memory area is big > enough, and how holes are combined if they are > adjacent. This is called dynamic multi-partition memory management. Rather than have fixed partitions which never change over the life of the system (fixed multi-partition memory management) you can change the number and size of partitions in response to need. > I read this section as talking about two different > types of memory management. > Could someone tell me if I am right or wrong. Correct.

Swapping
In multiprocessing systems it is not always possible to have the code and data for all executing processes in RAM. Swapping, discussed in the following reading, is a rather coarse method for

solving this particular problem. The next chapter of the study guide introduces another solution to this problem, virtual memory
Reading 7.3 Reading, Chapter 9, Section 9.2

Contiguous Memory Allocation


Rather than dive straight into the complex memory management algorithms used in todays operating systems we are going to start with some old, simpler memory management algorithms. This allows you to understand the principles of memory management without getting bogged down with the complexities. Understanding these principles now will make it much easier next week when we look at some really complex memory management algorithms.
Reading 7.4 Reading, Chapter 9, Section 9.3

Paging and Segmentation


In the previous reading the assumption was that the memory belonging to a process had to be contiguous. If we use virtual addressing (where the hardware performs dynamic address binding) then a process doesn't have to be contiguous in RAM. The next reading talks about two algorithms which allow the operating system to implement this. In both of these schemes the memory of a process is broken up into blocks. In paging the blocks are all the same size, they are called pages. In segmentation the blocks can be different sizes, they are called segments.
Reading 7.5 Reading, Chapter 9, Sections 9.4, 9.5, and 9.6

Summary
Most (if not all) modern operating systems use some form of complex memory management algorithm which makes use of dynamic binding in the form of virtual addresses. This requires that the translation from virtual to physical address must occur at least once per instruction (sometimes it can be required three or four times per instruction). If this translation were to be done using software it would be the system very, very slow. This means that address translation must be done in hardware.

This week has introduced you to the basic concepts of memory management using some pretty simple memory management algorithms. Next week we build on this knowledge and take a look at the more complex virtual memory management algorithms which are common with todays computers.
Reading 7.6 Textbook, Chapter 9, Section 9.7

Exercises
Exercises Textbook, Chapter 9 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16.

Chapter 8 Virtual Memory Management

Introduction
The previous chapter concentrated on simple memory management algorithms which used real memory only. This chapter moves onto memory management algorithms which use virtual memory. It is essential to understand the operation of virtual memory as it is used in all modern operating systems. Some of the concepts, algorithms and data structures introduced in this chapter are reasonably complex and you may need to take some time to gain a full understanding.

Objectives
By the end of this week you should be familiar with the concepts and operation of virtual memory management be aware of the reasons, benefits and costs of using virtual memory management have knowledge of the operation of virtual memory management schemes including demand paging and segmentation be able to define when a page fault occurs, what happens during a page fault and why the number of page faults should be minimised be able to discuss the need for page replacement algorithms and describe a number of the more common algorithms be familiar with the rationale and approaches used in page frame allocation know what thrashing is and the steps which can be taken to reduce the problem be familiar with concepts such as pre-paging, the impact of page size, inverted page tables and I/O interlock

Resources
Textbook Chapter 10

Study Guide Chapter 8 Online lecture 9

Background
Virtual memory involves the use of secondary memory, such as hard disk space, to simulate primary memory, or RAM. Each process is broken down into blocks, all of which are brought into virtual memory, but only some of which will be in primary memory at any one time. If these blocks are all of the same size, the system is a paging system, and blocks are said to be paged in and paged out (if the blocks are of differing sizes, the system is said to use segmentation, rather than paging). When a process requires a page that is currently not in primary memory, a page fault occurs, and the page has to be brought in. This is handled by referring to the page table for the process. This is a control block which, at minimum, lists, for each page, whether it is currently in primary memory, and, if so, in which page frame it is located; its address in secondary memory; and a series of permission bits which indicate whether the process can read from, write to, or execute that page. If a page fault occurs, a real I/O operation will be required. Thus, it is likely that the operating system will move the process to the blocked queue, and run another process, while the page fault is handled. It is important to appreciate that a page fault, despite its name, is not really an error of any kind indeed, on most computer systems, page faults are likely to be occurring at the rate of several hundred per second!
Reading 8.1 Textbook, Chapter 10, Introduction and Section 10.1

Demand Paging
The next reading introduces demand paging. Demand paging is one of the most widely implemented virtual memory management algorithms.
Reading 8.2 Textbook, Chapter 10, Section 10.2

The principle of locality (called the locality model in the text) is important to memory management algorithms. The principle of locality is that the next memory reference for a process is likely to be very close to the last memory reference. This means that a process tends to access memory close to its current location. As it

progresses that location will change, for example when a different procedure is executed. The principle of locality is important to memory management because it means that you don't have to have all the code and data for a process in memory at any one time. You only really need the code and data immediately around the current memory location. If you have this then chances are that the next memory reference can be handled. This fact is used as the basis for implementing virtual memory management schemes.

Demand Paging Performance


Imagine yourself as the new Systems Administrator for a small company. The managing directory, the person who hires and fires people, decides that his computer is just too slow and that you must purchase him a new one from within your budget. Now being a typical Systems Administrator in a typical small company your budget doesn't allow you to do this. What can you do? After watching the managing director use his computer you notice that he has many copies of Word, Excel and Netscape open. It seems that he leaves his computer on all the time, never closes programs and always starts a new copy every time he needs an application. You proceed to kill off the extra copies, explain the problem and suddenly everything is okay and there is no need to buy a new computer. Why? Hopefully the next reading might help explain.
Reading 8.3 Textbook, Chapter 10, Section 10.3

Page Replacement
Eventually you will reach the stage where all the current page frames are being used Remember, RAM is divided into sections which are the same size as a page. These sections are called page frames. the current process needs to access a memory location on a page which is on the disk. In this situation you have to replace on of the current pages in RAM. But how do you decide which one? This choice can strongly influence the performance of your system.
Reading 8.4 Textbook, Chapter 10, Section 10.4

Frame Allocation
Page replacement decided which current page in RAM to replace. Frame allocation examines how many page frames each process should be allowed to have.
Reading 8.5 Textbook, Chapter 10, Section 10.5

Thrashing
Thrashing is bad. It's almost like busy waiting for disks. Thrashing is what goes on when you are running lots of large applications using lots of RAM and you hard-drive disk access light is almost continuously red and your drive is making lots of grinding noises. It's when the computer is doing almost nothing except to move pages into and out of RAM. Thrashing comes about when the page fault frequency is too high. The next reading introduces the concept of page fault frequency, thrashing and related concepts that can help address thrashing, including working sets.
Reading 8.6 Textbook, Chapter 10, Section 10.6

Other considerations
The following reading examines some operating system examples, and considers some other issues which may need to be taken into account.
Reading 8.7 Textbook, Chapter 10, Sections 10.7 and 10.8

Summary
Reading 8.8 Textbook, Chapter 10, Section 10.9

Review Questions
Exercises

Textbook, Chapter 10 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 19, 20

Chapter 9 File Systems

Introduction
In this chapter we examine the file system. The file system is the part of the operating system which implements long-term storage, typically using traditional concepts such as files and directories (you probably know them as documents as folders). This chapter examines the basic concepts associated with the file system, and the methods used to implement these concepts. You will find that the next few chapters of the COIT13152 study guide will require less work than previous weeks.

Objectives
By the end of this week you should understand the meanings of the terms file, directory, and partition, what each is used for, and know how each is protected be aware of the layers, from application programs down to devices, involved in the implementation of file systems be able to describe the algorithms and data structures used to break up and allocate files onto disks and their impact on performance have knowledge about the methods used to track free space on a disk be familiar with efficiency, performance and recovery considerations for file systems

Resources
Textbook Chapters 11 and 12 Study Guide Chapter 9 Online lectures 10 and 11

File Concept
The basic logical storage structure used by most operating systems

is the file. The next reading explains the basics of the file concept including what it is, what attributes are usually associated with a file, what operations can be performed on a file, what types of file there are and how files are structured. The user interface of many modern operating systems (such as Windows 95/98/NT and the MacOS) are tending to hide the (relatively low-level) concepts of files behind high-level metaphors such as documents.
Reading 9.1 Textbook, Chapter 11, Introduction and Section 11.1

An Alternate View
The previous reading is pretty dry and probably doesn't make a lot of sense. The following is an attempt to explain the same concepts a little differently, and hopefully a bit better. Maybe the two views in combination may help you understand the concepts. The following diagram is an attempt to give you an overview of the situation. The problem we have is that we have user programs which want to be able to store and retrieve data from long term storage. Long term storage on a computer system takes the form of hard-drives, tape drives, CD-ROMs, DVDs and other similar devices. The problem is that we don't want to have normal programmers having to worry about the intricate details of how to talk to each different type of device. The solution which has developed in operating systems is that of the file system. The file system is the section of the operating system which implements the file I/O related system calls. The file system interface is not a graphical user interface. It is the collection of system calls and data structures which you as a programmer can use to store and retrieve information from the disks and tapes connected to your system. When the previous reading talks about file attributes, operations, types and structure these are all part of the interface which operating system provides the application programmer.

Access Methods
By this stage, most of the programs written by computing students make use of very simple file access. You open a file, you read what you want, maybe skip some information, read some more stuff and then close the file. In other words file access is sequential. This is only one of the possible methods which can be used to access the contents of a file. The following reading introduces some of the others.
Reading 9.2 Textbook, Chapter 11, Section 11.2

It is important to realise that in the real world files can grow to be quite large especially in the fields of databases and multimedia. Some databases can grow at the rate of 10 Gigabytes a week.

Directories
Having many files all in one folder/directory can be quite overwhelming. It is necessary to have some other way to organise files to make management easier. The following reading introduces the concepts of directories and partitions, and introduces the concept of file sharing.
Reading 9.3 Textbook, Chapter 11, Sections 11.3 through 11.5

Protection and semantics


Older, more primitive operating systems for personal computers didn't have to worry about protection. On a computer running MS-DOS any one and any program can access and modify any file. Today's operating systems which support multi-tasking (some also support multiple users) must provide some form of file protection and also some form of semantics. The following reading explains what these concepts are and some of the possibilities.
Reading 9.4 Textbook, Chapter 11, Section 11.6

Summary
This chapter in the text has concentrated on the file system interface. The collection of services and data structures which the kernel provides to the application programmer so they can read and write information to/from long-term storage. Next, we move onto look at how these concepts are actually implemented.
Reading 9.5 Textbook, Chapter 11, Section 11.7

Review Questions
Exercises Textbook, Chapter 11 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13

File-System Structure and Implementation


The file system of most modern operating systems is implemented using a layered structure. In much the same way as communication protocols such as TCP/IP. This section tries to give you an overview of those layers.

Reading 9.6 Textbook, Chapter 12, Introduction and Sections 12.1 and 12.2

All that is probably about as clear as mud. Let's try to clear it up a touch. This is an attempt to reinforce the benefits of the layered approach. It really is important that you understand why this is done and how it works. To show how it works we're going to attempt to walk through what happens when a program wants to read a small amount of information from the disk. Here are the steps The application program calls a file system related system call. For example, read(fd, input, 1024) attempts to read a 1Kbyte section from disk and place it into the variable input. Passes through the system call interface. Remember, a system call is a software interrupt. So the operating system's interrupt handler will kick in, see that this is a system call. It will decide which part of the operating system should be responsible for handling this system call. It then passes it on. Pass through the logical file structure. So we are now into the layers of the file system. This layer provides the system calls, the operations and file types. What is does now is decide which file organisation module to pass this request onto. Most modern operating systems support more than one. This decision will be based on which type of file system the requested file belongs to. Let's assume we are on a Linux system and the file we requested is on a Windows 95 partition. In this situation the logical file structure layer will pass the request onto the Windows 95 file organisation module. Passing through the file organisation module. The file organisation module is the one which knows how directories and files are organised on the disk. Different file systems use different organisations. Windows 95 used

to use the VFAT file system, it know uses VFAT-32. Windows NT uses the NTFS format. Linux uses EXT2. Most modern operating systems will have multiple file organisation modules so they can read different formats. In our example, our logical file structure has passed the request onto the VFAT32 file organisation module. This module translates the name of the file we want to read into an actual physical location which usually consists of disk interface, disk drive, surface, cylinder, track, sector. Onto the basic file system. Now that we know the physical location we can issue commands to the appropriate device driver. This is the responsibility of the basic file system. It will also typically provide additional services such as buffering and caching. For example, the particular sector we are requesting may already be in one of the buffers or caches maintained by the basic file system. If this is the case it will simply return that information WITHOUT reading it again from the disk. Similar bufering may also occur at other levels in this hierarchy. I/O control Finally we are at the lowest level. This is the level which knows how to talk to a particular device. The importance of the layered approach is the flexibility which it allows. The use of the layered approach allows us to have multiple implementations, particularly of the I/O control and file organisation levels. This enables the support of multiple different file systems an devices.

Directory Implementation
Having looked at how files are implemented and stored it is time to move onto directories.
Reading 9.7 Textbook, Chapter 12, Section 12.3

Allocation Methods
The information stored in files must be stored on disks. This means the operating system must decide where on the disk the information is to be stored. This decision can directly influence the speed with which the information can be retrieved and stored. The following reading introduces the three most common methods used by operating systems to store file-based information onto disk.
Reading 9.8 Textbook, Chapter 12, Section 12.4

Free-Space Management
Not only must the operating system know where the contents of a file are it must also know how much free space there is and where it is. The following reading introduces some methods used to track free space on a disk.
Reading 9.9 Textbook, Chapter 12, Section 12.5

Efficiency and Performance


Disks are slow! Very slow! Disk I/O is becoming a major bottleneck in the performance of computers. As a result a number of strategies have been developed to increase the efficiency of disk I/O.
Reading 9.10 Textbook, Chapter 12, Section 12.6

Recovery
Files are meant to store information for the long term. The last thing you want is for files to disappear but with computers and more importantly with people, these things happen. If something does happen and you lose a file you want to be able to get it back, to recover it.
Reading 9.11 Textbook, Chapter 12, Section 12.7 though 12.9

Summary
Reading 9.12 Textbook, Chapter 12, Section 12.10

Review Questions
Exercises Textbook, Chapter 12 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Chapter 10 Input/Output Systems

Introduction
This chapter looks at I/O systems and devices, such as disks and tertiary-storage structures. In the old days (the 70's) computers were attached to a fairly "standard" set of peripherals, hard disks, tapes, printers and terminals and that was about it! Nowadays a typical PC comes with an almost limitless choice of attachments, CD's, sound cards, network cards, various types of "joystick", .... How can we, as non-acolytes, ever learn how to use these devices? Well, the easiest way is to treat them all as though they were the same! By designing a suitable consistent (and usable) interface to an I/O device, it is quite simple to get people to use a new type of device. Write an interface that is the same as all the others and hide the actual details inside. As will be shown, the most commonly used design is to treat a device as though it is a file! Data is read from and written to a file. Similarly, data is read from and written to a device, therefore, since "everybody" (i.e the operating system at least) knows how to use files this seems to be an acceptable approach. Unix uses this approach. All devices are treated as files! In fact, some versions of parallel Unix even treat the CPUs as files! (see the examples from a couple of chapters back) Once a consistent interface is designed, there is a chance that people will use a device. However, they will only continue to use the device if it is "quick". Thus, the manufacturer needs to write an efficient back-end to the user interface that efficiently interfaces to the actual device. This chapter discusses some of the many issues involved in making simple and efficient use of I/O devices.

Objectives
By the end of this week you should have a basic understanding of how I/O operates at the hardware level

have knowledge of concepts such as character and block devices, blocking and non-blocking I/O be aware of the performance issues related to I/O devices which an operating system must address be able to explain the process an I/O request goes through as it is fulfilled. have revised the basic structure of disks be familiar with the requirements, rationale and methods used to schedule disk requests have looked at basic issues dealing with the management of disks such as formatting, the boot block and bad blocks have examined the concept of swap-space and its management have looked at disk reliability and stable storage

Resources
Textbook, Chapters13 and 14 Study Guide Chapter 10 Online lecture 12, 13 and 14

Overview
The following reading provides an overview of I/O and then reviews and expands upon some of the basic concepts introduced much earlier in the semester.
Reading 10.1 Textbook, Chapter 13, Introduction, Sections 13.1, 13.2

Application I/O Interface


Remember that at the start of this chapter we said that the operating system was going to provide a single, simple interface to all these I/O devices? Well this next reading goes into more details about this interface and the challenges faced when implementing it.
Reading 10.2 Textbook, Chapter 13, Section 13.3

Kernel I/O Subsystem

To provide the API discussed in the previous reading the kernel must provide a number of services to aid and manage I/O. This reading introduces scheduling, buffering, coding, spooling, error handling and data structures.
Reading 10.3 Textbook, Chapter 13, Section 13.4

I/O Request to Hardware


Remember how in the last chapter we talked about the layers in the file system and provided an example of reading a file from disk? Well the next reading uses this same example to examine the similar layers within a typical I/O request. The main difference is that it tends to ignore the intricacies of the file system layers to concentrate on the I/O layers. Steps 2 and 3 in the life cycle shown in the text book would include some of the steps which were introduced in the example given in the last study guide chapter.
Reading 10.4 Textbook, Chapter 13, Section 13.5

Streams
Streams are a mechanism for connecting a user process with a particular device.
Reading 10.5 Textbook, Chapter 13, Section 13.6

Performance
As mentioned previously, I/O is a major bottleneck in the performance of a computer. The following reading examines some of the concerns which the I/O subsystem of an operating system kernel must consider.
Reading 10.6 Textbook, Chapter 13, Section 13.7

Summary of Chapter 13
Reading 10.7 Textbook, Chapter 13, Section 13.8

Well the brings to an end our use of chapter 13. You might want to use this point to take a break. Well, at least once you've finished the review questions.

Review Questions
Exercises Textbook, Chapter 13 1, 2, 4, 5, 6, 7, 10

Introduction to secondary and tertiary storage


The last few chapters have been moving down the I/O hierarchy: from the high level of file and directory operations down to the kernel I/O systems. In this chapter we continue our movement down and consider the low-level details of both secondary and tertiary storage.

Disk Structure
Disks are a central resource to a computer system. Disks are used by user applications and the operating system for such services as virtual memory and file storage.
Reading 10.8 Textbook, Chapter 14, Section 14.1

Disk Scheduling
We've already seen this semester a number of times where the operating system must step into manage and schedule the use of a resource. Requests to read and write to a disk are no different. In fact, disks are one of the slowest resources an operating system must manage and also one of the most heavily used. The following reading discusses one approach which can be taken to attempt to increase the speed of disk operations, scheduling disk I/O requests to reduce the amount of disk head seeking.
Reading 10.9 Textbook, Chapter 14, Section 14.2

Disk Management
Apart from fulfilling I/O requests there are a number of other tasks which must be implemented in order to have working disks. This reading introduces the basic concepts associated with these other tasks.
Reading 10.10 Textbook, Chapter 14, Section 14.3

Swap-Space Management
Every modern operating system implements some form of swap space in conjunction with its virtual memory management algorithm. In fact most operating systems will die without swap space when under a heavy load. Swap space must be managed by the operating system. The following reading introduces the basics of swap space management from the operating system perspective.
Reading 10.11 Textbook, Chapter 14, Section 14.4

Other Storage Structures


Reliability, and high data-transfer rates, are both very important issues today. The next few sections examine some alternatives to standard hard disks, and examines how an operating system can provide the necessary abstractions required to use tertiary storage devices.
Reading 10.12 Textbook, Chapter 14, Section 14.5 through 14.8

Secondary Storage Summary


The following reading ends the section of this week's study guide which examines secondary storage.
Reading 10.13 Textbook, Chapter 14, Section 14.9

Exercises
Textbook, Chapter 14 1, 2, 4, 6, 8, 9, 10, 11, 12, 13, 14, 15, 17, 24

Chapter 11 Protection and Security

Introduction
Congratulations!! You've just about finished. After last week's marathon chapter this one is smaller. However, it is also very important and has never been more important than in today's computing environment. The ubiquitous nature of computers and their use in just about every important transaction of our lives means that protection and security is a must. Protection and security start with the operating system and hardware. An insecure operating system means that many of the applications which run on that operating system are also insecure. There are a lot of insecure applications out there. This chapter introduces many of the basic concepts involved in protection and security from the perspective of the operating system.

Objectives
By the end of this chapter you should be aware of the goals of protection and security be able to explain concepts such as protection domains, the access matrix, access lists and capability lists have knowledge of the protection which can be provided by programming languages be familiar with authentication, encryption, threat monitoring, program and system threats

Resources
Textbook, Chapters 18 and 19 Study Guide Chapter 11 Online lectures 19 and 20

Goals of Protection
Before implementing any form of protection or security it is

important to define what it must achieve. The following reading discusses this.
Reading 11.1 Textbook, Chapter 18, Introduction and Section 18.1

Domain of Protection
What is to be protected? The following reading attempts to provide an answer.
Reading 11.2 Textbook, Chapter 18, Section 18.2

Access Matrix
An access matrix provides an abstract view of what is involved in protecting the components of a computer system. The following reading provides a much more in-depth description of an access matrix and its related operations.
Reading 11.3 Textbook, Chapter 18, Sections 18.3 and 18.4

Revocation of Access Rights


Removing the right to access a particular object may not be a simple process. The following reading examines some of the issues to be considered.
Reading 11.4 Textbook, Chapter 18, Section 18.5

Protection Systems
The following reading moves the discussion into a more practical sphere and describes the protection schemes used by a number of real systems.
Reading 11.5 Textbook, Chapter 18, Sections 18.6 and 18.7

Protection Summary

Reading 11.6 Textbook, Chapter 18, Section 18.8

Exercises
Textbook, Chapter 18 1, 2, 4, 6, 8, 9, 10, 11, 12, 13

Security
The previous readings on protection were dealing with how an operating system can provide the basic mechanisms for protecting different resources. We now move onto security.
Reading 11.7 Textbook, Chapter 19, Introduction and Section 19.1

Authentication
Before we can safely implement any form of security or protection we have to be certain that the object or person requesting access is actually who (or what) they say they are, authentication.
Reading 11.8 Textbook, Chapter 19, Sections 19.2 and 19.3

Threats
The following reading provides a general overview of some of the threats to security.
Reading 11.9 Textbook, Chapter 19, Sections 19.4, 19.5, 19.6

One type of program threat, and possibly the most common, that isn't mentioned in the text are badly written programs. Errors or bad design in programs, especially system programs, provide many of the holes in system security which enable other programs to break in. For example, the section on worms discusses how the Internet worm used bugs in two UNIX system programs, finger and sendmail. The implementators of Microsoft's early networking programs hadn't learnt from UNIX history. Almost all the early versions of Microsoft network servers suffered from the same problems exploited by the Internet worm. The Trojan horse problem, particularly one which looks like a system login, was (and probably still is) a common method used

by students at CQU to break into the accounts of other students. There is a wide array of security resources on the Web which provide a number of practical examples of the information discussed in this reading.
Reading 11.10 COIT13152 Web site, Security links on the related links page

Encryption
The following reading provides a fairly theoretical overview of the basis of encryption.
Reading 11.11 Textbook, Chapter 19, Section 19.7

Classifications
Reading 11.12 Textbook, Chapter 19, Section 19.8

Example
The following reading introduces the security model used by Windows NT. The first paragraph contains perhaps the most important information (it is also written very diplomatically) The default security level that NT uses could be described as minimal, but it can be readily configured by the system administrator to the desired level. Most of the NT systems in the world have not been "readily configured" and thus have a large number of security problems.
Reading 11.13 Textbook, Chapter 19, Section 19.9

Summary
Reading 11.14 Textbook, Chapter 19, Section 19.10

Exercises

Textbook, Chapter 19, 1, 2, 3, 4, 5, 7, 8

Chapter 12 Review

Introduction
Congratulations!! You've just about finished. There are no more objectives, no more readings, and no more exercises, except that you should now review all those topics that have been covered during the term! Hopefully you now have a good understanding of how operating systems work, and have learnt a lot that will be valuable to you in the future.

You might also like