processes
threads
In linux, some processes are divided into pieces called threads. Threads are very similar to processes. They have an identifier, TID (thread Id) and the kernel schedules and runs threads, just like processes. Processes do not usually share memory and I/O connections, threads do. All threads inside a single process share the same system resources.
Many processes have only one thread. A process with only one thread is known to be single-threaded. All processes start out single-threaded. This starting thread is often known as the main thread, and typically corresponds to a 'main' function within a program. This main thread is capable of starting new threads, depending on how the application is written. This is known multi-threading. Threads can run simultaneously on multiple processors/cores, speeding up computation. Threads start faster than processes and communicate more efficiently than processes. This is because threads share memory to communicate amongst themselves, and processes depend on IPC (inter-process communication) calls to communicate.
By default, the output of the `ps` and `top` commands do not show threads, only processes. However, you can modify this behavior:
ryan:// $ ps m |grep httping -A10
1121895 pts/1 - 0:00 httping -delay 2 www.google.com
- - Sl+ 0:00 -
- - Sl+ 0:00 -
- - Sl+ 0:00 -
- - Sl+ 0:00 -
- - Sl+ 0:00 -
- - Sl+ 0:00 -
- - Sl+ 0:00 -
- - Sl+ 0:00 -
This is an example of a multi-threaded golang app. The top line with the PID represents the process, and each line below represents a thread within the process.
adjusting process priority
You can change the way the kernel allocates CPU time to a process, relative to other processes. The kernel runs each process according to it's scheduling priority, known as it's `nice` value. This can be a value in range -20 - +19, with -20 being the highest priority. You can see this value for each process using `top` (the PR column). A regular user can only set nice values between 0 and 19, anything below 0 must be set by a superuser. Child processes will inherit the nice value of their parent. Use `ps -l` or `ps -lax` to view the niceness of a process.
use `renice` to change the niceness value of an *existing* process:
[ryan@nebula /]# renice -n 11 83883
83883 (process ID) old priority 10, new priority 11
Context Switching
The OS performs context switching, or swapping process state on the CPU, as the primary mechanism behind multiprogramming (or time sharing). There are two main steps to context switching: 1) The OS Saves the context of the current process running on the CPU, including all of its register values (PC, stack pointers, general purpose registers, condition code, etc.), its memory state, and some other state (open files, etc.) 2) The OS restores the saved context from another process on the CPU and starts the CPU running this other process, continuing its execution from the instruction where it left off.
Process State
In multiprogrammed systems, the OS must track and manage the multiple processes existing in the system at any given time. The OS maintains information about each process, including: 1) The process ID (PID) 2) The address space information for the process 3) The execution state of the process (CPU register values, stack location, etc.) 4) The set of resources allocated to the process (open files) 5) The current process state (ready, running, blocked, exited) 1) Ready - the process could run on the CPU but it is not currently scheduled 2) Running - The process is scheduled on the CPU and is actively executing 3) Blocked - The process is waiting for some event before it can continue being executed (waiting for data to be read from disk, etc.) 4) Exited - The process has exited but has not yet been cleaned up.
Creating Processes
In Unix, the fork system call is used to create a new process. The process calling fork is the parent process and the new process is the child process. When fork() is called, the program must determine if it is the parent or the child process (typically using getpid()), and then determine how to proceed. If you want concurrency in your program, calling fork() is enough. However, to run a different image, the child process must call exec() (or one of itβs variants). After calling fork(), the program counter for both the parent and the child are the same. Once exec() is called, the parent process is wiped from memory of the child process address space.
Process Descriptors
Linux maintains a process descriptor, which is a structure where the linux kernel maintains information about a single process. It contains all the information needed by the scheduler to maintain the process state machine.