Chapter 2: Processes and Threads -------------------------------- - fork/exec: the reason for this two-step process is to allow the child to manipulate its file descriptors after the fork but before the exec, to accomplish redirection of standard input, output, etc. - child and parent have different address spaces (ASs); - process tables or process control blocks: - process state; - program counter; - stack pointer; - memory allocation; - status of open files; - scheduling information, etc. - when a process is interrupted by a disk interrupt, for example, the process state is saved in the current stack by the interrupt hardware and switches to the interrupt routine; - all interrupts start by saving the registers of the current process, usually in the PCB. Then the information pushed onto the stack by the interrupt is removed and the stack pointer is set to point to a temporary stack used by the process handler; - a thread can wipe out other thread's stack. There is no protection between threads because (1) it is impossible and (2) it should not be necessary; - two ways to implement a thread package: in user space or in the kernel; - User-space threads: - for the kernel, there is just one proces; - can be implemented in a kernel that does not support threads; - doing thread switching in user space is at least an order of magnitude faster than trapping to the kernel; - no trapping is needed, no context switch, no TLB flushing needed; - each process can have its own specialized scheduler; - handling blocking calls is a problem, as if a system call is made, all threads will block; one solution is to use select to see if the call is going to block or not. If so, another thread is selected to run and the call is not done. - handling page faults is a similar problem; - as the kernel schedules process, a thread has to voluntarily give up the CPU; - probably the most devastating argument against ULTs: programmers want threads precisely in applications where the threads block often, such as web servers; - Kernel threads: - no run-time system needed; - no thread table in each process; it's kept in the kernel - thread operations are performed through kernel calls; - high cost; - performance gain: keep a set of treads; - scheduling is done on threads instead of processes; - Hibrid approach: - multiplex user level threads over a single kernel thread; - Scheduler activations - try to combine advantages of both worlds; - mimic the functionality of kernel threads but with the better performance of ULTs; - no special non-blocking system calls; - similary, when blocking one thread should allow other thread to proceed; - efficiency: avoid transitions from user to kernel space; - kernel assigns a certain number of virtual processors to each process and lets the run-time system allocate threads to processors; - virtual processes are added and removed from a process dynamically; - the basic idea is that when the kernel knows that a thread has blocked, the kernel notifies the process' run-time system passing as parameters on the stack the number of the thread in question and a description of the event; this is called an upcall; - the run-time then proceeds as usual; - when an interrupt occurs, the kernel sees if the event is related to the current thread. If not, nothing happens. If the event is related to the current thread, the thread is interrupted, the run-time is scheduled in the same virtual cpu and it may decide to change the current running thread. This may be cause, e.g., when a page fault for a thread in the same process is completed. - an objection to scheduler activations is the reliance on upcalls, a concept that violates the structure of layered systems; - good solution for critical regions: - no two processes may be inside their critical regions at the same time; - no assumptions may be made about speeds or the number of CPUs; - no process running outside its critical region may block other processes; - no process should have to wait forever to enter its critical region; - mutual exclusion with busy waiting: - disabling interrupts; - used for only 1 cpu; - users cannot do it; - lock variables (spin locks); - strict alternation; - "turn" variable; - process can block with no other process inside critical region; - Peterson's solution: - enter_region and leave_region routines; - TSL instruction;