Operating Systems Design and Implementation Chapter 02 (version - - PDF document

operating systems
SMART_READER_LITE
LIVE PREVIEW

Operating Systems Design and Implementation Chapter 02 (version - - PDF document

Operating Systems Design and Implementation Chapter 02 (version January 30, 2008 ) Melanie Rieback Vrije Universiteit Amsterdam, Faculty of Sciences Dept. Computer Science Room R4.23. Tel: (020) 598 7874 E-mail: melanie@cs.vu.nl, URL:


slide-1
SLIDE 1

Operating Systems

Design and Implementation

Chapter 02

(version January 30, 2008)

Melanie Rieback

Vrije Universiteit Amsterdam, Faculty of Sciences

  • Dept. Computer Science

Room R4.23. Tel: (020) 598 7874 E-mail: melanie@cs.vu.nl, URL: www.cs.vu.nl/∼melanie/

01 Introduction 02 Processes 03 Input/Output 04 Memory Management 05 File Systems

00 – 1 /

Processes

  • Processes, introduction
  • Interprocess communication
  • IPC problems
  • Scheduling
  • Processes in MINIX
  • Implementation in MINIX

02 – 1 Processes/

slide-2
SLIDE 2

Processes

Primitive model: a process represents a user or ap- plication and executes a program on behalf of its owner. Data involved in this processing is retrieved from and stored on files. Data in files persist over processes. A process is an invention by an operating system: it is used as a placeholder for executing programs so that we can keep a precise account of how program exe- cution is affecting the use of hardware and software resources.

process = program in execution

02 – 2 Processes/2.1 Introduction to Processes

Concurrent Processes

Important: processes are, in principle, mutually inde-

  • pendent. This implies that the CPU can be allocated

in turn to different processes:

A B C D D C B A Process switch One program counter Four program counters Process Time B C D A (a) (b) (c)

  • You don’t know when the operating system de-

cides to allocate the CPU to a next process.

  • Processes are independent: they need explicit

means to communicate with each other.

  • Always remember: a process executes a program;

a process is not the same as a program.

02 – 3 Processes/2.1 Introduction to Processes

slide-3
SLIDE 3

Process Hierarchies

Basic problem: How do you actually create processes? In many systems, the operating system creates just

  • ne initial process.

Solution: offer primitives that allow a process to cre- ate another process ⇒ leads to a hierarchy of pro- cesses:

A B D E F C

Question: What would happen when a parent pro- cess dies?

02 – 4 Processes/2.1 Introduction to Processes

Process States

Basic idea: If the operating system is to allocate re- sources such as the CPU to processes, it will have to select suitable processes for allocation ⇒ we need to keep track of the state of a process:

1 2 3 4 Blocked Running Ready

  • 1. Process blocks for input
  • 2. Scheduler picks another process
  • 3. Scheduler picks this process
  • 4. Input becomes available
  • Running: the process is currently executed by

the CPU

  • Blocked: the process is waiting for a resource to

come available

  • Ready: the process is ready to be selected

The scheduler takes decisions on (de)allocation of the CPU (transitions 2 & 3). Question: How can transitions 1 & 4 take place?

02 – 5 Processes/2.1 Introduction to Processes

slide-4
SLIDE 4

Scheduler vs Processes

Note: it is the scheduler who decides to (de)allocate the CPU. This leads to a simple process organization:

1 n – 2 n – 1 Scheduler Processes

Question: Does this organization have anything to do with the hierarchical organization of processes?

02 – 6 Processes/2.1 Introduction to Processes

Process Implementation

To implement processes, the OS keeps track of the process’ state, values of registers, memory usage,

  • etc. In MINIX:

Kernel Process management File management Registers Ptr to text segment UMASK mask Program counter Ptr to data segment Root directory Program status word Ptr to bss segment Working directory Stack pointer Exit status File descriptors Process state Signal status Real UID Current scheduling priority Process ID Effective UID Maximum scheduling priority Parent process Real GID Scheduling ticks left Process group Effective GID Quantum size Children’s CPU time Controlling TTY CPU time used Real UID Save area for read/write Message queue ptrs Effective UID System call parameters Pending signal bits Real GID Various flag bits Various flag bits Effective GID Process name File info for sharing text Bitmaps for signals Various flag bits Process name Question: Why do we see redundancy in maintaining states ? 02 – 7 Processes/2.1 Introduction to Processes

slide-5
SLIDE 5

Interrupt Handling

Basic idea: To deallocate the CPU in favor of the scheduler, we use hardware support – whenever the hardware generates an interrupt (e.g., on account of a timer), the scheduler “automagically” gets control.

  • Associated with each I/O device is a memory lo-

cation where execution continues when that de- vice generates an interrupt (interrupt vector).

  • The interrupt vector contains the start address of

an operating-system provided procedure (inter- rupt handler). Execution continues with that pro- cedure.

02 – 8 Processes/2.1 Introduction to Processes

Interrupt Handling & Scheduling

1. Hardware stacks program counter, etc. 2. Hardware loads new program counter from interrupt vector 3. Assembly language procedure saves registers 4. Assembly language procedure sets up a new stack 5. C interrupt service constructs and sends message 6. Message-passing code marks waiting message recipient ready 7. Scheduler decides which process is to run next 8. C procedure returns to the assembly code 9. Assembly language procedure starts up new current process

Note: Every time an interrupt occurs, the scheduler will eventually get control. It acts as the big mediator. A process can normally not give the CPU to another process without going through the scheduler.

02 – 9 Processes/2.1 Introduction to Processes

slide-6
SLIDE 6

Threads

Idea: Sometimes you want something as a process in

  • rder to structure your application, but you don’t want

the overhead that goes with it ⇒ make use of threads. Basic idea: Threads also share resources, but only within the same process ⇒ resource management is completely left outside the control of the operating sys- tem.

02 – 10 Processes/2.1 Introduction to Processes

Threads: Minimal Support

Important: threads fall under the regime of a single process, and thus reside in the same address space ⇒ all information exchange is through data shared be- tween the threads.

  • Each thread has its own stack, processor context,

and state;

  • Threads should synchronize through simple prim-

itives (semaphores, monitors);

  • Each thread may call upon any service provided

by the operating system; it does so on behalf of the process to which it belongs.

Computer Computer Program counter Thread Process (a) (b)

Question: What’s so light-weight about all this? Where do processes and threads really differ?

02 – 11 Processes/2.1 Introduction to Processes

slide-7
SLIDE 7

Threads – Some Problems

  • Does the OS keep an administration of threads

(kernel threads), or not (user threads)? Ques- tion: What happens if a user-space thread does a blocking system call?

  • What to do when you clone a process: does the

new process get all the threads as well? What about threads currently blocking on a system call?

  • When the OS sends a signal, how can you relate

that signal to a thread? Should you relate it to a thread? Conclusion: Designing threads is maybe just a bit more tedious than you would expect at first thought.

02 – 12 Processes/2.1 Introduction to Processes

Interprocess Communication

The essence: Processes need to communicate with each other:

  • Data needs to be exchanged between processes
  • We have to synchronize processes to avoid they

get in each other’s way

  • We need to synchronize processes on account of

dependencies

02 – 13 Processes/2.2 Interprocess Communication

slide-8
SLIDE 8

Race Conditions

4 5 6 7

  • abc
  • prog. c
  • prog. n

Process A

  • ut = 4

in = 7 Process B Spooler directory

  • Process A reads in = 7, and decides to append

its file at that position

  • Process A is suspended by the OS (because its

time slot expired)

  • Process B also reads in = 7, puts its file at that
  • position. It then sets in to 8 and eventually gets

suspended

  • Process A writes its file to position 7

Problem: reading and updating in should be an atomic

  • action. If it’s not, processes can race each other and

come to the wrong conclusions.

02 – 14 Processes/2.2 Interprocess Communication

Mutual Exclusion

Critical region: A piece of shared memory (like a common variable) for which we have: 1: No two process may be simultaneously in their critical regions 2: No assumptions may be made about speeds or number of CPUs 3: No process running outside its critical region may block other processes 4: No process should have to wait forever to enter its critical region (Non)solutions:

  • Disable interrupts: simply prevent that the CPU

can be re-allocated. Pretty blunt. Works for single- CPU systems only.

  • Lock variables: Same as with the spooler vari-

able in: you’re just too late to give the variable its new value

02 – 15 Processes/2.2 Interprocess Communication

slide-9
SLIDE 9

Mutual Exclusion Strict Alteration

while(TRUE) { while(turn != 0); critical_region(); turn = 1; noncritical_region(); } while(TRUE) { while(turn != 1); critical_region(); turn = 0; noncritical_region(); }

Question: Which assumption is implicitly being made? Note: This is again a nonsolution: it violates the con- dition that processes may not block each other.

02 – 16 Processes/2.2 Interprocess Communication

Mutual Exclusion – Peterson

#define FALSE 0 #define TRUE 1 #define N 2 int turn; int interested[N]; void enter_region(int process) { int other;

  • ther = 1 - process;

interested[process] = TRUE; turn = process; while(turn == process && interested[other]); } void leave_region(int process){ interested[process] = FALSE; }

Note: This solution can “easily” be extended to cover N > 2 processes.

02 – 17 Processes/2.2 Interprocess Communication

slide-10
SLIDE 10

Mutual Exclusion The TSL Instruction

Finally: Software solutions are just no good in the general case. Instead, we can actually implement software into the hardware, and offer the result as a separate instruction:

enter

region:

tsl register,lock | copy lock to register and set lock to 1 cmp register,#0 | was lock zero? jne enter

region

| if it was non zero, lock was set, so loop ret | return to caller; critical region entered leave

region:

move lock,#0 | store a 0 in lock ret | return to caller

02 – 18 Processes/2.2 Interprocess Communication

Avoiding Busy Waiting

Problem: The solutions so far let a process keep the CPU, busy waiting until it can enter its critical region. Question: With a single CPU, does the other process ever get a chance to continue? Solution: Let a process waiting to enter its critical region return the CPU to the scheduler voluntarily:

void sleep(){ enter_region(); set own state to BLOCKED; leave_region(); give CPU to scheduler; } void wakeup(process){ enter_region(); set state of process to READY; leave_region(); give CPU to scheduler; }

Question: Why do we need enter region() and leave region() here? And why is it OK to use their busy-waiting implementations? Question: Why would you want to give the CPU back to the scheduler when waking up a process?

02 – 19 Processes/2.2 Interprocess Communication

slide-11
SLIDE 11

Producer-Consumer

#define N 100 int count = 0; void producer(){ int item; while(TRUE){ item = produce_item(); if (count == N)

✁✄✂✆☎✄☎✞✝ ();

insert_item(item); count = count + 1; if (count == 1)

✟✡✠✆☛ ☎✞☞✄✝ (consumer);

} } void consumer(){ int item; while(TRUE){ if (count == 0)

✁✄✂✆☎✄☎✞✝ ();

item = remove_item(); count = count - 1; if (count == N-1)

✟✡✠✆☛ ☎✞☞✄✝ (producer);

consume_item(item); } }

