Process - an instance of an executing program (multiple processes can simultaneously execute one program)
Process state - the current status of a process. Generally either running (on the CPU), ready (waiting for CPU to become free), waiting (blocked; waiting for resources or data). In practice, more states are used.
Thread - part of a process that can be executed independently to perform a task. thread scheduling is typically controlled by the process unless it is mapped to a lightweight process.
Lightweight process - like a processes in that it is schedulable by the kernel, but like a thread in that it shares an address space and resources with other related lightweight processes.
In general, when creating a new process, the kernel must allocate memory to provide the process with an independent address space. This includes zeroing the data segment, copying program code into the text segment, and providing memory for the program stack and heap (dynamic program memory).
However, this is not how process creation is typically implemented. In Linux, child processes are given the same resources as their parents (including the address space), but costly copying is avoided since child processes often exec() shortly after spawning and often don't make any modifications (writes) to the address space.
Copy-on-write is the term used to describe the process of initially avoiding duplication of the parents resources, and instead sharing physical pages between the parent and child until one of them tries to write to a page. At that time, a copy of the page is made and assigned to the writing process. Child processes can be created as lightweight processes, which allows the parent and child to share many of the process-specific data structures. Finally, a parent process can be blocked until a child exits or execs. This allows the parent and child to safely share the same address space, and is useful if it is known that the child will quickly exec() or will otherwise have a short lifespan.
The definition of a process context is heavily implementation dependent. In Linux, the registers (general purpose registers, floating point registers, stack pointer, program counter, memory and control registers) and the process descriptor contain the information that must be saved and restored each time a process is swapped. The process descriptor contains such information relating to the memory space (stack, data structures, and state information). Also, each time that a process switch occurs, cached items such as the data and instruction caches, and the translation lookaside buffer (TLB) must be invalidated. These items more or less make up a process context.
Swapping processes is a relatively expensive task. First, the process's context must be saved, and a switch must be made to the kernel. The kernel must then retrieve and load the saved context of the process that is next to run. Finally, the kernel must transfer control to the new process.
Threads are useful to a process in the same way that processes are useful to an OS--they allow more than one task to occur concurrently. What makes threads different from processes is that related threads share the same address space. Within one process can exist a number of threads that access common items such as global variables. Since threads share the same process state, there is much less overhead associated with swapping threads. However, an effort has to be made to ensure that proper modification is made to shared variables (see locking methods)
TAKEN FROM UNIX MAN PAGE
These threads share the same global memory (data and heap segments), but each thread has its own stack (automatic variables).
POSIX.1 also requires that all the threads of a process share:
As well as the stack, POSIX.1 specifies that various other attributes are distinct for each thread, including:
The following Linux-specific features are also per-thread:
The entire man page for pthreads can be found here with more information on how compliant threads should act.
Caveat: The specifics of thread implementation vary from system to system. The following describes general problems and solutions regarding threads.
Threads are often used when two different but related tasks need to occur concurrently. For example, a process might accept IO from a user via the keyboard and also might also access the file system. The intended result of using multiple threads would be allow one thread to perform IO even if the other is blocked and waiting for data (possibly from disk!). The way in which threads are implemented is very important.
Implementing threads entirely in user space and having them scheduled by the process would be ideal for performance. Not requiring that context switches occur with the kernel makes thread switching very quick (remember that context switches require switching from the process to the kernel and back to the process). However, this implementation will make the kernel believe that when it receives a blocking request that the entire process should be put in the waiting state (since it is unaware of the existence of threads).
A lightweight process (LWP) is basically a thread that the kernel is aware of and schedules, but does not contain much of the overhead associated with a normal process. Since LWPs live in the kernel space, many of their operations require system calls (which can be expensive). Another implication is that the number of LWPs that exist is limited by the availablility of kernel space. Switching between LWPs is also more expensive than switching between threads controlled by the process, but it also avoids the drawbacks listed above. The terms thread and lightweight process are sometimes used interchangeably because of how they are managed.
Coming soon...