Lab 6: Scheduling
Advanced Operating Systems

Zubair Nabi
zubair.nabi@itu.edu.pk

March 6, 2013
Introduction

1

An operating system runs more processes than it has processors
Introduction

1

An operating system runs more processes than it has processors

2

Needs some plan to time share the processors between the
processes
Introduction

1

An operating system runs more processes than it has processors

2

Needs some plan to time share the processors between the
processes

3

A common approach is to provide each process with a virtual
processor – An illusion that it has exclusive access to the
processor
Introduction

1

An operating system runs more processes than it has processors

2

Needs some plan to time share the processors between the
processes

3

A common approach is to provide each process with a virtual
processor – An illusion that it has exclusive access to the
processor

4

It is then the job of the OS to multiplex these multiple virtual
processors on the underlying physical processors
Scheduling triggers

1

A process does I/O, put it to sleep, and schedule another process
Scheduling triggers

1

A process does I/O, put it to sleep, and schedule another process

2

Use timer interrupts to stop running on a processor after a fixed
time quantum (100 msec)
Context switching

• Used to achieve multiplexing
Context switching

• Used to achieve multiplexing
• Internally two low-level context switches are performed
Context switching

• Used to achieve multiplexing
• Internally two low-level context switches are performed
1

Process’s kernel thread to the current CPU’s scheduler thread
Context switching

• Used to achieve multiplexing
• Internally two low-level context switches are performed
1
2

Process’s kernel thread to the current CPU’s scheduler thread
Scheduler’s thread to a process’s kernel thread
Context switching

• Used to achieve multiplexing
• Internally two low-level context switches are performed
1
2

Process’s kernel thread to the current CPU’s scheduler thread
Scheduler’s thread to a process’s kernel thread

• No direct switching from one user-space process to another
Context switching

• Used to achieve multiplexing
• Internally two low-level context switches are performed
1
2

Process’s kernel thread to the current CPU’s scheduler thread
Scheduler’s thread to a process’s kernel thread

• No direct switching from one user-space process to another
• Each process has its own kernel stack and register set (its
context)
Context switching

• Used to achieve multiplexing
• Internally two low-level context switches are performed
1
2

Process’s kernel thread to the current CPU’s scheduler thread
Scheduler’s thread to a process’s kernel thread

• No direct switching from one user-space process to another
• Each process has its own kernel stack and register set (its
context)
• Each CPU has its own scheduler thread
Context switching

• Used to achieve multiplexing
• Internally two low-level context switches are performed
1
2

Process’s kernel thread to the current CPU’s scheduler thread
Scheduler’s thread to a process’s kernel thread

• No direct switching from one user-space process to another
• Each process has its own kernel stack and register set (its
context)
• Each CPU has its own scheduler thread
• Context switch involves saving the old thread’s CPU registers and
restoring previously-saved registers of the new thread (enabled
by swtch)
swtch
• Saves and restores contexts
swtch
• Saves and restores contexts
• Takes two arguments: struct context **old and

struct context *new
swtch
• Saves and restores contexts
• Takes two arguments: struct context **old and

struct context *new
• Replaces the former with the latter
swtch
• Saves and restores contexts
• Takes two arguments: struct context **old and

struct context *new
• Replaces the former with the latter

• Each time a process has to give up the CPU, its kernel thread
invokes swtch to save its own context and switch to the
scheduler context
swtch
• Saves and restores contexts
• Takes two arguments: struct context **old and

struct context *new
• Replaces the former with the latter

• Each time a process has to give up the CPU, its kernel thread
invokes swtch to save its own context and switch to the
scheduler context
• Flow in case of an interrupt:
swtch
• Saves and restores contexts
• Takes two arguments: struct context **old and

struct context *new
• Replaces the former with the latter

• Each time a process has to give up the CPU, its kernel thread
invokes swtch to save its own context and switch to the
scheduler context
• Flow in case of an interrupt:
1 trap handles the interrupt and the calls yield
swtch
• Saves and restores contexts
• Takes two arguments: struct context **old and

struct context *new
• Replaces the former with the latter