Question: There’s something wrong here – what?

02 – 20 Processes/2.2 Interprocess Communication

Semaphores

Basic idea: Introduce a special integer type with two

  • perations down and up that operate on the semaphore

sema:

  • down: if sema ≤ 0 then block the calling process

until it becomes positive again; otherwise decre- ment sema by one.

  • up: if there is a process blocking on sema, wake it

up; otherwise increment sema by one. Note: We’re talking about atomic actions here.

02 – 21 Processes/2.2 Interprocess Communication

slide-12
SLIDE 12

Semaphore – Example

#define N 100 typedef int semaphore; semaphore mutex = 1; semaphore empty = N; semaphore full = 0; void producer(){ int item; while(TRUE){ item = produce_item();

✌✎✍✆✏✄✑ (&empty); ✌✎✍✆✏✄✑ (&mutex);

insert_item(item);

✒✄✓

(&mutex);

✒✄✓

(&full); } } void consumer(){ int item; while(TRUE){

✌✎✍✆✏✄✑ (&full); ✌✎✍✆✏✄✑ (&mutex);

item = remove_item(item);

✒✄✓

(&mutex);

✒✄✓

(&empty); consume_item(item); } }

02 – 22 Processes/2.2 Interprocess Communication

Monitors

Problem: Semaphores have been heavily criticized for the chaos they can introduce in programs. What is generally needed is a more structured approach to- ward process synchronization ⇒ monitors. Essence: Think of a monitor as a protected object whose methods can be invoked only one at a time

02 – 23 Processes/2.2 Interprocess Communication

slide-13
SLIDE 13

Monitors – Example

✔✖✕✆✗✖✘✚✙✡✕✆✛

ProdCons { condition full, empty; int count = 0; void enter(int item) { if (count == N)

✜✡✢ ✘✚✙ (full);

insert_item(item); count = count + 1; if (count == 1)

✣ ✘✚✤✞✗ ✢✎✥ (empty);

} void remove(int* item) { if (count == 0)

✜✡✢ ✘✚✙ (empty);

item* = remove_item(); count = count - 1; if (count == N-1)

✣ ✘✚✤✞✗ ✢✎✥ (full);

} } void producer(){ int item; while(TRUE){ item = produce_item();

✦ ✛✎✕✞✧✩★✎✕✪✗ ✣✬✫✮✭ ✗✩✙ ✭ ✛✰✯✱✘✚✙ ✭ ✔✳✲ ;

} } void consumer(){ int item; while(TRUE){

✦ ✛✎✕✆✧✎★✩✕✆✗ ✣✴✫ ✛ ✭ ✔✖✕✆✵ ✭ ✯✷✶✸✘✚✙ ✭ ✔✴✲ ;

consume_item(item); } }

02 – 24 Processes/2.2 Interprocess Communication

Message-Passing

Idea: processes communicate by sending and receiv- ing only messages:

send( destination, &message ); receive( source, &message ); receive( ANY, &message );

  • What to do when the communication channel is

unreliable?

  • How can you verify the identity of the other party

(authentication)?

02 – 25 Processes/2.2 Interprocess Communication

slide-14
SLIDE 14

Message-passing: example

#define N 100 void producer(){ int item; message msg; while(TRUE){ item = produce_item(); receive(consumer, &msg); build_message(&msg, item); send(consumer, &msg); } } void consumer(){ int item, i; message msg; for(i = 0; i < N; i++) send(producer, &msg); while(TRUE){ receive(producer, &msg); item = extract_item(); send(producer, &msg); consume_item(item); } }

Question: What’s happening here? Are there any assumptions about buffering & blocking?

02 – 26 Processes/2.2 Interprocess Communication

Dining philosophers (1/3)

