Title: Task-Switching
1Task-Switching
- How the x86 processor assists with
context-switching among multiple program-threads
2Program Model
- Programs consist of data and instructions
- Data consists of constants and variables, which
may be persistent or transient - Instructions may be private or shared
- These observations lead to a conceptual model for
the management of programs, and to special
processor capabilities that assist in supporting
that conceptual model
3Conceptual Program-Model
runtime library
Shared Instructions and Data
(persistent)
STACK
Private Data (transient)
created during runtime
heap
BSS
Uninitialized Data (persistent)
DATA
created at compile time
Initialized Data (persistent)
TEXT
Private Instructions (persistent)
4Task Isolation
- The CPU is designed to assist the system software
in isolating the private portions of one program
from those of another while they both are
residing in physical memory, while allowing them
also to share certain instructions and data in a
controlled way - This sharing includes access to the CPU,
whereby the tasks take turns at executing
5Multi-tasking
GDT
IDT
IDTR
TR
GDTR
TSS 1
TSS 2
supervisor-space (ring0)
user-space (ring3)
shared runtime library
STACK
STACK
SP
SS
heap
heap
BSS
BSS
DATA
DATA
DS
TEXT
TEXT
IP
CS
Task 2
Task 1
6Context-Switching
- The CPU can perform a context-switch to save
the current values of all its registers (in the
memory-area referenced by the TR register), and
to load new values into all its registers (from
the memory-area specified a new Task-State
Segment selector) - There are four ways to trigger this switch
operation on x86 processors
7How to cause a task-switch
- Use an ljmp instruction (long jump)
- ljmp task_selector, 0
- Use an lcall instruction (long call)
- lcall task_selector, 0
- Use an int-n instruction (with a task-gate)
- int 0x80
- Use an iret instruction (with NT1)
- iret
8ljmp and lcall
- These instructions are similar they both make
use of a selector for a Task-State Segment
descriptor
Base31..24
D P L
Base23..16
type
0
P
0 0 0
Limit 19..16
A V L
Base 15..0
Limit 15..0
TSS Descriptor-Format
type 16bitTSS( 0x1available or 0x3busy) or
32bitTSS( 0x9available or 0xBbusy)
9The two TSS formats
- Intel introduced the Task-State Segment in the
80286 processor (used in IBM-PC/AT) - The 80286 CPU had a 16-bit architecture
- Later Intel introduced its 80386 processor which
had a 32-bit architecture requiring a larger and
more elaborate format for its Task-State Segment
data-structure - The 286 TSS is now considered obsolete
10The 80286 TSS format
16-bits
link
0
sp0
2
ss0
4
sp1
6
ss1
8
sp2
10
ss2
12
IP
14
FLAGS
16
22 words
AX
18
CX
20
DX
22
BX
24
SP
26
BP
28
SI
30
DI
32
ES
34
field is static
CS
36
field is volatile
SS
38
DS
40
LDTR
42
11The 80386 TSS format
32-bits
0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68
72 76 80 84 88 92 96 100
link
esp0
ss0
esp1
ss1
esp2
ss2
PTDB
EIP
26 longwords
ss0
ss0
EFLAGS
ss0
ss0
EAX
ss0
ss0
ECX
ss0
ss0
EDX
ss0
ss0
EBX
ss0
ss0
ESP
ss0
ss0
EBP
ss0
ss0
ESI
ss0
ss0
EDI
ES
CS
SS
DS
field is static
FS
GS
field is volatile
LDTR
IOMAP
TRAP
field is reserved
I/O permission bitmap
12Which to use ljmp or lcall?
- Use ljmp to switch to a different task in case
you have no intention of returning - Use lcall to switch to a different task in case
you want to return to this task later
13No Task Reentrancy!
- Since each task has just one save area (in its
TSS), it cannot not be permitted for a task to be
recursively reentered! - The CPU enforces this prohibition using a busy
bit within each tasks TSS descriptor - Whenever the TR register is loaded with a new
selector-value, the CPU checks to be sure the
task isnt already busy if its not, the task
is entered, but gets marked busy
14Task-Nesting
- But its OK for one task to be nested within
another, and another, and another
initial TSS
TR
TSS 4
LINK
TSS 3
LINK
TSS 2
LINK
TSS 1
LINK
lcall
lcall
lcall
current TSS
15The NT-bit in FLAGS
- When the CPU switches to a new task via an
lcall instruction, it sets NT1 in FLAGS (and
it leaves the old TSS marked busy) - The new task can then return to the old task by
executing an iret instruction (the old task is
still busy, so returning to it with an lcall
or an ljmp wouldnt be possible)
16Task-switch Semantics
Field ljmp effect lcall effect iret effect
new busy-bit changes to 1 changes to 1 stays 1
old busy-bit is cleared stays 1 is cleared
new NT-flag Is cleared Is set to 1 no change
old NT-flag no change no change is cleared
new LINK-field no change new value no change
old LINK-field no change no change no change
17Task-Gate Descriptor
- It is also possible to trigger a task-switch with
a software or hardware interrupt, by using a
Task-Gate Descriptor in the IDT
D P L
P
type (0x5)
0
Task-State Segment Selector
Task-Gate Descriptor Format
18Threads versus Tasks
- In some advanced applications, a task can consist
of multiple execution-threads - Like tasks, threads take turns executing (and
thus require context-switching) - CPU doesnt distinguish between threads and
tasks context-switching semantics are the
same for both - Difference lies in sharing of data/code
19A task with multiple threads
TSS 1
TSS 2
Each thread has its own TSS-segment
supervisor-space (ring0)
user-space (ring3)
STACK 1
STACK 2
STACKS (each is thread-private)
heap
DATA 1
DATA 2
DATA (some shared, some private)
CODE 1
CODE 2
TEXT (some shared, some private)
20Demo program twotasks.s
- We have constructed a simple demo that
illustrates the CPU task-switching ability - Its one program, but with two threads
- Everything is in one physical segment, but the
segment-descriptors create a number of different
overlapping logical segments - One task is the supervisor thread it calls a
subordinate thread (to print a message)
21A thread could use an LDT
- To support isolation of memory-segments among
distinct tasks or threads, the CPU allows use of
private descriptor-tables - Same format for the segment-descriptors
- But selectors use a Table-Indicator bit
3 2 1 0
15
Descriptor-table index field
RPL
T I
Format of a segment-selector (16-bits)
TI Table-Indicator (0 GDT, 1 LDT)
RPL Requested Privilege-Level
22LDT descriptors
- Each Local Descriptor Table is described by its
own system segment-descriptor in the Global
Descriptor Table
Base31..24
0 0 0
D P L
Base23..16
type
0
P
Limit 19..16
A V L
Base 15..0
Limit 15..0
LDT Descriptor-Format
Type-field the type code for any LDT
segment-descriptor is 0x2
23In-class Exercise 1
- In our twotasks.s demo, the two threads will
both execute at privilege-level zero - An enhanced version of this demo would have the
supervisor (Thread 1) execute in ring 0 and
the subordinate (Thread 2) execute in ring 3 - Can you modify the demo-program so it
incorporates that suggested improvement?
24More enhancements?
- The demo-program could be made much more
interesting if it used more than one subordinate
thread, and if the supervisor thread took turns
repeatedly making calls to each subordinate
(i.e., time-sharing) - You can arrange for a thread to be called more
than once by using a jmp after the iret
instruction (to re-execute the thread)
25In-class Exercise 2
- Modify the demo so it has two subordinate
threads, each of which prints a message, and each
of which can be called again and again (i.e., add
a jmp-instruction after iret) - begin entry-point to the thread
- . . .
- iret
- jmp begin