System Calls
================
A system call is a request made by a process to the operating system (OS) to perform a specific operation. In Linux, system calls are used to interact with the kernel and request services such as file I/O, process creation, or memory management.
Types of System Calls
There are several types of system calls in Linux:
- Process control: These system calls allow processes to be created, terminated, or modified. Examples include `fork()`, `execve()`, and `kill()`.
- File and I/O operations: These system calls allow programs to interact with the file system, such as reading and writing files, creating directories, or deleting files. Examples include `open()`, `read()`, and `write()`.
- Informational queries: These system calls provide information about the system, such as the current process ID, CPU usage, or memory allocation. Examples include `getpid()` and `getrusage()`.
- Communication and synchronization: These system calls allow processes to communicate with each other, such as sending signals or creating semaphores. Examples include `kill()`, `semctl()`, and `msgsnd()`.
How System Calls Work
When a process makes a system call, the following steps occur:
1. Trap: The process executing the system call traps the CPU by generating an interrupt.
2. Interrupt handling: The kernel handles the interrupt by saving the current state of the process (registers and stack) and jumping to the interrupt handler routine.
3. System call dispatch: The interrupt handler routine dispatches the system call to the appropriate kernel module or function.
4. System call execution: The kernel module or function executes the system call, performing the requested operation.
5. Return from trap: The kernel returns control to the process, which resumes executing where it left off.
Real-World Example: Using `open()` and `write()`
Suppose you are writing a program that writes log messages to a file. Your program would use the `open()` system call to create a file descriptor for the log file, followed by the `write()` system call to write the message to the file.
```c
#include
#include
#include
int main() {
int fd = open("log.txt", O_WRONLY | O_CREAT, 0644);
if (fd == -1) {
perror("open");
return 1;
}
char* message = "Something important happened!";
write(fd, message, strlen(message));
close(fd);
return 0;
}
```
Interrupts
================
Interrupts are a way for the kernel to notify processes of events that require attention. In Linux, interrupts are triggered by hardware devices or software signals.
Types of Interrupts
There are several types of interrupts in Linux:
- Hardware interrupts: These interrupts are generated by hardware devices such as disk drives, network cards, or keyboard controllers.
- Software interrupts: These interrupts are generated by software signals such as timer expiration or process termination.
- Trap interrupts: These interrupts are generated by the CPU itself when a system call is executed.
How Interrupts Work
When an interrupt occurs:
1. Interrupt generation: The hardware device or software signal generates an interrupt.
2. Interrupt handling: The kernel handles the interrupt by saving the current state of all processes (registers and stack) and jumping to the interrupt handler routine.
3. Interrupt dispatch: The interrupt handler routine dispatches the interrupt to the appropriate kernel module or function.
4. Interrupt execution: The kernel module or function executes the interrupt, performing the requested operation.
5. Return from interrupt: The kernel returns control to the processes that were interrupted, which resume executing where they left off.
Real-World Example: Handling a Disk I/O Interrupt
Suppose you are writing a program that reads data from a disk drive. When the disk drive completes its I/O operation, it generates an interrupt to notify your program of the completion.
```c
#include
#include
#include
#include
int main() {
int fd = open("data.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
char buffer[1024];
// Read data from disk drive
read(fd, buffer, 1024);
close(fd);
return 0;
}
```
When the disk drive completes its I/O operation, it generates an interrupt that is handled by the kernel. The kernel then notifies your program of the completion through a signal or by updating a shared memory region.
Theoretical Concepts: Context Switching
Context switching occurs when the kernel switches between two processes, saving and restoring their registers and stack. This process requires the following steps:
1. Save current context: Save the registers and stack of the current process.
2. Restore new context: Restore the registers and stack of the new process.
3. Jump to new process: Jump to the new process's starting address.
Context switching is a costly operation in terms of CPU cycles, so the kernel tries to minimize it by using techniques such as:
- Process scheduling: Scheduling processes to run in parallel or on different CPUs to reduce context switching.
- Thread-level speculation: Speculating that a thread will continue executing without needing to switch contexts.
Theoretical Concepts: Interrupt Latency
Interrupt latency refers to the time it takes for an interrupt to be handled by the kernel. This includes the time it takes for the interrupt to be generated, dispatched, and executed.
Low interrupt latency is important because it allows processes to respond quickly to events such as disk I/O completion or network packets arrival. High interrupt latency can lead to poor system performance and responsiveness.
Theoretical Concepts: Interrupt Coalescing
Interrupt coalescing is a technique used by the kernel to reduce interrupt latency. It works by combining multiple interrupts into a single interrupt, reducing the number of times the kernel needs to handle an interrupt.
For example, if a disk drive generates multiple I/O completion interrupts in rapid succession, the kernel can combine these interrupts into a single interrupt, reducing the overhead of handling each individual interrupt.
Summary
----------------
In this sub-module, we have explored the concepts of system calls and interrupts. System calls allow processes to interact with the kernel, while interrupts notify processes of events that require attention. Understanding how system calls and interrupts work is important for developing efficient and responsive Linux systems.