Problem: There are five philosophers, each seated at a round table before a bowl of spaghetti. A philoso- pher needs two forks to eat the spaghetti: the one to her left, and the one to her right.

#define N 5 void philosopher(int i){ while(TRUE){ think(); take_fork( i ); take_fork( (i+1) % N ); eat(); put_fork( i ); put_fork( (i+1) % N ); } }

02 – 27 Processes/2.3 Classical IPC Problems

slide-15
SLIDE 15

Dining philosophers (2/3)

Main issue: how to avoid deadlock & starvation. The following solution has no deadlock (why not?):

philosopher(int i){ while( TRUE ){ think(); if( i > 0 ) { take_fork( i ); take_fork( (i+1) % N ); } else { take_fork( (i+1) % N ); take_fork( i ); } eat(); put_fork( i ); put_fork( (i+1) % N ); } }

Note: There is great code available at http://www.cs.utk.edu/∼mbeck/classes/cs560/

02 – 28 Processes/2.3 Classical IPC Problems

Dining philosophers (3/3)

philosopher( int i ) { while( TRUE ) { think(); take_forks( i ); eat(); put_forks( i ); } } take_forks( int i ) { down( &mutex ); state[i] = HUNGRY; test( i ); up( &mutex ); down( &start_eating[i] ); } put_forks( int i ) { down( &mutex ); state[i] = THINKING; test( LEFT(i) ); test( RIGHT(i) ); up( &mutex ); } test( int i ) { if( state[i] == HUNGRY && state[LEFT(i)] != EATING && state[RIGHT(i)] != EATING ){ state[i] = EATING; up( &start_eating[i] ); } }

Question: Can there be deadlock or starvation?

02 – 29 Processes/2.3 Classical IPC Problems

slide-16
SLIDE 16

Readers/writers (1/2)

Problem: N processes make use of shared data. It is permitted to let reading processes simultaneously ac- cess the data, but only exactly one process for modi- fications.

typedef int semaphore; semaphore mutex = 1; semaphore dbase = 1; int rcount = 0; void reader(){ while(TRUE){ down( &mutex ); rcount = rcount + 1; if( rcount == 1 ) down( &dbase ); up( &mutex ); read_data_base(); down( &mutex ); rcount = rcount - 1; if( rcount == 0) up( &dbase ); up( &mutex ); use_data_read(); } } void writer(){ while(TRUE){ think_up_data(); down( &dbase ); write_data_base(); up( &dbase ); } }

Question: What’s so bad about this solution?

02 – 30 Processes/2.3 Classical IPC Problems

Readers/writers (2/2)

Simple solution: make a FIFO-queue of readers and writers and let several readers in at the same time. Guaranteed free of deadlock and starvation.

batch #1

  • f

readers batch #2

  • f

readers writer 02 – 31 Processes/2.3 Classical IPC Problems

slide-17
SLIDE 17

Process scheduling (1/3)

Problem: There are a number of processes ready to execute their associated program. However, there is

  • nly one CPU available ⇒ one of the processes has

to be selected.

Long CPU burst Short CPU burst Waiting for I/O (a) (b) Time

Scheduler: part of an operating system that is re- sponsible for deciding which process may execute, and when that process may execute ⇒ scheduling al- gorithm.

02 – 32 Processes/2.4 Process Scheduling

Process scheduling (2/3)

All systems:

  • Fairness: no prejudice with respect to pro-

cesses.

  • Policy enforcement: seeing that stated schedul-

ing policy is carried out.

  • Balance: all parts of the system are kept busy.

Batch systems:

  • Throughput: do as many jobs as possible.
  • Turnaround time: have jobs processed as

quick as possible.

  • Utilization: keep the CPU busy doing “real”

work.

02 – 33 Processes/2.4 Process Scheduling

slide-18
SLIDE 18

Process scheduling (3/3)

Interactive systems:

  • Response time: respond to requests quickly.
  • Proportionality: meet user’s expectations.

Real-time systems:

  • Meeting deadlines: avoid losing data.
  • Predictability: avoid quality degradation in mul-

timedia systems. Question: Suppose we had a really nifty scheduler that could optimize on everything, but it would take some time. Is that useful?

02 – 34 Processes/2.4 Process Scheduling

Batch systems (1/2)

Shortest Job First: Suppose we know what the jobs are that need to be executed, and how long each job will take ⇒ optimize the turnaround times per job. Turnaround: delay between job submission and job completion.

8 4 4 4 8 4 4 4 turnaround time 11 14 average 8 12 20 4 8 12 16 20

Note: Scheduling the shortest job first leads to mini- mal average turnaround time per job.

02 – 35 Processes/2.4 Process Scheduling

slide-19
SLIDE 19

Batch systems (2/2)

Problems: 1: You have to know all jobs in advance ⇒ cheat a bit: shortest jobs get queued at the head, but run- ning jobs are never preempted. (there’s a prob- lem here – what is it?) However, you will not get an optimum. 2: You have to know the completion time per job ⇒ use estimates based on previous runs, e.g. Tk+1 = a·Tk−1 +(1−a)·Tk Question: What happens if a = 0 or a = 1?

02 – 36 Processes/2.4 Process Scheduling

Interactive systems

Problem: we don’t want some process to run to com- pletion if that’s going to take a very long time. Instead, the process should be interrupted and suspended in favor of some other process ⇒ preemptive schedul- ing.

CPU select preempt

Note: what we need is some way to switch between processes ⇒ context switching.

02 – 37 Processes/2.4 Process Scheduling

slide-20
SLIDE 20

Context switching

Problem: We have to change from one process to

  • another. The stuff that is going to be used by another

process should be saved ⇒ CPU registers.

save registers restore registers select next process save registers restore registers select next process idle executing P P* scheduler

02 – 38 Processes/2.4 Process Scheduling

Round-robin scheduling

CPU

Simple idea: every process is assigned a quantum – a number of time units that it is allowed to use the CPU without interruption. If the process blocks, or the quantum is exceeded, the scheduler assigns the CPU to the next process in line. Question: How big should a quantum be?

02 – 39 Processes/2.4 Process Scheduling

slide-21
SLIDE 21

Priority scheduling

Idea: Combine round-robin with process priorities: a process with a higher priority is selected first.

Priority 4 Priority 3 Priority 2 Priority 1 Queue headers Runable processes (Highest priority) (Lowest priority)

Issues:

  • Are priorities assigned statically or dynamically?
  • Can priorities be adjusted during execution?
  • How are priorities assigned?

02 – 40 Processes/2.4 Process Scheduling

Process management in MINIX

… … …

Init Kernel User processes Server processes Device drivers Kernel User mode Kernel mode Layer 4 3 2 1 User process Process manager File system Disk driver Clock task System task TTY driver Ethernet driver Info server Network server User process User process

  • All communication at layers 2–4 through message-

passing.

  • Layers 1–2, and often 1–3 in other systems are

usually taken together into a single binary to be executed in kernel mode. The rest executes in user mode. MINIX 3 has a microkernel. Question: What’s so good about having the file sys- tem (and process manager), as well as all the drivers,

  • utside the kernel?

02 – 41 Processes/2.5 Processes in MINIX

slide-22
SLIDE 22

IPC in MINIX

Essence: All interprocess communication uses block- ing message passing:

  • When a process sends a message, it is blocked

until the receiver actually reads it.

  • A process is always blocked when receiving a mes-

