The ‘thread’ abstraction A look at the distinction between

advertisement
The ‘thread’ abstraction
A look at the distinction between
the idea of a ‘process’ and the
concept of a ‘thread’
Recall the ‘process’ notion
• A process is “a program in execution”
• Modern systems allow ‘multiprogramming’
(i.e., several processes exist concurrently)
• Each process requires an allocation of cpu
time, in order to make forward progress
• The OS must do ‘scheduling’ of cpu time
• Each process also needs the use of other
system facilities (memory, files, devices)
Process Control Blocks
• An operating system represents a process
by means of a kernel data-structure known
generically as a ‘process control block’ (or
‘task control block’) which keeps a record
of a program’s current state, the cpu time
consumed and available, and the system
resources being accessed or requested
• Linux calls this record a ‘task_struct’ and
maintains a dynamic linked-list of them all
‘task_struct’
next_task
prev_task
pid gid task-name
process
execution
aspects
task-state task-priority
processor register-values
(EIP, ESP, EFLAGS, etc)
memory-map
open files
terminal
timers
signal-handlers
resource
ownership
aspects
Dual task components
• A process has two distinct aspects
– Its ‘execution’ (forward progression of states)
– Its ‘ownership’ (share of system’s resources)
and these may be usefully disentangled
• The word ‘thread’ refers to the execution
aspect of a process (i.e., the entity which
gets ‘scheduled’ by an Operating System
for a share of the available cpu time
‘multi-threading’
• It is possible for an operating system to
support a concept of ‘process’ in which
more than one execution-thread exists
(with process-resources being shared)
one process with
one thread
one process with
multiple threads
Advantages of ‘threads’
• All the ‘threads’ that belong to a process
can access the same memory, the same
files, the same terminal, the same timers
and the same signal-handlers
• Thus the system ‘overhead’ involved in
managing a multithreaded task is more
efficient (less time-consuming to set up
and keep track of) than if each of those
threads had individualized ownerships
Easier software development
• A complex program is easier to write (and
debug) if it can be broken down into a set
of simpler pieces
• The programmer doesn’t have to think of
‘the big picture’ all the time, but can focus
on each separate piece one at a time
Example: program compilation
• The job of compiling a computer program
involves a succession of distinct steps:
preprocessing
linking
lexical analysis
parsing
assembling
• These steps could be written as separate
execution-threads within a single process
Example: matrix multiplication
• Each entry in a matrix-product is gotten by
summing up the products of numbers from
two equal-size lists (a row and a column)
• Separate ‘threads’ could be working on the
individual matrix-entries concurrently:
Row 1 times column 1
Row 1 times column 2
Row 2 times column 1
Row 2 times column 2
Process-communication
• The operating system ‘isolates’ processes
by giving them different memory-maps and
preventing simultaneous access to files or
peripheral devices (e.g. printers, terminals)
• Special mechanisms (e.g., “pipes”) allow
data-communication between processes
process
A
pipe
process
B
pipe
process
C
Thread communication
• Since the threads in a process share the
same memory, no special mechanism is
needed for them to communicate data
process
All these threads can read or write the same memory-locations
Multiprocessor systems
cpu
0
cpu
1
cpu
2
cpu
3
main memory
system bus
I/O
I/O
I/O
I/O
I/O
A mult-threaded application may benefit from scheduling its
separate threads for simultaneous execution on different CPUs
But note that a new hardware issue arises: cache coherency
How Linux supports ‘threads’
• Linux uses a refinement of ‘fork()’ to create
the illusion of separate ‘threads’ executing
within a process (this function is ‘clone()’)
• It creates a new process (i.e., puts a new
‘task_struct’ into the task-list) but the caller
can request that the ‘resources’ should be
copies (e.g., same map of virtual memory,
same timers, same signal-handlers, etc.)
New ‘scheduler’ not needed
• By treating ‘processes’ and ‘threads’ in a
uniform manner (insofar as scheduling is
concerned), Linux avoids extra complexity
while still getting most ‘thread’ advantages
• An additional field in the ‘task_struct’ takes
notice of the ‘thread-group’ relationship, so
cpu time can be appropriately apportioned
among threads (no process monopolizes)
Details of ‘clone()’
• The ‘clone()’ library-function takes 4 args
– A function that the new thread will execute
– A memory-region for use as thread’s stack
– Flags (telling which resources to be copied)
– A pointer to a data-area for communications
• It returns the new thread’s process-ID (pid)
Demo: ‘trythread.cpp’
• We wrote a demo-program illustrating use
of the ‘clone()’ function with different flags
• First a new thread is created which will be
executing within the same memory space
(so it can directly access it’s parent’s data)
• Then another thread is created, but it gets
a copy of the memory space, so it doesn’t
access the same memory as its parent
In-class exercise
• Last week we wrote several modules that
created pseudo-files (mm, vma, threadlist)
showing current information in the kernel
• Now we can use those modules to study
the relationships and differences between
processes and threads (in a Linux context)
• Revise ‘mmfork.cpp’ so it displays the mm
and vma info for parent-and-child threads
Download