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