Computers today can realize a lot of useful features and with some ilusion to be at the same time. Let's see some things we can made:
- Setup a CRON to run every day at 12AM to check our emails
- Navigate through internet while we maintain an Obsidian in another window with some text
- See the actual date time, in real time
- Listen to music
Everything at the same time. How the computer can make it all, if it haves only one processor, and that processor can execute one instruction per time?
Well, the processor can execute only one instruction per time, but it can provide some ilusion to run more than one thing and switch between tasks to advance each of them a bit. Let's deep dive into the fundamentals of this working.
Instructions
The fundamental unit of work a CPU can process is an instruction. An instruction is simply a description of a command that, when the CPU receives and read the description, performs the actual command.
CPU can receive instructions with data to perform useful actions, like adding 2 plus 2, making division calcs and many more. Instructions can be composed into a chain of instructions, on a virtual unlimited length. A set of instructions is denominated a program.
Program
The program is a set of instructions in a sequence that a CPU can perform.
Each instruction, by itself, is only an instruction, but when you chain 10, 12, and thousands of instructions together, they can compose really great applications and programs that perform so many actions.
Some programs are not autonomous, but are utilities that enables the processor to do other things that it cannot do alone, like talk to USB devices, talk to the network, etc.
Interrupts
The processor can receive some signalling from others hardware components, to allow communication between them. By receiving the signal, the processor can choose how to react to them. This signals can be conceived as events, but their technical names are Interrupts.
A lot of hardware pieces can signal the processor to request to handle some data. SSD's, keyboard inputs, GPU's can activate Interrupts, and, by the type, the processor can stop anything what it's making to handle the signal.
Let's say that the processor requested the SSD some data to be loaded, like opening a file. The SSD locate the file inside the filesystem, and then, signal the processor that the data is ready. The processor can, accordingly to the function where the interrupt handler is pointed too, ignore it initially to execute some program with higher priority, or can take this data and execute the program that initially requested the data.
Another interesting type of interrupt is called the Timer Interrupt. The motherboard is shipped with a timer chip or other hardware machinery with the function to count the time, and when it achieves some interval, it sends a signal to the CPU, activating an Interrupt. The processor, then, handles this signal accordingly to the function pointer for this interrupt type.
Traps
A trap is some instruction that signals to CPU to raise it's privilege levels. Let's say a CPU often runs in Ring 2 or 3. When he receives a trap instruction, the CPU switchs to Ring 0 or 1 to run privileged instructions.
Syscalls are great examples of trap execution. Everytime your program calls the syscall
instruction (e.g. open
syscall), your CPU is traped, and then switch the to more privileged levels to communicate with hardware and see with the scheduler what it should do now.
Interrupts traps CPU too. When the CPU is signaled by some hardware, it runs instructions in privileged levels to load the data from hardware or handle the signals.
Context switching
Everytime the CPU is trapped, a context switch occurs.
A context switch is an action the CPU takes to save the actual execution state.
The operating system tracks the actual execution by using the Process Control Block data structure. The PCB contains the pointer to the next instruction to be executed, some data of the program, the cpu registers, etc.
The context switch will execute some assembly instructions to save all this data to a dedicated data structure in memory to replace it with another execution state and continue from there.
Schedulers
I think that the most fundamental component of the Operation System is the scheduler, but it needs some hardware machinery to work properly.
The main function of the scheduler is to prioritize and tell the processor which process will run next.
Let's say the processor now is running the text editor, but it needs to play the music too in the background. So, when the Timer Interrupt or some syscall (like, saving the file with write
) is called, the CPU is trapped, and then, it saves the actual process context, and switch to privileged mode.
The scheduler will see that the music player process has some priority to be executed next, so it schedules them to the processor. The processor then loads the process state, and goes to normal mode to execute it.
Fundamentals of Concurrency
The Operating System
Whereas the CPU can run many instructions as a program, it's the Operating System that is the main program that enables many programs to run at the same time, by providing some important abstractions.
The Operating System provides all the glue code that make the CPU usage and programming more friendly, and lifts the weight of managing times and memory from them.
Proccess
We already see that a set of instructions can be denominated a program. Inside the Operating System, when we run some program, inside of it it's denominated a process.
A process is an abstraction where the OS provide the complete execution context, where the instructions are runned in a blackbox fashion: the program in it cannot run certain instructions and cannot talk directly to other devices. When the program needs data or an action from another device, it calls the OS, using syscalls. The syscall is a trap that make the processor raise it's privilege levels and handles the control to the OS. The OS now communicate to the other devices what the program is requesting, and, when the device returns, the OS returns to the process with the actual response.
Here, the execution context encompass everything the program needs to run: the records that points to the sequence of instructions, the virtual memory, the scheduling control,etc.
Thread
If a process is a running program, threads are lightweight processes.
When a program runs, the system creates the execution context for the new process, and the process runs inside this execution context. But the process can partition it's execution instruction sequence to have two independent sequences to run.
These instruction sequences here are called a thread, and a thread have a narrower execution context than a process. In fact, the thread have the records that points to the instruction sequence, but it's virtual memory are the same as the process.
The concurrent system
Now, we have all the pieces to describe a concurrent system. Let's say we have 2 processes and each have 2 threads.
The operating system schedules the thread P1T1. When the thread P1T1 make a syscall, the CPU is trapped and the OS takes control. When the OS make the call for the data thread P1T1 requested, it schedules the thread P2T1 and continue from there.
Thread P2T1 makes a syscall, and the OS handles it. The OS schedules the thread P2T2 and continues from there.
Here, each thread are going for the CPU time and Memory space, making it a basic concurrent system. It's concurrent because each thread here will fight for cpu time and memory comsuption here. The scheduller will schedulle one thread by time, and the others will lie in wait, awaiting for they time to advance in execution.