sage until there is something to read. Processes can communicate only with processes in their own layer, and with the layer just below them. MINIX uses priority scheduling, with one scheduling queue per priority. User processes have the lowest priority; device driver tasks the highest. Important: MINIX runs as several totally independent programs (minimum: kernel, drivers, process man- ager, file system). This means that procedures with the same name, but in different programs, do not con- flict.

02 – 42 Processes/2.5 Processes in MINIX

Memory layout

Memory available for user programs Memory available for user programs src/servers/init/init src/servers/fs/fs src/servers/pm/pm src/servers/rs/rs src/kernel/kernel Limit of memory 3403K 3375K 3236K 1093K 1024K 640K 55K (Depends on number

  • f buffers included

in file system) 2K Start of kernel 1K Memory driver File system Process manager System task Clock task Kernel [Interrupt vectors] [Used by BIOS] Read only memory and I/O adapter memory (unavailable to MINIX 3) Reincarnation server [Boot monitor] Log driver Console driver Disk driver Init 590K src/drivers/memory/memory src/drivers/log/log src/drivers/tty/tty src/drivers/at_wini/at_wini 3537K 3549K 3489K 3416K

02 – 43 Processes/2.5 Processes in MINIX

slide-23
SLIDE 23

C Include File Semantics

In C, files can be merged by #include statements:

#include A #include A #include B #ifndef A_H #define A_H #endif

file A code in file A file B file C

#ifndef A_H #define A_H #endif

code in file A code file B code file C

#ifndef A_H #define A_H #endif

code in file A code file B code file C This part is skipped by the compiler 02 – 44 Processes/2.6 Implementation in MINIX

C Scope Semantics