• Each time a process has to give up the CPU, its kernel thread
invokes swtch to save its own context and switch to the
scheduler context
• Flow in case of an interrupt:
1 trap handles the interrupt and the calls yield
2 yield makes a call to sched
swtch
• Saves and restores contexts
• Takes two arguments: struct context **old and

struct context *new
• Replaces the former with the latter

• Each time a process has to give up the CPU, its kernel thread
invokes swtch to save its own context and switch to the
scheduler context
• Flow in case of an interrupt:
1 trap handles the interrupt and the calls yield
2 yield makes a call to sched
3 sched invokes swtch(&proc->context,
cpu->scheduler)
swtch
• Saves and restores contexts
• Takes two arguments: struct context **old and

struct context *new
• Replaces the former with the latter

• Each time a process has to give up the CPU, its kernel thread
invokes swtch to save its own context and switch to the
scheduler context
• Flow in case of an interrupt:
1 trap handles the interrupt and the calls yield
2 yield makes a call to sched
3 sched invokes swtch(&proc->context,
cpu->scheduler)
4 Control returns to the scheduler thread
Scheduling mechanism

• Each process that wants to give up the processor:
Scheduling mechanism

• Each process that wants to give up the processor:
1 Acquires ptable.lock (process table lock)
Scheduling mechanism

• Each process that wants to give up the processor:
1 Acquires ptable.lock (process table lock)
2

Releases any other locks that it is holding
Scheduling mechanism

• Each process that wants to give up the processor:
1 Acquires ptable.lock (process table lock)
2
3

Releases any other locks that it is holding
Updates proc->state (its own state)
Scheduling mechanism

• Each process that wants to give up the processor:
1 Acquires ptable.lock (process table lock)
Releases any other locks that it is holding
Updates proc->state (its own state)
4 Calls sched

2

3
Scheduling mechanism

• Each process that wants to give up the processor:
1 Acquires ptable.lock (process table lock)
Releases any other locks that it is holding
Updates proc->state (its own state)
4 Calls sched

2

3

• Mechanism followed by yield, and sleep and exit
Scheduling mechanism

• Each process that wants to give up the processor:
1 Acquires ptable.lock (process table lock)
Releases any other locks that it is holding
Updates proc->state (its own state)
4 Calls sched

2

3

• Mechanism followed by yield, and sleep and exit
• sched ensures that these steps are followed
sched
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

void sched (void) {
int intena ;

if(! holding (& ptable .lock ))
panic (" sched ptable .lock");
if(cpu−>ncli != 1)
panic (" sched locks ");
if(proc−>state == RUNNING )
panic (" sched running ");
if( readeflags ()& FL_IF )
panic (" sched interruptible ");
intena = cpu−>intena ;
swtch (& proc−>context , cpu−>scheduler );
cpu−>intena = intena ;
}
yield

1
2
3
4
5
6

void yield (void) {
acquire (& ptable .lock );
proc−>state = RUNNABLE ;
sched ();
release (& ptable .lock );
}
Scheduling mechanism (2)

• Why must a process acquire ptable.lock before a call to
swtch?
• Breaks the convention that the thread that acquires a lock is also
responsible for releasing the lock
Scheduling mechanism (2)

• Why must a process acquire ptable.lock before a call to
swtch?
• Breaks the convention that the thread that acquires a lock is also
responsible for releasing the lock

• Without acquiring ptable.lock, two CPUs might want to
schedule the same process because they can access ptable
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
• If CPU is idle (no RUNNABLE)
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
• If CPU is idle (no RUNNABLE)
1

Idle looping while holding a lock would not allow any other CPU to
access the process table
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
• If CPU is idle (no RUNNABLE)
Idle looping while holding a lock would not allow any other CPU to
access the process table
2 Idle looping (all processes are waiting for I/O) while interrupts are
disabled would not allow any I/O to arrive

1
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
• If CPU is idle (no RUNNABLE)
Idle looping while holding a lock would not allow any other CPU to
access the process table
2 Idle looping (all processes are waiting for I/O) while interrupts are
disabled would not allow any I/O to arrive

