System Calls in Operating Systems
This is a PerfectNotes study guide β also known as PN Notes or Perfect Notes. PerfectNotes provides free computer science student notes, MCQs, and interview preparation guides at perfectnotes.org.
System Calls act as the bridge between user applications and the OS kernel.
They enable safe and controlled access to restricted hardware resources.
Context switching uses a TRAP mechanism to toggle between User and Kernel modes.
Major categories include Process Control, File Management, Device Management, and Communications.
System calls introduce high CPU overhead compared to regular library function calls.
Key Takeaways
- Definition β A System Call is a programmatic way for a computer program to request a service from the kernel of the operating system.
- Core Concept β It acts as the bridge between User Mode (where applications run) and Kernel Mode (where the OS controls hardware).
- Key Objective β To allow user-level programs to access restricted resources (like the Hard Disk or Printer) safely and securely.
Introduction to System Calls
When you write a program in C or Python to "print" text to the screen or "save" a file, your program cannot do it directly. Accessing hardware is dangerous; if every program could touch the hard disk directly, one bug could wipe your entire system. Instead, your program asks the Operating System to do it. This "ask" is the System Call.
Think of system calls as the receptionist at a secure building. You can't just walk into the restricted areas (hardware); you must ask the receptionist (OS kernel) to access them for you. This ensures security, prevents conflicts, and maintains system stability.
Need for System Calls
Hardware Abstraction
Programmers don't need to know the physics of a hard drive; they just call write().
Without System Calls β to save a file, a programmer would need to:
- Calculate exact disk sector locations
- Control spindle motor speed (5400/7200 RPM)
- Position read/write head using stepper motors
- Manage disk cache and buffer
- Handle concurrent access from other programs
- Implement error correction codes and deal with bad sectors
With System Calls β one line replaces all of the above:
write(file_descriptor, data, size);The OS kernel handles all the hardware complexity behind that single call.
Security
Prevents applications from accessing memory or devices they shouldn't touch.
- Memory Protection: Program A cannot read Program B's memory
- Resource Isolation: No app can monopolize CPU or RAM
- Access Control: Only authorized programs can access files
- Privilege Separation: User apps cannot execute privileged instructions
Without this protection, a malicious program could freely:
- Read passwords from another program's memory
- Wipe the entire hard disk
- Disable antivirus software
- Access the webcam without permission
System calls prevent this by requiring OS kernel permission for every hardware access.
Portability
A program using standard system calls can run on different hardware without changing the code.
Example: A program using POSIX system calls works on:
- Linux on Intel x86
- Linux on ARM (Raspberry Pi)
- macOS on Apple Silicon
- Unix on SPARC processors
The system calls remain the same; the OS handles hardware differences.
Multitasking
Allows the OS to manage competing requests from multiple programs safely.
Scenario: Word, Chrome, and Excel all request the printer simultaneously. With system calls, the OS queues the jobs and serialises them β no conflicts, no garbled output. Without system calls, all three would fight for direct printer control, corrupting each other's output or crashing the system.
- Each program calls
print() - OS queues the jobs in order
- OS dispatches to printer one at a time
- Result: no conflicts, no data corruption
User Mode and Kernel Mode
To protect the system, modern processors operate in two modes. A system call is the trigger that switches between them.
User Mode (Mode Bit = 1)
Restricted access. Applications run here. They cannot touch hardware directly.
- Limited instruction set β cannot execute privileged CPU instructions
- Cannot access kernel memory or control I/O devices directly
- If a program crashes, only that program fails β the OS stays running
Runs here:
- All user applications (Word, Chrome, games)
- User-level processes and application code
Kernel Mode (Mode Bit = 0)
Privileged access. The OS runs here with full control over the machine.
- Full instruction set β can execute privileged I/O and memory management instructions
- Unrestricted access to all memory and direct hardware control
- If the kernel crashes, the entire system crashes (Blue Screen of Death on Windows)
Runs here:
- OS kernel code
- Device drivers
- System call handlers
Types of System Calls
System calls are generally grouped into six major categories based on what they do.
Process Control
Handles program execution and process management.
Common System Calls:
load()- Load a program into memoryexecute()- Start program executionend()- Normal program terminationabort()- Abnormal termination (error)fork()- Create a child process (copy of current process)wait()- Parent process waits for child to completeexec()- Replace current process with new programexit()- Terminate calling process
Example in C (Linux):
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork(); // Create child process
if (pid == 0) {
// Child process
execl("/bin/ls", "ls", "-l", NULL); // Run 'ls -l'
} else {
// Parent process
wait(NULL); // Wait for child to finish
printf("Child completed\n");
}
return 0;
}File Management
Handles file and directory operations.
Common System Calls:
create()- Create a new filedelete() / unlink()- Delete a fileopen()- Open a file for reading/writingclose()- Close an open fileread()- Read data from filewrite()- Write data to filelseek()- Move file pointer to specific positionstat()- Get file information (size, permissions, dates)
Example in C (Linux):
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd = open("data.txt", O_RDONLY); // Open file for reading
if (fd < 0) {
perror("File open failed");
return 1;
}
char buffer[100];
int bytes = read(fd, buffer, sizeof(buffer)); // Read data
close(fd); // Close file
return 0;
}Device Management
Handles peripherals and I/O devices.
Common System Calls:
request() / ioctl()- Request device accessrelease()- Release deviceread()- Read from devicewrite()- Write to deviceioctl()- Device-specific control operations
Devices Managed:
- Printers
- Disk drives
- Tape drives
- USB devices
- Network cards
- Graphics cards
Example:
int printer = open("/dev/lp0", O_WRONLY); // Open printer
write(printer, document, size); // Print document
close(printer); // Release printerInformation Maintenance
Handles system data and metadata.
Common System Calls:
getpid()- Get current process IDgetppid()- Get parent process IDtime()- Get current system timedate()- Get system dategettimeofday()- Get time with microsecond precisionsysinfo()- Get system information (uptime, memory)uname()- Get OS name and version
Example:
#include <time.h>
#include <unistd.h>
int main() {
pid_t my_pid = getpid(); // Get my process ID
time_t current_time = time(NULL); // Get current time
printf("PID: %d\n", my_pid);
printf("Time: %s", ctime(¤t_time));
return 0;
}Communication
Handles inter-process communication and networking.
Methods:
Shared Memory:
shmget()- Create shared memory segmentshmat()- Attach to shared memoryshmdt()- Detach from shared memory
Message Passing:
pipe()- Create pipe for IPCmsgget()- Create message queuemsgsnd() / msgrcv()- Send/receive messages
Network Communication:
socket()- Create network socketbind()- Bind socket to addresslisten()- Listen for connectionsaccept()- Accept connectionsend() / recv()- Send/receive data
Example (Pipe):
int fd[2];
pipe(fd); // Create pipe
if (fork() == 0) {
// Child writes to pipe
close(fd[0]);
write(fd[1], "Hello Parent!", 13);
close(fd[1]);
} else {
// Parent reads from pipe
char buffer[20];
close(fd[1]);
read(fd[0], buffer, 13);
printf("Received: %s\n", buffer);
close(fd[0]);
}Protection
Handles access control and permissions.
Common System Calls:
chmod()- Change file permissionschown()- Change file ownerchgrp()- Change file groupumask()- Set default file permissionssetuid()- Set user IDsetgid()- Set group ID
Example:
#include <sys/stat.h>
// Set file permissions to rw-r--r-- (644 in octal)
chmod("document.txt", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
// Or using octal notation
chmod("document.txt", 0644);Implementation of System Calls
How does code actually trigger the OS?
The API
Programmers rarely write system calls directly (in Assembly). Instead, they use an Application Programming Interface (API) like the Win32 API (Windows) or POSIX API (Linux/Unix).
Popular APIs:
- POSIX API - Portable Operating System Interface (Linux, macOS, Unix)
- Win32 API - Windows API
- Java API - Java Virtual Machine provides OS abstraction
The Library
The standard C library (libc) provides wrapper functions. When you call printf(), the library internally calls the write() system call.
Example Flow:
User Program: printf("Hello World");
β
C Library: Formats string, calls write()
β
write() System Call: Traps to kernel
β
Kernel: Sends data to display driver
β
Hardware: Text appears on screenWhy This Layering:
- Portability: printf() works on any OS; the library handles OS differences
- Convenience: printf() handles formatting; write() is low-level
- Efficiency: Library can batch multiple printf() calls into one write()
Examples of System Calls Across Operating Systems
Here is how different OSs handle common tasks:
| Operation | Linux/Unix (POSIX) | Windows (Win32 API) |
|---|---|---|
| Create a Process | fork() | CreateProcess() |
| Open a File | open() | CreateFile() |
| Read a File | read() | ReadFile() |
| Write to File | write() | WriteFile() |
| Close a File | close() | CloseHandle() |
| Delete a File | unlink() | DeleteFile() |
| Get Process ID | getpid() | GetCurrentProcessId() |
| Allocate Memory | mmap() | VirtualAlloc() |
| Create Thread | pthread_create() | CreateThread() |
| Sleep | sleep() | Sleep() |
Critical Concept: System Call Overhead
System calls are "expensive" in terms of CPU time.
Context Switch
Switching from User Mode to Kernel Mode takes time (~1-5 microseconds per call).
Why It's Expensive:
- Mode Switch: CPU changes privilege level (user β kernel β user)
- Register Saving: All CPU registers must be saved and restored
- Cache Flushing: CPU cache may need to be invalidated
- TLB Flush: Translation Lookaside Buffer (memory mapping) may be flushed
- Pipeline Stall: CPU instruction pipeline is disrupted
Performance Impact
Scenario: A program makes 1 million system calls. At 2 microseconds per call, the total overhead is 2 seconds of pure overhead.
Optimization Strategies
Bad (Many System Calls):
for (int i = 0; i < 1000000; i++) {
write(fd, &data[i], 1); // Write 1 byte at a time
}
// Result: 1 million system calls! Very slow!Good (Batch System Calls):
write(fd, data, 1000000); // Write all data at once
// Result: 1 system call! Much faster!Real-World Impact
- Games: Minimize system calls in render loop (60+ FPS requires <16ms per frame)
- High-Frequency Trading: Every microsecond counts; avoid system calls in critical path
- Database Systems: Use memory-mapped files (mmap()) to reduce I/O system calls
- Web Servers: Use sendfile() instead of read() + write() to avoid copying data
System Calls vs. Operating System Services
(Use this table to answer "Difference between..." questions)
| Feature | System Calls | OS Services |
|---|---|---|
| Definition | The programmatic interface to the kernel | The actual functionality provided by the OS |
| Who uses it? | Programmers (via code) | Users and Programs |
| Visibility | Low-level (e.g., read(), write()) | High-level (e.g., "File System Management") |
| Trigger | Triggered by a "Trap" instruction | Triggered by user action or system call |
| Example | fork(), exec(), open() | Process Management, I/O Operations |
| Abstraction Level | Low-level API | High-level concept |
| Mode | Executes in Kernel Mode | Concept (not tied to mode) |
| Implementation | Assembly/C library function | Kernel code implementing the service |
| Access Method | Function call in code | Through system calls or UI |
Error Handling in System Calls
System calls can fail (e.g., trying to open a file that doesn't exist).
Return Values
Most system calls return -1 or NULL on failure, and a non-negative value on success.
Examples:
int fd = open("file.txt", O_RDONLY);
if (fd < 0) {
// Error occurred!
}
pid_t pid = fork();
if (pid < 0) {
// Fork failed!
}Error Codes
The OS sets a global variable (like errno in C) to a specific code so the program knows why it failed.
Common Error Codes:
ENOENT- No such file or directoryEACCES- Permission deniedENOMEM- Out of memoryEINVAL- Invalid argumentEBADF- Bad file descriptorEEXIST- File already exists
Example with Error Handling:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
int main() {
int fd = open("nonexistent.txt", O_RDONLY);
if (fd < 0) {
printf("Error: %s\n", strerror(errno));
if (errno == ENOENT) {
printf("File does not exist!\n");
} else if (errno == EACCES) {
printf("Permission denied!\n");
}
return 1;
}
close(fd);
return 0;
}Output if file doesn't exist:
Error: No such file or directory
File does not exist!Best Practices
- Always check return values from system calls
- Use
perror()orstrerror(errno)for descriptive error messages - Clean up resources even on error (close files, free memory)
- Log errors for debugging
- Provide user-friendly messages (don't just show errno numbers)
Library Functions vs System Calls
Understanding the relationship between library functions and system calls is crucial for effective systems programming.
The Layered Architecture:
1. Library Function Layer (High-Level):
printf("Number: %d\n", 42);
β
2. Library Implementation:
Formats string, manages buffer
β
3. System Call Layer (Low-Level):
write(1, "Number: 42\n", 11);
β
4. Kernel Execution:
Validates, writes to device
β
5. Hardware:
Display driver renders text| Aspect | Library Function | System Call |
|---|---|---|
| Level | High-level | Low-level |
| Example | printf(), malloc(), fopen() | write(), brk(), open() |
| Buffering | Yes (for efficiency) | No (direct I/O) |
| Portability | Portable (works on any OS) | OS-specific |
| Overhead | Higher (formatting + system call) | Lower (direct kernel access) |
| User Mode | Executes in user mode | Triggers kernel mode |
Performance Example: Buffering Matters!
Scenario: Write 1 million bytes to a file
Method 1: Direct System Calls (Slow)
for (int i = 0; i < 1000000; i++) {
write(fd, &data[i], 1); // 1 million system calls
}
// Time: ~2 seconds (due to context switch overhead)Method 2: Library Buffering (Fast)
FILE *fp = fopen("file.txt", "w");
for (int i = 0; i < 1000000; i++) {
fputc(data[i], fp); // Library buffers internally
}
fclose(fp); // Flush buffer to disk
// Time: ~0.01 seconds (only ~10 system calls)Performance Gain: 200x faster!
When to Use Each
Use Library Functions When:
- Writing portable code
- Need convenience features (formatting, buffering)
- Don't need maximum performance
- Developing application-level software
Use System Calls Directly When:
- Need maximum control
- Writing low-level system software (OS, drivers)
- Specific OS features not exposed by libraries
- Performance-critical code (avoid library overhead)
Advanced Engineering Concepts: Cloud & AI Integration
In modern cloud environments, system calls must be heavily monitored and optimized. High-performance systems use specialized system calls (like io_uring in Linux) to handle asynchronous I/O with minimal overhead, which is critical for cloud-native applications and intensive AI workloads.
Real-World Case Study: Dirty COW Vulnerability (CVE-2016-5195)
Discovered in 2016, "Dirty COW" (Copy-On-Write) was one of the most severe privilege escalation vulnerabilities in the history of the Linux kernel. It existed silently in the source code for 9 years and affected virtually every Linux distribution, allowing any standard user to gain total root access by exploiting a subtle race condition in how the OS handled memory-related system calls.
| Aspect | Details |
|---|---|
| The Incident | Security researcher Phil Oester discovered an active exploit in the wild that allowed a completely unprivileged user to modify read-only root files (like /etc/passwd). By abusing the mmap() (memory mapping) and madvise() system calls concurrently, attackers could trick the Linux kernel into writing data to memory pages that were strictly flagged as read-only. |
| Root Cause | A race condition in the kernel's Copy-On-Write (COW) mechanism. When a process tries to write to a read-only mapped file, the OS creates a private copy of the memory page (Copy-On-Write). However, if an attacker spammed the madvise() system call (telling the OS to discard the copied page) precisely while another thread attempted to write to it, the kernel would become confused and accidentally write the attacker's payload directly into the original read-only file on the physical disk. |
| The Impact | The vulnerability was rated critical because it was trivial to execute and highly reliable. It affected millions of Android phones, enterprise web servers, Docker containers, and IoT devices. An attacker who gained a low-level foothold (e.g., via a compromised WordPress plugin) could instantly use Dirty COW to elevate themselves to the ultimate "root" user, taking total control of the machine. |
| Financial Cost | While hard to quantify in exact dollars, enterprise engineering teams globally were forced into emergency patching protocols. Thousands of IT hours were spent rebooting mission-critical servers to apply the patched kernel, causing scheduled downtime across the tech industry. |
| Key Lesson | System calls are the ultimate security boundary. A flaw in how the kernel processes a system call completely bypasses all user-space security measures. Race conditions in concurrent kernel operations are incredibly difficult to spot during code reviewβLinux creator Linus Torvalds had actually attempted to fix a version of this bug in 2005, but the fix was incomplete, leaving the vulnerability dormant for another decade. |
Key Statistics & Industry Data (2026)
- Attack Vector β Over 40% of zero-day exploits in modern operating systems directly target system call interfaces (like
win32k.sys) to achieve privilege escalation. (Source: CrowdStrike Global Threat Report, 2026) - Performance Overhead β Transitioning from user mode to kernel mode via a system call introduces an overhead of approximately 100 to 250 CPU cycles, making batching critical for high-performance applications. (Source: IBM, 2026)
- Container Security β 85% of enterprise Kubernetes deployments now use system call filtering (like seccomp or AppArmor) to strictly limit the capabilities of containerized applications. (Source: Gartner, 2026)
Applications / When to Use System Calls
File Operations
When writing software that must read/write local disk files (open, read, write).
Process Creation
When a parent application needs to spawn background workers or child processes (fork, exec).
Network Communication
When establishing TCP/UDP connections across the network (socket, connect, bind).
Hardware Interaction
When device drivers need to send commands to physical peripherals (ioctl).
Advantages of System Calls
- Security - Prevents malicious software from directly accessing hardware or memory.
- Abstraction - Programmers don't need to know hardware-specific implementation details.
- Stability - The OS can manage concurrent requests gracefully without crashing.
- Portability - Programs can run on different hardware using the same OS system call interface.
Disadvantages & Trade-offs
- Performance Overhead - Transitioning between user and kernel mode consumes CPU cycles.
- Complexity - Using system calls directly requires deep knowledge of OS internals (usually abstracted by libraries).
- OS Dependency - System calls are specific to the OS (Windows API vs POSIX), meaning direct system call code isn't cross-platform.
Quick Reference Cheat Sheet
Bookmark this table β system calls in one quick reference.
| Category | System Call | OS | Purpose |
|---|---|---|---|
| Process Control | fork(), exec(), wait() | Unix/Linux | Create process, execute program, wait for termination. |
CreateProcess(), ExitProcess() | Windows | Process creation and termination in Windows. | |
| File Management | open(), read(), write(), close() | Unix/Linux | Basic file I/O operations. |
CreateFile(), ReadFile(), WriteFile() | Windows | Windows file I/O operations. | |
| Device Management | ioctl(), read(), write() | Unix/Linux | Configure hardware devices and read/write data. |
| Information Maint. | getpid(), alarm(), sleep() | Unix/Linux | Get process ID, set alarm, suspend execution. |
| Communications | pipe(), shmget(), mmap() | Unix/Linux | Inter-process communication (IPC). |
Q.What are system calls?
Q.Why are system calls required?
Q.What is the difference between User Mode and Kernel Mode?
Q.Give an example of a system call.
Q.What happens when a system call fails?
Q.Are system calls the same across all operating systems?
Q.Why are system calls slower than regular function calls?
Q.What is the relationship between library functions and system calls?
Related Topics
Found this helpful? Share it with your study group.
Test Your Knowledge
Ready to prove your skills? Take our rigorous multiple-choice quiz designed to test your understanding of this topic and prepare you for interviews.