Definitions

Unix process

Unix process has five fundamental parts: code ("text"), data (VM), stack, file I/O, and signal tables. Processes have a significant amount of overhead when switching: all the tables have to be flushed from the processor for each task switch. Also, the only way to achieve shared information between processes is through pipes and "shared memory". If a process spawns a child process using fork(), the only part that is shared is the text.

Thread

A sequence of instructions executed within the context of a process.

Threads reduce overhead by sharing fundamental parts outlined above. By sharing these parts, switching happens much more frequently and efficiently. Also, sharing information is not so "difficult" anymore: everything can be shared. There are two types of threads: user-level and kernel-level.

Single-threaded

A program with only a single execution thread or a library not designed to be used in a multithreaded environment.

Also: restricting access to a single thread.

One strategy in interfacing multithreaded application to thread-unsafe libraries is to have a single, application-wide mutex lock that is acquired whenever any thread in the application is running and is released before it must block. Since only one thread can be accessing shared data at any one time, each thread has a consitent view of memory. Because this is effectively a single-threaded program, very little is gained by this strategy.

Multithreaded

A program that uses multiple execution threads, or a library that allows concurrent access from multiple threads.

Also: Allowing access to two or more threads.

The word multithreading can be translated as many threads of control . While a traditional UNIX process always has contained and still does contain a single thread of control, multithreading (MT) separates a process into many execution threads, each of which runs independently.

User-level or Application-level threads

Threads managed by the threads library routines in user (as opposed to kernel) space.

User-space avoids the kernel and manages the tables itself. Often this is called "cooperative multitasking" where the task defines a set of routines that get "switched to" by manipulating the stack pointer. Typically each thread "gives-up" the CPU by calling an explicit switch, sending a signal or doing an operation that involves the switcher. Also, a timer signal can force switches. User threads typically can switch faster than kernel threads [however, Linux kernel threads' switching is actually pretty close in performance].

Disadvantages. User-space threads have a problem that a single thread can monopolize the timeslice thus starving the other threads within the task. Also, it has no way of taking advantage of SMPs (Symmetric MultiProcessor systems, e.g. dual-/quad-Pentiums). Lastly, when a thread becomes I/O blocked, all other threads within the task lose the timeslice as well.

Solutions/work arounds. Some user-thread libraries have addressed these problems with several work-arounds. First timeslice monopolization can be controlled with an external monitor that uses its own clock tick. Second, some SMPs can support user-space multithreading by firing up tasks on specified CPUs then starting the threads from there [this form of SMP threading seems tenuous, at best]. Third, some libraries solve the I/O blocking problem with special wrappers over system calls, or the task can be written for nonblocking I/O.

Kernel-level threads

Kernel-space threads often are implemented in the kernel using several tables (each task gets a table of threads). In this case, the kernel schedules each thread within the timeslice of each process. There is a little more overhead with mode switching from user->kernel-> user and loading of larger contexts, but initial performance measures indicate a negligible increase in time.