1

• The first process with p->state == RUNNABLE is selected
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
• If CPU is idle (no RUNNABLE)
Idle looping while holding a lock would not allow any other CPU to
access the process table
2 Idle looping (all processes are waiting for I/O) while interrupts are
disabled would not allow any I/O to arrive

1

• The first process with p->state == RUNNABLE is selected
• The process is assigned to the per-CPU proc
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
• If CPU is idle (no RUNNABLE)
Idle looping while holding a lock would not allow any other CPU to
access the process table
2 Idle looping (all processes are waiting for I/O) while interrupts are
disabled would not allow any I/O to arrive

1

• The first process with p->state == RUNNABLE is selected
• The process is assigned to the per-CPU proc
• The process’s page table is switched to via switchuvm
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
• If CPU is idle (no RUNNABLE)
Idle looping while holding a lock would not allow any other CPU to
access the process table
2 Idle looping (all processes are waiting for I/O) while interrupts are
disabled would not allow any I/O to arrive

1

• The first process with p->state == RUNNABLE is selected
• The process is assigned to the per-CPU proc
• The process’s page table is switched to via switchuvm
• The process is marked as RUNNING
scheduler
• Simple loop: find a process to run, run it until it stops, repeat
• Acquires and releases ptable.lock, and enables interrupts
on every iteration. Why?
• If CPU is idle (no RUNNABLE)
Idle looping while holding a lock would not allow any other CPU to
access the process table
2 Idle looping (all processes are waiting for I/O) while interrupts are
disabled would not allow any I/O to arrive

1

• The first process with p->state == RUNNABLE is selected
• The process is assigned to the per-CPU proc
• The process’s page table is switched to via switchuvm
• The process is marked as RUNNING
• swtch is called to start running it
scheduler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