Modules in C are modeled by source files. Types, vari- ables, etc., can be exported to other files (default); imported (#extern), or be explicitly restricted to the current file (#static):

#define PUBLIC #define EXTERN extern #define PRIVATE static EXTERN int k; PRIVATE int n; EXTERN int k; PRIVATE int n; PUBLIC int k; PRIVATE int n;

02 – 45 Processes/2.6 Implementation in MINIX

slide-24
SLIDE 24

Messages (1/2)

In MINIX, there are seven different message types:

m_source m_type m1_i1 m1_i2 m1_i3 m1_p1 m1_p2 m1_p3 m_source m_type m2_i1 m2_i2 m2_i3 m2_l1 m2_l2 m2_p1 m_source m_type m3_i1 m3_i2 m3_p1 m3_ca1 m_source m_type m4_l1 m4_l2 m4_l3 m4_l4 m4_l5 m5_c2 m5_c1 m_source m_type m5_i1 m5_i2 m5_l1 m5_l2 m5_l3 m_source m_type m7_i1 m7_i2 m7_i3 m7_i4 m7_p1 m7_p2 m_source m_type m8_i1 m8_i2 m8_p1 m8_p2 m8_p3 m8_p4

int: i char*: p long: l char: c char[]: ca

02 – 46 Processes/2.6 Implementation in MINIX

Messages – Dereferencing

03020 typedef struct { 03021 int m_source; /* who sent the message */ 03022 int m_type; /* what kind of message is it */ 03023 union { 03024 mess_1 m_m1; 03025 mess_2 m_m2; 03026 mess_3 m_m3; 03027 mess_4 m_m4; 03028 mess_5 m_m5; 03029 mess_7 m_m7; 03030 mess_8 m_m8; 03031 } m_u; 03032 } message; 03033 03034 /* The following defines provide names for useful members. */ 03035 #define m1_i1 m_u.m_m1.m1i1 03036 #define m1_i2 m_u.m_m1.m1i2 03037 #define m1_i3 m_u.m_m1.m1i3 03038 #define m1_p1 m_u.m_m1.m1p1 03039 #define m1_p2 m_u.m_m1.m1p2 03040 #define m1_p3 m_u.m_m1.m1p3 ..... 03049 #define m3_i1 m_u.m_m3.m3i1 03050 #define m3_i2 m_u.m_m3.m3i2 03051 #define m3_p1 m_u.m_m3.m3p1 03052 #define m3_ca1 m_u.m_m3.m3ca1

m1 i3 ⇒ msg type #1, int-field #3 m3 ca1 ⇒ msg type #3, character-array-field #1 message msg; msg.m_u.m_m1.m1i3 = 3; msg.m1_i3 = 3;

02 – 47 Processes/2.6 Implementation in MINIX

slide-25
SLIDE 25

System Calls (1/2)

Remember: Whenever a system call is made, the call is translated into a message to a specific structure.

3670 /*===========================================================================* 03671 * Messages for BLOCK and CHARACTER device drivers * 03672 *===========================================================================*/ 03673 03674 /* Message types for device drivers. */ 03675 #define DEV_RQ_BASE 0x400 /* base for device request types */ 03676 #define DEV_RS_BASE 0x500 /* base for device response types */ 03677 03678 #define CANCEL (DEV_RQ_BASE + 0) /* general req to force a task to cancel */ 03679 #define DEV_READ (DEV_RQ_BASE + 3) /* read from minor device */ 03680 #define DEV_WRITE (DEV_RQ_BASE + 4) /* write to minor device */ 03681 #define DEV_IOCTL (DEV_RQ_BASE + 5) /* I/O control code */ 03682 #define DEV_OPEN (DEV_RQ_BASE + 6) /* open a minor device */ 03683 #define DEV_CLOSE (DEV_RQ_BASE + 7) /* close a minor device */ 03684 #define DEV_SCATTER (DEV_RQ_BASE + 8) /* write from a vector */ 03685 #define DEV_GATHER (DEV_RQ_BASE + 9) /* read into a vector */ 03686 #define TTY_SETPGRP (DEV_RQ_BASE + 10) /* set process group */ 03687 #define TTY_EXIT (DEV_RQ_BASE + 11) /* process group leader exited */ 03688 #define DEV_SELECT (DEV_RQ_BASE + 12) /* request select() attention */ 03689 #define DEV_STATUS (DEV_RQ_BASE + 13) /* request driver status */ 03690 03691 #define DEV_REPLY (DEV_RS_BASE + 0) /* general task reply */ 03692 #define DEV_CLONED (DEV_RS_BASE + 1) /* return cloned minor */ 03693 #define DEV_REVIVE (DEV_RS_BASE + 2) /* driver revives process */ 03694 #define DEV_IO_READY (DEV_RS_BASE + 3) /* selected device ready */ 03695 #define DEV_NO_STATUS (DEV_RS_BASE + 4) /* empty status reply */ 03696 03697 /* Field names for messages to block and character device drivers. */ 03698 #define DEVICE m2_i1 /* major-minor device */ 03699 #define PROC_NR m2_i2 /* which (proc) wants I/O? */ 03700 #define COUNT m2_i3 /* how many bytes to transfer */ 03701 #define REQUEST m2_i3 /* ioctl request code */ 03702 #define POSITION m2_l1 /* file offset */ 03703 #define ADDRESS m2_p1 /* core buffer address */ 11215 /* Transfer bytes from/to the device. */ 11216 r = (*dp->dr_transfer)(mp->PROC_NR, mp->m_type, mp->POSITION, iov, nr_req);

02 – 48 Processes/2.6 Implementation in MINIX

System Calls (2/2)

05409 #define SYSCALL_FUNC 0x0F /* mask for system call function */ 05410 #define SYSCALL_FLAGS 0xF0 /* mask for system call flags */ ..... 05413 /* System call numbers that are passed when trapping to the kernel. The 05414 * numbers are carefully defined so that it can easily be seen (based on 05415 * the bits that are on) which checks should be done in sys_call(). 05416 */ 05417 #define SEND 1 /* 0 0 0 1 : blocking send */ 05418 #define RECEIVE 2 /* 0 0 1 0 : blocking receive */ 05419 #define SENDREC 3 /* 0 0 1 1 : SEND + RECEIVE */ 07480 PUBLIC int sys_call(call_nr, src_dst, m_ptr) 07481 int call_nr; /* system call number and flags */ 07482 int src_dst; /* src to receive from or dst to send to */ 07483 message *m_ptr; /* pointer to message in the caller’s space */ 07484 { 07485 /* System calls are done by trapping to the kernel with an INT instruction. 07486 * The trap is caught and sys_call() is called to send or receive a message 07487 * (or both). The caller is always given by ’proc_ptr’. 07488 */ 07489 register struct proc *caller_ptr = proc_ptr; /* get pointer to caller */ 07490 int function = call_nr & SYSCALL_FUNC; /* get system call function */ 07491 unsigned flags = call_nr & SYSCALL_FLAGS; /* get flags */ ..... 07558 switch(function) { 07559 case SENDREC: 07560 /* A flag is set so that notifications cannot interrupt SENDREC. */ 07561 priv(caller_ptr)->s_flags |= SENDREC_BUSY; 07562 /* fall through */ 07563 case SEND: 07564 result = mini_send(caller_ptr, src_dst, m_ptr, flags); 07565 if (function == SEND || result != OK) { 07566 break; /* done, or SEND failed */ 07567 } /* fall through for SENDREC */ 07568 case RECEIVE: 07569 if (function == RECEIVE) 07570 priv(caller_ptr)->s_flags &= ~SENDREC_BUSY; 07571 result = mini_receive(caller_ptr, src_dst, m_ptr, flags); 07572 break; ..... 07580 default: 07581 result = EBADCALL; /* illegal system call */ 07582 } 07583 07584 /* Now, return the result of the system call to the caller. */ 07585 return(result); 07586 }

02 – 49 Processes/2.6 Implementation in MINIX

slide-26
SLIDE 26

Process Table

5500 #ifndef PROC_H 05501 #define PROC_H 05502 05503 /* Here is the declaration of the process table. It contains all process 05504 * data, including registers, flags, scheduling priority, memory map, 05505 * accounting, message passing (IPC) information, and so on. 05506 * ..... 05510 */ ..... 05516 struct proc { 05517 struct stackframe_s p_reg; /* process’ registers saved in stack frame */ 05518 reg_t p_ldt_sel; /* selector in gdt with ldt base and limit */ 05519 struct segdesc_s p_ldt[2+NR_REMOTE_SEGS]; /* CS, DS and remote segments */ 05520 05521 proc_nr_t p_nr; /* number of this process (for fast access) */ 05522 struct priv *p_priv; /* system privileges structure */ 05523 char p_rts_flags; /* SENDING, RECEIVING, etc. */ 05524 05525 char p_priority; /* current scheduling priority */ 05526 char p_max_priority; /* maximum scheduling priority */ 05527 char p_ticks_left; /* number of scheduling ticks left */ 05528 char p_quantum_size; /* quantum size in ticks */ 05529 05530 struct mem_map p_memmap[NR_LOCAL_SEGS]; /* memory map (T, D, S) */ 05531 05532 clock_t p_user_time; /* user time in ticks */ 05533 clock_t p_sys_time; /* sys time in ticks */ 05534 05535 struct proc *p_nextready; /* pointer to next ready process */ 05536 struct proc *p_caller_q; /* head of list of procs wishing to send */ 05537 struct proc *p_q_link; /* link to next proc wishing to send */ 05538 message *p_messbuf; /* pointer to passed message buffer */ 05539 proc_nr_t p_getfrom; /* from whom does process want to receive? */ 05540 proc_nr_t p_sendto; /* to whom does process want to send? */ 05541 05542 sigset_t p_pending; /* bit map for pending kernel signals */ 05543 05544 char p_name[P_NAME_LEN]; /* name of the process, including \0 */ 05545 };

02 – 50 Processes/2.6 Implementation in MINIX

MINIX v3 processes (1/2)

Note: Not all processes are equal, and we need a way to distinguish their privileges:

05718 struct priv { 05719 proc_nr_t s_proc_nr; /* number of associated process */ 05720 sys_id_t s_id; /* index of this system structure */ 05721 short s_flags; /* PREEMTIBLE, BILLABLE, etc. */ 05722 05723 short s_trap_mask; /* allowed system call traps */ 05724 sys_map_t s_ipc_from; /* allowed callers to receive from */ 05725 sys_map_t s_ipc_to; /* allowed destination processes */ 05726 long s_call_mask; /* allowed kernel calls */ 05727 05728 sys_map_t s_notify_pending; /* bit map with pending notifications */ 05729 irq_id_t s_int_pending; /* pending hardware interrupts */ 05730 sigset_t s_sig_pending; /* pending signals */ 05731 05732 timer_t s_alarm_timer; /* synchronous alarm timer */ 05733 struct far_mem s_farmem[NR_REMOTE_SEGS]; /* remote memory map */ 05734 reg_t *s_stack_guard; /* stack guard word for kernel tasks */ 05735 };

Important: Only low-level user-mode device drivers are allowed to, for example, request register manipu- lations.

02 – 51 Processes/2.6 Implementation in MINIX

slide-27
SLIDE 27

MINIX v3 processes (2/2)

  • nr-
  • id-name---- -flags- -traps-
  • ipc_to mask-----------------------

(-4) (01) IDLE P-BS-

  • 00000000 00001111 10000000 0000000

[-3] (02) CLOCK

  • --S-
  • -R--

00000000 00001111 10000000 0000000 [-2] (03) SYSTEM

  • --S-
  • -R--

00000000 00001111 10000000 0000000 [-1] (04) KERNEL

  • --S-
  • 00000000 00001111 10000000 0000000

(05) pm P--S- ESRBN 11111111 11111111 10000000 0000000 1 (06) fs P--S- ESRBN 11111111 11111111 10000000 0000000 2 (07) rs P--S- ESRBN 11111111 11111111 10000000 0000000 3 (09) memory P--S- ESRBN 00110111 01101111 10000000 0000000 ... 7 (00) init P-B-- E--B- 00000111 00000000 00000000 0000000 ... 15 (16) printer P--S- ESRBN 01111111 11111111 11111111 1111111

Note: The debugger provides info on who can do

  • what. We see that the init process can call only pm,

fs, and rs by means of B = send/receive calls (i.e., synchronous communication. VERY IMPORTANT: CHECK THIS OUT FOR YOURSELF WITH THE MINIX CD

02 – 52 Processes/2.6 Implementation in MINIX

Bootstrapping MINIX (1/2)

M a s t e r b

  • t

r e c

  • r

d & p a r t i t i

  • n

t a b l e B

  • t

b l

  • c

k P a r t i t i

  • n

1 b

  • t

b l

  • c

k l

  • a

d s B

  • t

p r

  • g

r a m l

  • a

d s B

  • t

p r

  • g

r a m f

  • r

p a r t i t i

  • n

1 P a r t i t i

  • n

2 b

  • t

b l

  • c

k B

  • t

p r

  • g

r a m f

  • r

p a r t i t i

  • n

2 l

  • a

d s (a) (b)

Basic idea: A computer has a ROM-installed pro- gram that automatically reads the first 512 bytes of the bootblock into memory. The loaded code is then executed (program Boot).

02 – 53 Processes/2.6 Implementation in MINIX

slide-28
SLIDE 28

Bootstrapping MINIX (2/2)

  • Boot reads the second sector of the (1K) boot-

block, and inspects the parameters saved there.

  • The user is presented with options for loading the

rest of the system, or can even choose to load

  • ther OSs that lie in different partitions.
  • Boot normally simply loads the binary image of a

compiled MINIX version into main memory, and jumps to main().

  • main()’s task is to initialize memory and the pro-

cess table (there are some processes running now!), and start the shell at the console. Question: We can use a very similar scheme for disk- less workstations. How?

02 – 54 Processes/2.6 Implementation in MINIX

Interrupt Handling (1/2)

Interrupt INT IRQ 0 (clock) IRQ 1 (keyboard) IRQ 3 (tty 2) IRQ 4 (tty 1) IRQ 5 (XT Winchester) IRQ 6 (floppy) IRQ 7 (printer) IRQ 8 (real time clock) IRQ 9 (redirected IRQ 2) IRQ 10 IRQ 11 IRQ 12 IRQ 13 (FPU exception) IRQ 14 (AT Winchester) IRQ 15 ACK Master interrupt controller INT ACK Slave interrupt controller INT CPU INTA Interrupt ack s y s t e m

  • d

a t a

  • b

u s

During system initialization, main() initializes the con- trollers and sets the interrupt vector.

02 – 55 Processes/2.6 Implementation in MINIX

slide-29
SLIDE 29

Interrupt Handling (2/2)

1: Save registers of interrupted process 2: Handle the interrupt 3: Continue by rescheduling the best process. Note: The hardware already saves a number of key registers by pushing them onto a new stack of the in- terrupted process.

06515 #define hwint_master(irq) \ 06516 call save /* save interrupted process state */;\ 06517 push (_irq_handlers+4*irq) /* irq_handlers[irq] */;\ 06518 call _intr_handle /* intr_handle(irq_handlers[irq]) */;\ 06519 pop ecx ;\ 06520 cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\ 06521 jz 0f ;\ 06522 inb INT_CTLMASK /* get current mask */ ;\ 06523

  • rb

al, [1<<irq] /* mask irq */ ;\ 06524

  • utb

INT_CTLMASK /* disable the irq */;\ 06525 0: movb al, END_OF_INT ;\ 06526

  • utb

INT_CTL /* reenable master 8259 */;\ 06527 ret /* restart (another) process */

02 – 56 Processes/2.6 Implementation in MINIX

Saving registers

06617 ! Save for protected mode. 06618 ! This is much simpler than for 8086 mode, because the stack already points 06619 ! into the process table, or has already been switched to the kernel stack. 06620 06621 .align 16 06622 save: 06623 cld ! set direction flag to a known value 06624 pushad ! save "general" registers 06625

  • 16 push

ds ! save ds 06626

  • 16 push

es ! save es 06627

  • 16 push

fs ! save fs 06628

  • 16 push

gs ! save gs 06629 mov dx, ss ! ss is kernel data segment 06630 mov ds, dx ! load rest of kernel segments 06631 mov es, dx ! kernel does not use fs, gs 06632 mov eax, esp ! prepare to return

✹✻✺✽✼✿✾✮❀❂❁✽✼✮❃❄❃✿❀✿❅✚❆❇✺❈❅✮❀✿❉✿❊✮❅✿❋❂✼✿●❄●❄❅✮❀❍✺❄✺✮■

06633 incb (_k_reenter) ! from -1 if not reentering 06634 jnz set_restart1 ! stack is already kernel stack 06635 mov esp, k_stktop !

✹✻✺❑❏▼▲◆❉✷❁◆❖❂❉❍P❘◗✮❀✿❅✿❋❍❀✮❃❂✺❑❉✮✼❍❁❑◗▼■

06636 push _restart ! build return address for int handler 06637 xor ebp, ebp ! for stacktrace 06638 jmp RETADR-P_STACKBASE(eax) !

✹❙❅✮❀✿❉✿❊✮❅✿❋❚❉❍P❯❏✿❖❍❀✿❅✮❀❯❱❍P❑❊❲❁✽✼❑❳✷❀❩❨❄❅❍P◆❳❬■

06639 06640 .align 4 06641 set_restart1: 06642 push restart1 06643 jmp RETADR-P_STACKBASE(eax) !

✹❙❅✮❀✿❉✿❊✮❅✿❋❚❉❍P❯❏✿❖❍❀✿❅✮❀❯❱❍P❑❊❲❁✽✼❑❳✷❀❩❨❄❅❍P◆❳❬■

02 – 57 Processes/2.6 Implementation in MINIX

slide-30
SLIDE 30

Restoring registers

06681 _restart: 06682 06683 ! Restart the current process or the next process if it is set. 06684 06685 cmp (_next_ptr), 0 ! see if another process is scheduled 06686 jz 0f 06687 mov eax, (_next_ptr) 06688 mov (_proc_ptr), eax ! schedule new process 06689 mov (_next_ptr), 0 06690 0: mov esp, (_proc_ptr) ! will assume P_STACKBASE == 0 06691 lldt P_LDT_SEL(esp) ! enable process’ segment descriptors 06692 lea eax, P_STACKTOP(esp) ! arrange for next interrupt 06693 mov (_tss+TSS3_S_SP0), eax ! to save state in process table 06694 restart1: 06695 decb (_k_reenter) 06696

  • 16 pop

gs 06697

  • 16 pop

fs 06698

  • 16 pop

es 06699

  • 16 pop

ds 06700 popad 06701 add esp, 4 ! skip return adr

❭❙❪✿❫❍❴✿❵✮❴❜❛✿❝❂❛◆❞❩❵✮❴✿❡✮❴✿❵❄❵▼❛✻❢✮❣❩❞❍❤❄✐❍❥

06702 iretd ! continue process

02 – 58 Processes/2.6 Implementation in MINIX

System Calls

06648 .align 16 06649 _s_call: 06650 _p_s_call: 06651 cld ! set direction flag to a known value 06652 sub esp, 6*4 ! skip RETADR, eax, ecx, edx, ebx, est 06653 push ebp ! stack already points into proc table 06654 push esi 06655 push edi 06656

  • 16 push

ds 06657

  • 16 push

es 06658

  • 16 push

fs 06659

  • 16 push

gs 06660 mov dx, ss 06661 mov ds, dx 06662 mov es, dx 06663 incb (_k_reenter) 06664 mov esi, esp ! assumes P_STACKBASE == 0 06665 mov esp, k_stktop !

❭✻❝❑❪▼❛◆❞✷❦◆❫❂❞❍❤❘❧✮❴✿❵✿❢❍❴✮♠❂❝❑❞✮♥❍❦❑❧▼❥

06666 xor ebp, ebp ! for stacktrace 06667 ! end of inline save 06668 ! now set up parameters for sys_call() 06669 push ebx ! pointer to user message 06670 push eax ! src/dest 06671 push ecx ! SEND/RECEIVE/BOTH 06672 call _sys_call ! sys_call(function, src_dest, m_ptr) 06673 ! caller is now explicitly in proc_ptr 06674 mov AXREG(esi), eax ! sys_call MUST PRESERVE si 06675 06676 ! Fall into code to restart proc/task running.

Note: s call is called by a nonkernel process to in- voke a system service. This means switching from user to kernel mode. When call is finished, we con- tinue with restart().

02 – 59 Processes/2.6 Implementation in MINIX

slide-31
SLIDE 31

Interprocess communication

Model: assume a process P sends a message msg to process Q. 1: P calls send(P,Q,msg). 2: If Q is prepared to receive a message from (1) P,

  • r (2) any process, msg is copied from P to Q’s

buffer space. Both P and Q can continue. 3: If Q cannot receive a message from P, P is blocked and queued until its message can be delivered. Assume Q wants to receive a message: 1: First check if there is a process that is blocking because it wanted to send a message to Q. 2: If there was a message already pending, the mes- sage is copied to Q’s buffer space, and sender and receiver continue. 3: If there was no message pending, Q blocks until

  • ne does.

02 – 60 Processes/2.6 Implementation in MINIX

Example: Doing a System Call

07480 PUBLIC int sys_call(call_nr, src_dst, m_ptr) 07481 int call_nr; /* system call number and flags */ 07482 int src_dst; /* src to receive from or dst to send to */ 07483 message *m_ptr; /* pointer to message in the caller’s space */ 07484 { 07485 /* System calls are done by trapping to the kernel with an INT instruction. 07486 * The trap is caught and sys_call() is called to send or receive a message 07487 * (or both). The caller is always given by ’proc_ptr’. 07488 */ 07489 register struct proc *caller_ptr = proc_ptr; /* get pointer to caller */ 07490 int function = call_nr & SYSCALL_FUNC; /* get system call function */ 07491 unsigned flags = call_nr & SYSCALL_FLAGS; /* get flags */ ..... 07558 switch(function) { 07559 case SENDREC: 07560 /* A flag is set so that notifications cannot interrupt SENDREC. */ 07561 priv(caller_ptr)->s_flags |= SENDREC_BUSY; 07562 /* fall through */ 07563 case SEND: 07564 result = mini_send(caller_ptr, src_dst, m_ptr, flags); 07565 if (function == SEND || result != OK) { 07566 break; /* done, or SEND failed */ 07567 } /* fall through for SENDREC */ 07568 case RECEIVE: 07569 if (function == RECEIVE) 07570 priv(caller_ptr)->s_flags &= ~SENDREC_BUSY; 07571 result = mini_receive(caller_ptr, src_dst, m_ptr, flags); 07572 break; ..... 07580 default: 07581 result = EBADCALL; /* illegal system call */ 07582 } 07583 07584 /* Now, return the result of the system call to the caller. */ 07585 return(result); 07586 }

Note: we’re sending (7564) a message to the process that handles the call, and block until we receive the answer (7571).

02 – 61 Processes/2.6 Implementation in MINIX

slide-32
SLIDE 32

Sending a Message

07591 PRIVATE int mini_send(caller_ptr, dst, m_ptr, flags) 07592 register struct proc *caller_ptr; /* who is trying to send a message? */ 07593 int dst; /* to whom is message being sent? */ 07594 message *m_ptr; /* pointer to message buffer */ 07595 unsigned flags; /* system call flags */ 07596 { 07597 /* Send a message from ’caller_ptr’ to ’dst’. If ’dst’ is blocked waiting 07598 * for this message, copy the message to it and unblock ’dst’. If ’dst’ is 07599 * not waiting at all, or is waiting for another source, queue ’caller_ptr’. 07600 */ ..... 07605 /* Check for deadlock by ’caller_ptr’ and ’dst’ sending to each other. */ 07606 xp = dst_ptr; 07607 while (xp->p_rts_flags & SENDING) { /* check while sending */ 07608 xp = proc_addr(xp->p_sendto); /* get xp’s destination */ 07609 if (xp == caller_ptr) return(ELOCKED); /* deadlock if cyclic */ 07610 } 07611 07612 /* Check if ’dst’ is blocked waiting for this message. The destination’s 07613 * SENDING flag may be set when its SENDREC call blocked while sending. 07614 */ 07615 if ( (dst_ptr->p_rts_flags & (RECEIVING | SENDING)) == RECEIVING && 07616 (dst_ptr->p_getfrom == ANY || dst_ptr->p_getfrom == caller_ptr->p_nr)) { 07617 /* Destination is indeed waiting for this message. */ 07618 CopyMess(caller_ptr->p_nr, caller_ptr, m_ptr, dst_ptr, dst_ptr->p_messbuf); 07620 if ((dst_ptr->p_rts_flags &= ~RECEIVING) == 0) enqueue(dst_ptr); 07621 } else if ( ! (flags & NON_BLOCKING)) { 07622 /* Destination is not waiting. Block and dequeue caller. */ 07623 caller_ptr->p_messbuf = m_ptr; 07624 if (caller_ptr->p_rts_flags == 0) dequeue(caller_ptr); 07625 caller_ptr->p_rts_flags |= SENDING; 07626 caller_ptr->p_sendto = dst; 07627 07628 /* Process is now blocked. Put in on the destination’s queue. */

♦❄♣❍q✿r✿s❄t❄✉❍✈

07629 xpp = &dst_ptr->p_caller_q; /* find end of list */ 07630 while (*xpp != NIL_PROC) xpp = &(*xpp)->p_q_link; 07631 *xpp = caller_ptr; /* add caller to end */ 07632 caller_ptr->p_q_link = NIL_PROC; /* mark new end of list */ 07633 } else { 07634 return(ENOTREADY); 07635 } 07636 return(OK);

✇❄①❍②✿③✚④❇⑤❈⑥❍⑦❍⑧✻⑨✮⑥❂⑦❑⑨❚①❍⑩✿❶✮⑩✮✈

07637 }

02 – 62 Processes/2.6 Implementation in MINIX

Receiving a Message

07642 PRIVATE int mini_receive(caller_ptr, src, m_ptr, flags) 07643 register struct proc *caller_ptr; /* process trying to get message */ 07644 int src; /* which message source is wanted */ 07645 message *m_ptr; /* pointer to message buffer */ 07646 unsigned flags; /* system call flags */ 07647 { 07648 /* A process or task wants to get a message. If a message is already queued, 07649 * acquire it and deblock the sender. If no message from the desired source 07650 * is available block the caller, unless the flags don’t allow blocking. 07651 */ ..... 07660 /* Check to see if a message from desired source is already available. 07661 * The caller’s SENDING flag may be set if SENDREC couldn’t send. If it is 07662 * set, the process should be blocked. 07663 */ 07664 if (!(caller_ptr->p_rts_flags & SENDING)) { ..... 07688 /* Check caller queue. Use pointer pointers to keep code simple. */ 07689 xpp = &caller_ptr->p_caller_q; 07690 while (*xpp != NIL_PROC) { 07691 if (src == ANY || src == proc_nr(*xpp)) { 07692 /* Found acceptable message. Copy it and update status. */ 07693 CopyMess((*xpp)->p_nr, *xpp, (*xpp)->p_messbuf, caller_ptr, m_ptr); 07694 if (((*xpp)->p_rts_flags &= ~SENDING) == 0) enqueue(*xpp); 07695 *xpp = (*xpp)->p_q_link; /* remove from queue */ 07696 return(OK); /* report success */ 07697 } 07698 xpp = &(*xpp)->p_q_link; /* proceed to next */ 07699 } 07700 } 07701 07702 /* No suitable message is available or the caller couldn’t send in SENDREC. 07703 * Block the process trying to receive, unless the flags tell otherwise. 07704 */ 07705 if ( ! (flags & NON_BLOCKING)) { 07706 caller_ptr->p_getfrom = src; 07707 caller_ptr->p_messbuf = m_ptr; 07708 if (caller_ptr->p_rts_flags == 0) dequeue(caller_ptr); 07709 caller_ptr->p_rts_flags |= RECEIVING; 07710 return(OK); 07711 } else { 07712 return(ENOTREADY); 07713 } 07714 }

02 – 63 Processes/2.6 Implementation in MINIX

slide-33
SLIDE 33

Scheduling

IDLE_Q rdy_head rdy_tail TASK_Q TASK_Q pm rs 3 2 1 IDLE_Q IDLE clock system

. . . . . . . . . . . .

15 3 2 1 15 tty disk mem log fs 4 4 USER_Q USER_Q init 7 7

  • ❷❩❸❺❹✖❻
❷❽❼✰❾✬❹ : select the most suitable task/process

to run, starting with highest priority queue.

  • ❿✖➀❽➁➃➂➄❿✖➂➄❿ : enter a task/process into the appropri-

ate queue.

  • ➅✬❿✳➁➃➂➄❿✖➂➄❿ : do the opposite.
  • ➆✳❹➈➇➄❿✳➅ : reshuffle the USER Q when a quantum has

expired: the head is moved to the end.

02 – 64 Processes/2.6 Implementation in MINIX

System Task (1/2)

Problem: because the memory manager, file system, drivers and other typical OS processes are placed

  • utside the kernel, they cannot modify kernel data
  • structures. However, they do control a lot of things

that are administrated in the kernel. Solution: install a separate system task that (1) com- municates with these special processes, (2) operates in kernel mode, (3) changes kernel data structures on behalf of these processes.

02 – 65 Processes/2.7 System Task

slide-34
SLIDE 34

System Task (2/2)

02 – 66 Processes/2.7 System Task

Clock – Hardware

Essentially just two types:

  • Simple ones that generate an interrupt with each

cycle of the power supply (every 20 or 16.7 ms)

  • Advanced ones with their own oscillator by which

a counter is decremented. Whenever the counter hits zero, an interrupt is generated. The advanced ones are really what we need because you can program them. Example: if the oscillator has a frequency of 1 MHz, and the clock is connected to a 16-bit register, we can set the timer between 1 and 65536 µs.

Crystal oscillator Counter is decremented at each pulse Holding register is used to load the counter

02 – 67 Processes/2.8 Clocks

slide-35
SLIDE 35

Clock – Software

Basic functions:

  • Maintaining time of day (just count ticks in soft-

ware).

  • Lowering the quantum of a running process (decre-

ment a software counter).

  • Accounting for CPU usage (guess how...)
  • Handling alarms and watchdogs.
  • Profiling, monitoring, and statistics gathering.

Current time Next signal Clock header 3 4 6 2 1 X 4200 3 02 – 68 Processes/2.8 Clocks

The Clock Task

10468 PUBLIC void clock_task() 10469 { 10470 /* Main program of clock task. If the call is not HARD_INT it is an error. 10471 */ 10472 message m; /* message buffer for both input and output */ 10473 int result; /* result returned by the handler */ 10474 10475 init_clock(); /* initialize clock task */ 10476 10477 /* Main loop of the clock task. Get work, process it. Never reply. */ 10478 while (TRUE) { 10479 10480 /* Go get a message. */ 10481 receive(ANY, &m); 10482 10483 /* Handle the request. Only clock ticks are expected. */ 10484 switch (m.m_type) { 10485 case HARD_INT: 10486 result = do_clocktick(&m); /* handle clock tick */ 10487 break; 10488 default: /* illegal request type */ 10489 kprintf("CLOCK: illegal request %d from %d.\n", m.m_type,m.m_source); 10490 } 10491 } 10492 }

02 – 69 Processes/2.8 Clocks

slide-36
SLIDE 36

Processing a clock tick

10497 PRIVATE int do_clocktick(m_ptr) 10498 message *m_ptr; /* pointer to request message */ 10499 { 10500 /* Despite its name, this routine is not called on every clock tick. It 10501 * is called on those clock ticks when a lot of work needs to be done. 10502 */ 10503 10504 /* A process used up a full quantum. The interrupt handler stored this 10505 * process in ’prev_ptr’. First make sure that the process is not on the 10506 * scheduling queues. Then announce the process ready again. Since it has 10507 * no more time left, it gets a new quantum and is inserted at the right 10508 * place in the queues. As a side-effect a new process will be scheduled. 10509 */ 10510 if (prev_ptr->p_ticks_left <= 0 && priv(prev_ptr)->s_flags & PREEMPTIBLE) { 10511 lock_dequeue(prev_ptr); /* take it off the queues */ 10512 lock_enqueue(prev_ptr); /* and reinsert it again */ 10513 } 10514 10515 /* Check if a clock timer expired and run its watchdog function. */ 10516 if (next_timeout <= realtime) { 10517 tmrs_exptimers(&clock_timers, realtime, NULL); 10518 next_timeout = clock_timers == NULL ? 10519 TMR_NEVER : clock_timers->tmr_exp_time; 10520 } 10521 10522 /* Inhibit sending a reply. */ 10523 return(EDONTREPLY); 10524 }

02 – 70 Processes/2.8 Clocks

Clock interrupt handler

10556 PRIVATE int clock_handler(hook) 10557 irq_hook_t *hook; 10558 { 10559 /* This executes on each clock tick (i.e., every time the timer chip generates 10560 * an interrupt). It does a little bit of work so the clock task does not have 10561 * to be called on every tick. */ ..... 10583 register unsigned ticks; 10584 10585 /* Acknowledge the PS/2 clock interrupt. */ 10586 if (machine.ps_mca) outb(PORT_B, inb(PORT_B) | CLOCK_ACK_BIT); 10587 10588 /* Get number of ticks and update realtime. */ 10589 ticks = lost_ticks + 1; 10590 lost_ticks = 0; 10591 realtime += ticks; 10592 10593 /* Update user and system accounting times. Charge the current process for 10594 * user time. If the current process is not billable, that is, if a non-user 10595 * process is running, charge the billable process for system time as well. 10596 * Thus the unbillable process’ user time is the billable user’s system time. 10597 */ 10598 proc_ptr->p_user_time += ticks; 10599 if (priv(proc_ptr)->s_flags & PREEMPTIBLE) { 10600 proc_ptr->p_ticks_left -= ticks; 10601 } 10602 if (! (priv(proc_ptr)->s_flags & BILLABLE)) { 10603 bill_ptr->p_sys_time += ticks; 10604 bill_ptr->p_ticks_left -= ticks; 10605 } 10606 10607 /* Check if do_clocktick() must be called. Done for alarms and scheduling. 10608 * Some processes, such as the kernel tasks, cannot be preempted. 10609 */ 10610 if ((next_timeout <= realtime) || (proc_ptr->p_ticks_left <= 0)) { 10611 prev_ptr = proc_ptr; /* store running process */ 10612 lock_notify(HARDWARE, CLOCK); /* send notification */ 10613 } 10614 return(1); /* reenable interrupts */ 10615 }

02 – 71 Processes/2.8 Clocks