Process: Instance of a program being executed.
Parts of a Process:
Program v.s. Process: Programs are passive entities, stored on the disk. Processes are active entities.
Structuring an application as processes allows for independence from:
Process Control Block (PCB): The concrete representation of a process.
Two ways to efficiently organize PCBs:
Linux solves this by adding two more fields for “left and right sibling”, resulting in:
parent: Points to process’s parent (same as before)first_child: Points to process’s first childyounger_sibling (left sibling): Points to sibling of process, created immediately following this process.older_sibling (right sibling): Points to sibling of process, created immediately prior to this process.Why? — Linked-list introduces a lot of performance issues and dynamic memory management issues.
- This implementation improves time efficiency and doesn’t need dynamic memory management (and space efficiency remains the same).
Typical Process States:
Newly created processes begin in the Ready state.
void main()
{
printf("Hello world");
}State Sequence:
printf)Context Switch: When CPU transfers control from one process to another.
CPU State: All intermediate values held in any CPU registers and hardware flags at the time of interruption.
Note: The PCB contains an up-to-date copy of CPU state only when a process’s state is ready or blocked!
Example Steps:
- Save state of old process (P1)
- Load state of new process (P2)
Process Scheduler: Selects among available processes for next execution on CPU.
Why? — Process schedulers exist to aid the goals of:
- Multiprogramming: Have some process running at all times to maximize CPU utilization.
- Timesharing: Switch the CPU among processes frequently so that users can interact with each program while it’s running.
Processes migrate among the various queues.
Selects which process should be executed next and allocates CPU.
Selects which processes should be brought into the ready queue.
More on Process Mix:
Processes can be described as either:
- I/O-Bound: Spends more time doing I/O than computation, lots of short CPU bursts; or
- CPU-Bound: Spends more time doing computations, few but very long CPU bursts.
These must be mixed to avoid starving the other.
Suspends process from main memory to achieve higher degrees of multiprogramming.
More on Swapping Out — When a process is swapped out, its entire memory image (Text, Data, Stack, and Heap sections) is moved from main memory (RAM) to a backing store (like a disk).
- The PCB, however, is retained. Its state is just updated to “Suspended.”
When swapping back in, the memory image is loaded back into RAM (not necessarily in the same location), and the process eventually becomes Ready again.
There are other operations as well.
Parent process creates a child process, which can create other processes, forming a tree of processes.
fork() and exec() System Callsfork(): Copies variables and registers from parent to child.
exec(): Can be used after fork to replace process’s memory space with a new program.Example: fork and exec
pid = fork();
if (pid < 0) {
// Error
return 1;
} else if (pid == 0) {
// Child process
execlp("/bin/ls","ls",NULL);
} else {
// Parent process
wait(NULL);
printf("Child complete");
}
return 0;
Design Choice — Can children live without their parents?
Some OSes don’t allow children to exist if their parent is terminated.
- If this design choice is made, it can lead to cascading termination.
- Termination would be initiated by the OS.
wait() System Callpid = wait(&status)wait(): Returns status information and PID of process.
wait().Two Scenarios:
wait)waitConcurrently executing processes within a system may be:
Why do IPC?
- Information Sharing: Several applications may be interested in the same piece of info (e.g., clipboard)
- Computation Speedup: Some tasks could be split into subtasks to be executed in parallel (for computers with multiple cores)
- Modularity: We may want to construct the system in a modular fashion and divide system functions into separate processes or threads.
Two models of IPC:
Shared Memory v. Message Passing:
- Message passing is more complex and slower. Good for small messages.
- Shared memory makes data synchronization the programmer’s problem.
Producer makes info used by the consumer
Shared Memory is a solution to the producer-consumer problem.
Naive Producer:
item next_produced;
while(true) {
while (((in + 1) % BUFFER_SIZE == out)
; /* do nothing */
buffer[in] = next_produced;
in = (in + 1) % BUFFER_SIZE;
}Naive Consumer:
item next_consumed;
while (true) {
while (in == out)
; /* do nothing */
next_consumed = buffer[out];
out = (out + 1) % BUFFER_SIZE
/* consume the item in next_consumed */
}This code uses the following:
in: points to next free position in arrayout: points to first full position in arrayin == out(in + 1) % BUFFER_SIZE == outThis code doesn’t synchronize the producer and consumer, so if the consumer tries to consume an item that hasn’t been produced, it’ll fail. We’ll discuss process synchronization in a future chapter, as this has just been a short sample.
Message System: Processes communicate without sharing variables.
IPC provides two operations:
SendReceiveSynchronization: Message passing may blocking or non-blocking.
1. Blocking (Sync)
2. Non-Blocking (Async)