void scheduler (void) {
struct proc

∗p;

for (;;){
sti ();
acquire (& ptable .lock );
for(p = ptable .proc; p < & ptable .proc[ NPROC ]; p++){
if(p−>state != RUNNABLE )
continue ;
proc = p;
switchuvm (p);
p−>state = RUNNING ;
swtch (&cpu−>scheduler , proc−>context );
switchkvm ();
proc = 0;
}
Sleep and wakeup

• sleep and wakeup enable an IPC mechanism
Sleep and wakeup

• sleep and wakeup enable an IPC mechanism
• Enable sequence coordination or conditional synchronization
Sleep and wakeup

• sleep and wakeup enable an IPC mechanism
• Enable sequence coordination or conditional synchronization
• sleep allows one process to sleep waiting for an event
Sleep and wakeup

• sleep and wakeup enable an IPC mechanism
• Enable sequence coordination or conditional synchronization
• sleep allows one process to sleep waiting for an event
• wakeup allows another process to wake up processes sleeping
on an event
Queue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

struct q {
void

∗ptr;

};
void∗ send( struct q

∗q,

void

∗q)

{

while(q−>ptr != 0)
;
q−>ptr = p;
}
void∗ recv( struct q
void

∗p;

while ((p = q−>ptr) == 0)
;
q−>ptr = 0;
return p;
}

∗p)

{
Queue with sleep and wakeup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

void∗ send( struct q

∗q,

void

∗q)

{

while(q−>ptr != 0)
;
q−>ptr = p;
wakeup (q);
}
void∗ recv( struct q
void

∗p;

while ((p = q−>ptr) == 0)
sleep (q);
q−>ptr = 0;
return p;
}

∗p)

{
Queue with sleep and wakeup and locking

1
2
3
4

struct q {
struct spinlock lock;
void
};

∗ptr;
Queue with sleep and wakeup and locking (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

void∗ send( struct q

∗q,

void

acquire (&q−>lock );
while(q−>ptr != 0)
;
q−>ptr = p;
wakeup (q);
release (&q−>lock ); }
void∗ recv( struct q
void

∗q)

{

∗p;

acquire (&q−>lock );
while ((p = q−>ptr) == 0)
sleep (q);
q−>ptr = 0;
release (&q−>lock );
return p; }

∗p)

{
Queue with sleep and wakeup and implicit locking
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

void∗ send( struct q

∗q,

void

acquire (&q−>lock );
while(q−>ptr != 0)
;
q−>ptr = p;
wakeup (q);
release (&q−>lock ); }
void∗ recv( struct q
void

∗q)

{

∗p;

acquire (&q−>lock );
while ((p = q−>ptr) == 0)
sleep (q, &q−>lock );
q−>ptr = 0;
release (&q−>lock );
return p; }

∗p)

{
sleep
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

void sleep (void

∗chan ,

struct spinlock

if(proc == 0)
panic (" sleep ");
if(lk == 0)
panic (" sleep without lk");
if(lk != & ptable .lock ){
acquire (& ptable .lock );
release (lk ); }
proc−>chan = chan;
proc−>state = SLEEPING ;
sched ();
proc−>chan = 0;
if(lk != & ptable .lock ){
release (& ptable .lock );
acquire (lk ); } }

∗lk)

{
wakeup
1
2
3
4
5
6
7
8
9
10
11
12

static void wakeup1 (void
struct proc

∗chan)

{

∗p;

for(p = ptable .proc; p < & ptable .proc[ NPROC ]; p++)
if(p−>state == SLEEPING && p−>chan == chan)
p−>state = RUNNABLE ;
}

void wakeup (void

∗chan)

acquire (& ptable .lock );
wakeup1 (chan );
release (& ptable .lock );
}

{
Today’s Task

• ptable.lock is a very coarse-grained lock which protects the
entire process table
• Design a mechanism (in terms of pseudocode) that splits it up
into multiple locks
• Explain why your solution will improve performance while
ensuring protection
Reading(s)

• Chapter 5, “Scheduling” from “xv6: a simple, Unix-like teaching
operating system”

AOS Lab 6: Scheduling

  • 1.
    Lab 6: Scheduling AdvancedOperating Systems Zubair Nabi zubair.nabi@itu.edu.pk March 6, 2013
  • 2.
    Introduction 1 An operating systemruns more processes than it has processors
  • 3.
    Introduction 1 An operating systemruns more processes than it has processors 2 Needs some plan to time share the processors between the processes
  • 4.
    Introduction 1 An operating systemruns more processes than it has processors 2 Needs some plan to time share the processors between the processes 3 A common approach is to provide each process with a virtual processor – An illusion that it has exclusive access to the processor
  • 5.
    Introduction 1 An operating systemruns more processes than it has processors 2 Needs some plan to time share the processors between the processes 3 A common approach is to provide each process with a virtual processor – An illusion that it has exclusive access to the processor 4 It is then the job of the OS to multiplex these multiple virtual processors on the underlying physical processors
  • 6.
    Scheduling triggers 1 A processdoes I/O, put it to sleep, and schedule another process
  • 7.
    Scheduling triggers 1 A processdoes I/O, put it to sleep, and schedule another process 2 Use timer interrupts to stop running on a processor after a fixed time quantum (100 msec)
  • 8.
    Context switching • Usedto achieve multiplexing
  • 9.
    Context switching • Usedto achieve multiplexing • Internally two low-level context switches are performed
  • 10.
    Context switching • Usedto achieve multiplexing • Internally two low-level context switches are performed 1 Process’s kernel thread to the current CPU’s scheduler thread
  • 11.
    Context switching • Usedto achieve multiplexing • Internally two low-level context switches are performed 1 2 Process’s kernel thread to the current CPU’s scheduler thread Scheduler’s thread to a process’s kernel thread
  • 12.
    Context switching • Usedto achieve multiplexing • Internally two low-level context switches are performed 1 2 Process’s kernel thread to the current CPU’s scheduler thread Scheduler’s thread to a process’s kernel thread • No direct switching from one user-space process to another
  • 13.
    Context switching • Usedto achieve multiplexing • Internally two low-level context switches are performed 1 2 Process’s kernel thread to the current CPU’s scheduler thread Scheduler’s thread to a process’s kernel thread • No direct switching from one user-space process to another • Each process has its own kernel stack and register set (its context)
  • 14.
    Context switching • Usedto achieve multiplexing • Internally two low-level context switches are performed 1 2 Process’s kernel thread to the current CPU’s scheduler thread Scheduler’s thread to a process’s kernel thread • No direct switching from one user-space process to another • Each process has its own kernel stack and register set (its context) • Each CPU has its own scheduler thread
  • 15.
    Context switching • Usedto achieve multiplexing • Internally two low-level context switches are performed 1 2 Process’s kernel thread to the current CPU’s scheduler thread Scheduler’s thread to a process’s kernel thread • No direct switching from one user-space process to another • Each process has its own kernel stack and register set (its context) • Each CPU has its own scheduler thread • Context switch involves saving the old thread’s CPU registers and restoring previously-saved registers of the new thread (enabled by swtch)
  • 16.
    swtch • Saves andrestores contexts
  • 17.
    swtch • Saves andrestores contexts • Takes two arguments: struct context **old and struct context *new
  • 18.
    swtch • Saves andrestores contexts • Takes two arguments: struct context **old and struct context *new • Replaces the former with the latter
  • 19.
    swtch • Saves andrestores contexts • Takes two arguments: struct context **old and struct context *new • Replaces the former with the latter • Each time a process has to give up the CPU, its kernel thread invokes swtch to save its own context and switch to the scheduler context
  • 20.
    swtch • Saves andrestores contexts • Takes two arguments: struct context **old and struct context *new • Replaces the former with the latter • Each time a process has to give up the CPU, its kernel thread invokes swtch to save its own context and switch to the scheduler context • Flow in case of an interrupt:
  • 21.
    swtch • Saves andrestores contexts • Takes two arguments: struct context **old and struct context *new • Replaces the former with the latter • Each time a process has to give up the CPU, its kernel thread invokes swtch to save its own context and switch to the scheduler context • Flow in case of an interrupt: 1 trap handles the interrupt and the calls yield
  • 22.
    swtch • Saves andrestores contexts • Takes two arguments: struct context **old and struct context *new • Replaces the former with the latter • Each time a process has to give up the CPU, its kernel thread invokes swtch to save its own context and switch to the scheduler context • Flow in case of an interrupt: 1 trap handles the interrupt and the calls yield 2 yield makes a call to sched
  • 23.
    swtch • Saves andrestores contexts • Takes two arguments: struct context **old and struct context *new • Replaces the former with the latter • Each time a process has to give up the CPU, its kernel thread invokes swtch to save its own context and switch to the scheduler context • Flow in case of an interrupt: 1 trap handles the interrupt and the calls yield 2 yield makes a call to sched 3 sched invokes swtch(&proc->context, cpu->scheduler)
  • 24.
    swtch • Saves andrestores contexts • Takes two arguments: struct context **old and struct context *new • Replaces the former with the latter • Each time a process has to give up the CPU, its kernel thread invokes swtch to save its own context and switch to the scheduler context • Flow in case of an interrupt: 1 trap handles the interrupt and the calls yield 2 yield makes a call to sched 3 sched invokes swtch(&proc->context, cpu->scheduler) 4 Control returns to the scheduler thread
  • 25.
    Scheduling mechanism • Eachprocess that wants to give up the processor:
  • 26.
    Scheduling mechanism • Eachprocess that wants to give up the processor: 1 Acquires ptable.lock (process table lock)
  • 27.
    Scheduling mechanism • Eachprocess that wants to give up the processor: 1 Acquires ptable.lock (process table lock) 2 Releases any other locks that it is holding
  • 28.
    Scheduling mechanism • Eachprocess that wants to give up the processor: 1 Acquires ptable.lock (process table lock) 2 3 Releases any other locks that it is holding Updates proc->state (its own state)
  • 29.
    Scheduling mechanism • Eachprocess that wants to give up the processor: 1 Acquires ptable.lock (process table lock) Releases any other locks that it is holding Updates proc->state (its own state) 4 Calls sched 2 3
  • 30.
    Scheduling mechanism • Eachprocess that wants to give up the processor: 1 Acquires ptable.lock (process table lock) Releases any other locks that it is holding Updates proc->state (its own state) 4 Calls sched 2 3 • Mechanism followed by yield, and sleep and exit
  • 31.
    Scheduling mechanism • Eachprocess that wants to give up the processor: 1 Acquires ptable.lock (process table lock) Releases any other locks that it is holding Updates proc->state (its own state) 4 Calls sched 2 3 • Mechanism followed by yield, and sleep and exit • sched ensures that these steps are followed
  • 32.
    sched 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void sched (void){ int intena ; if(! holding (& ptable .lock )) panic (" sched ptable .lock"); if(cpu−>ncli != 1) panic (" sched locks "); if(proc−>state == RUNNING ) panic (" sched running "); if( readeflags ()& FL_IF ) panic (" sched interruptible "); intena = cpu−>intena ; swtch (& proc−>context , cpu−>scheduler ); cpu−>intena = intena ; }
  • 33.
    yield 1 2 3 4 5 6 void yield (void){ acquire (& ptable .lock ); proc−>state = RUNNABLE ; sched (); release (& ptable .lock ); }
  • 34.
    Scheduling mechanism (2) •Why must a process acquire ptable.lock before a call to swtch? • Breaks the convention that the thread that acquires a lock is also responsible for releasing the lock
  • 35.
    Scheduling mechanism (2) •Why must a process acquire ptable.lock before a call to swtch? • Breaks the convention that the thread that acquires a lock is also responsible for releasing the lock • Without acquiring ptable.lock, two CPUs might want to schedule the same process because they can access ptable
  • 36.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat
  • 37.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why?
  • 38.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why? • If CPU is idle (no RUNNABLE)
  • 39.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why? • If CPU is idle (no RUNNABLE) 1 Idle looping while holding a lock would not allow any other CPU to access the process table
  • 40.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why? • If CPU is idle (no RUNNABLE) Idle looping while holding a lock would not allow any other CPU to access the process table 2 Idle looping (all processes are waiting for I/O) while interrupts are disabled would not allow any I/O to arrive 1
  • 41.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why? • If CPU is idle (no RUNNABLE) Idle looping while holding a lock would not allow any other CPU to access the process table 2 Idle looping (all processes are waiting for I/O) while interrupts are disabled would not allow any I/O to arrive 1 • The first process with p->state == RUNNABLE is selected
  • 42.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why? • If CPU is idle (no RUNNABLE) Idle looping while holding a lock would not allow any other CPU to access the process table 2 Idle looping (all processes are waiting for I/O) while interrupts are disabled would not allow any I/O to arrive 1 • The first process with p->state == RUNNABLE is selected • The process is assigned to the per-CPU proc
  • 43.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why? • If CPU is idle (no RUNNABLE) Idle looping while holding a lock would not allow any other CPU to access the process table 2 Idle looping (all processes are waiting for I/O) while interrupts are disabled would not allow any I/O to arrive 1 • The first process with p->state == RUNNABLE is selected • The process is assigned to the per-CPU proc • The process’s page table is switched to via switchuvm
  • 44.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why? • If CPU is idle (no RUNNABLE) Idle looping while holding a lock would not allow any other CPU to access the process table 2 Idle looping (all processes are waiting for I/O) while interrupts are disabled would not allow any I/O to arrive 1 • The first process with p->state == RUNNABLE is selected • The process is assigned to the per-CPU proc • The process’s page table is switched to via switchuvm • The process is marked as RUNNING
  • 45.
    scheduler • Simple loop:find a process to run, run it until it stops, repeat • Acquires and releases ptable.lock, and enables interrupts on every iteration. Why? • If CPU is idle (no RUNNABLE) Idle looping while holding a lock would not allow any other CPU to access the process table 2 Idle looping (all processes are waiting for I/O) while interrupts are disabled would not allow any I/O to arrive 1 • The first process with p->state == RUNNABLE is selected • The process is assigned to the per-CPU proc • The process’s page table is switched to via switchuvm • The process is marked as RUNNING • swtch is called to start running it
  • 46.
    scheduler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void scheduler (void){ struct proc ∗p; for (;;){ sti (); acquire (& ptable .lock ); for(p = ptable .proc; p < & ptable .proc[ NPROC ]; p++){ if(p−>state != RUNNABLE ) continue ; proc = p; switchuvm (p); p−>state = RUNNING ; swtch (&cpu−>scheduler , proc−>context ); switchkvm (); proc = 0; }
  • 47.
    Sleep and wakeup •sleep and wakeup enable an IPC mechanism
  • 48.
    Sleep and wakeup •sleep and wakeup enable an IPC mechanism • Enable sequence coordination or conditional synchronization
  • 49.
    Sleep and wakeup •sleep and wakeup enable an IPC mechanism • Enable sequence coordination or conditional synchronization • sleep allows one process to sleep waiting for an event
  • 50.
    Sleep and wakeup •sleep and wakeup enable an IPC mechanism • Enable sequence coordination or conditional synchronization • sleep allows one process to sleep waiting for an event • wakeup allows another process to wake up processes sleeping on an event
  • 51.
    Queue 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct q { void ∗ptr; }; void∗send( struct q ∗q, void ∗q) { while(q−>ptr != 0) ; q−>ptr = p; } void∗ recv( struct q void ∗p; while ((p = q−>ptr) == 0) ; q−>ptr = 0; return p; } ∗p) {
  • 52.
    Queue with sleepand wakeup 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void∗ send( struct q ∗q, void ∗q) { while(q−>ptr != 0) ; q−>ptr = p; wakeup (q); } void∗ recv( struct q void ∗p; while ((p = q−>ptr) == 0) sleep (q); q−>ptr = 0; return p; } ∗p) {
  • 53.
    Queue with sleepand wakeup and locking 1 2 3 4 struct q { struct spinlock lock; void }; ∗ptr;
  • 54.
    Queue with sleepand wakeup and locking (2) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void∗ send( struct q ∗q, void acquire (&q−>lock ); while(q−>ptr != 0) ; q−>ptr = p; wakeup (q); release (&q−>lock ); } void∗ recv( struct q void ∗q) { ∗p; acquire (&q−>lock ); while ((p = q−>ptr) == 0) sleep (q); q−>ptr = 0; release (&q−>lock ); return p; } ∗p) {
  • 55.
    Queue with sleepand wakeup and implicit locking 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void∗ send( struct q ∗q, void acquire (&q−>lock ); while(q−>ptr != 0) ; q−>ptr = p; wakeup (q); release (&q−>lock ); } void∗ recv( struct q void ∗q) { ∗p; acquire (&q−>lock ); while ((p = q−>ptr) == 0) sleep (q, &q−>lock ); q−>ptr = 0; release (&q−>lock ); return p; } ∗p) {
  • 56.
    sleep 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void sleep (void ∗chan, struct spinlock if(proc == 0) panic (" sleep "); if(lk == 0) panic (" sleep without lk"); if(lk != & ptable .lock ){ acquire (& ptable .lock ); release (lk ); } proc−>chan = chan; proc−>state = SLEEPING ; sched (); proc−>chan = 0; if(lk != & ptable .lock ){ release (& ptable .lock ); acquire (lk ); } } ∗lk) {
  • 57.
    wakeup 1 2 3 4 5 6 7 8 9 10 11 12 static void wakeup1(void struct proc ∗chan) { ∗p; for(p = ptable .proc; p < & ptable .proc[ NPROC ]; p++) if(p−>state == SLEEPING && p−>chan == chan) p−>state = RUNNABLE ; } void wakeup (void ∗chan) acquire (& ptable .lock ); wakeup1 (chan ); release (& ptable .lock ); } {
  • 58.
    Today’s Task • ptable.lockis a very coarse-grained lock which protects the entire process table • Design a mechanism (in terms of pseudocode) that splits it up into multiple locks • Explain why your solution will improve performance while ensuring protection
  • 59.
    Reading(s) • Chapter 5,“Scheduling” from “xv6: a simple, Unix-like teaching operating system”