System Calls
System Calls
System calls provide the interface between a running program and the operating system. They are the primary way for user programs to request services from the kernel.
What is a System Call?
- A programmatic way to request services from the OS kernel
- Provides a controlled entry point into the kernel
- Allows user programs to access hardware and system resources safely
- Acts as a bridge between user mode and kernel mode
How System Calls Work
Execution Flow
User Application
│
│ 1. Makes system call (e.g., read())
▼
┌──────────────────┐
│ Library Call │ (e.g., libc wrapper)
│ (User Mode) │
└────────┬─────────┘
│ 2. Trap instruction / Software interrupt
▼
┌──────────────────┐
│ Mode Switch │ User Mode → Kernel Mode
│ (Hardware) │
└────────┬─────────┘
│ 3. System call handler
▼
┌──────────────────┐
│ Kernel │
│ (Kernel Mode) │ 4. Execute system call
└────────┬─────────┘
│ 5. Return result
▼
┌──────────────────┐
│ Mode Switch │ Kernel Mode → User Mode
│ (Hardware) │
└────────┬─────────┘
│
▼
User Application (continues)Steps in Detail
- User program invokes system call (via library function)
- Arguments are placed in registers or on stack
- Trap instruction executes (software interrupt)
- Hardware switches to kernel mode
- System call number identifies the service
- Kernel executes the appropriate handler
- Result is placed in register
- Control returns to user program
System Call Interface
System Call Number
Each system call has a unique number:
| System Call | Number (Linux x86-64) |
|---|---|
| read | 0 |
| write | 1 |
| open | 2 |
| close | 3 |
| fork | 57 |
| execve | 59 |
| exit | 60 |
Parameter Passing Methods
- Registers - Parameters passed via CPU registers
- Block/Table - Parameters stored in memory block, address passed in register
- Stack - Parameters pushed onto stack by program, popped by OS
Types of System Calls
1. Process Control
| System Call | Description |
|---|---|
fork() |
Create a new process (child) |
exec() |
Replace process memory with new program |
exit() |
Terminate process |
wait() |
Wait for child process |
kill() |
Send signal to process |
getpid() |
Get process ID |
getppid() |
Get parent process ID |
fork() Example
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
// Error
perror("fork failed");
} else if (pid == 0) {
// Child process
printf("Child: My PID is %d\n", getpid());
} else {
// Parent process
printf("Parent: Created child with PID %d\n", pid);
wait(NULL); // Wait for child
}
return 0;
}2. File Management
| System Call | Description |
|---|---|
open() |
Open a file |
close() |
Close a file descriptor |
read() |
Read from file |
write() |
Write to file |
lseek() |
Move file pointer |
stat() |
Get file status |
chmod() |
Change file permissions |
unlink() |
Delete a file |
File Operations Example
#include <fcntl.h>
#include <unistd.h>
int main() {
// Open file
int fd = open("file.txt", O_RDONLY);
if (fd < 0) {
perror("open failed");
return 1;
}
// Read from file
char buffer[100];
ssize_t bytes = read(fd, buffer, sizeof(buffer) - 1);
if (bytes > 0) {
buffer[bytes] = '\0';
write(STDOUT_FILENO, buffer, bytes);
}
// Close file
close(fd);
return 0;
}3. Device Management
| System Call | Description |
|---|---|
ioctl() |
Device-specific operations |
read() |
Read from device |
write() |
Write to device |
mmap() |
Map device memory |
4. Information Maintenance
| System Call | Description |
|---|---|
getpid() |
Get process ID |
alarm() |
Set alarm clock |
sleep() |
Suspend execution |
time() |
Get system time |
gettimeofday() |
Get time with microseconds |
sysinfo() |
Get system information |
5. Communication
| System Call | Description |
|---|---|
pipe() |
Create a pipe |
shmget() |
Allocate shared memory |
shmat() |
Attach shared memory |
msgget() |
Create message queue |
msgsnd() |
Send message |
msgrcv() |
Receive message |
socket() |
Create socket |
connect() |
Connect to socket |
send() |
Send data via socket |
recv() |
Receive data via socket |
Pipe Example
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main() {
int pipefd[2]; // pipefd[0] = read, pipefd[1] = write
char buffer[100];
// Create pipe
if (pipe(pipefd) == -1) {
perror("pipe failed");
return 1;
}
pid_t pid = fork();
if (pid == 0) {
// Child: read from pipe
close(pipefd[1]); // Close write end
read(pipefd[0], buffer, sizeof(buffer));
printf("Child received: %s\n", buffer);
close(pipefd[0]);
} else {
// Parent: write to pipe
close(pipefd[0]); // Close read end
char *msg = "Hello from parent!";
write(pipefd[1], msg, strlen(msg) + 1);
close(pipefd[1]);
wait(NULL);
}
return 0;
}6. Protection
| System Call | Description |
|---|---|
chmod() |
Change file permissions |
chown() |
Change file owner |
umask() |
Set file mode creation mask |
setuid() |
Set user ID |
setgid() |
Set group ID |
User Mode vs Kernel Mode
| Aspect | User Mode | Kernel Mode |
|---|---|---|
| Privilege Level | Low (Ring 3) | High (Ring 0) |
| Access | Limited resources | Full hardware access |
| Instructions | Restricted set | All instructions |
| Memory Access | User space only | All memory |
| Crash Impact | Only process | Entire system |
| Mode Bit | 1 | 0 |
Why Two Modes?
- Protection - Prevent user programs from damaging system
- Security - Isolate processes from each other
- Stability - Bugs in user programs don't crash system
- Resource Control - OS manages all hardware access
Mode Switching
User → Kernel Mode (System Call)
- Save user context (registers, PC)
- Switch to kernel stack
- Set mode bit to 0
- Jump to kernel code
Kernel → User Mode (Return)
- Restore user context
- Switch to user stack
- Set mode bit to 1
- Jump to user code
Overhead
Mode switching is expensive:
- Save and restore registers
- Flush CPU pipeline
- Possible cache/TLB effects
- Typical time: 100s of nanoseconds
System Call vs Function Call
| Aspect | System Call | Function Call |
|---|---|---|
| Mode Switch | Yes (User ↔ Kernel) | No |
| Overhead | High | Low |
| Access | Kernel services | User space code |
| Implementation | OS kernel | User libraries |
| Invocation | Trap/interrupt | CALL instruction |
| Examples | fork(), read() | printf(), strlen() |
System Call vs Interrupt
| Aspect | System Call | Interrupt |
|---|---|---|
| Trigger | Software (trap) | Hardware/Software |
| Synchronous | Yes | No (usually) |
| Initiated By | User program | External device/signal |
| Predictable | Yes | No |
| Examples | read(), write() | Keyboard, timer |
Library Functions vs System Calls
Many C library functions are wrappers around system calls:
printf() → write() → sys_write
fopen() → open() → sys_open
malloc() → brk()/mmap() → sys_brk/sys_mmapWhy Use Library Functions?
- Buffering - printf() buffers output for efficiency
- Portability - Same interface across different OS
- Convenience - Higher-level abstraction
- Optimization - Libraries may batch system calls
Common System Call Errors
| Error Code | Name | Description |
|---|---|---|
| -1 | EPERM | Operation not permitted |
| -2 | ENOENT | No such file or directory |
| -9 | EBADF | Bad file descriptor |
| -12 | ENOMEM | Out of memory |
| -13 | EACCES | Permission denied |
| -17 | EEXIST | File exists |
| -22 | EINVAL | Invalid argument |
Checking Errors
int fd = open("file.txt", O_RDONLY);
if (fd == -1) {
perror("open"); // Print error message
printf("Error code: %d\n", errno);
}Important Interview Questions
Q1: What is a system call?
A: A system call is a programmatic way for a user process to request services from the operating system kernel. It provides a controlled interface for accessing hardware and protected resources.
Q2: What happens during a system call?
A:
- User program triggers trap/software interrupt
- CPU switches from user mode to kernel mode
- OS identifies system call via number
- Kernel executes requested service
- Result returned, mode switches back to user
Q3: Why do we need both user mode and kernel mode?
A: To protect the system from faulty/malicious programs. User mode restricts access to critical resources, while kernel mode allows full access. This isolation ensures system stability and security.
Q4: Difference between system call and library call?
A: System calls involve mode switch to kernel and access OS services. Library calls stay in user mode and don't directly access kernel. Library calls are faster as they don't require mode switching.
Q5: What is the overhead of a system call?
A: System calls have significant overhead due to:
- Mode switching (save/restore context)
- Stack switching
- Cache/TLB effects
- Typical time: 100-1000 nanoseconds
Quick Reference
System Call Categories:
├── Process Control: fork, exec, exit, wait, kill
├── File Management: open, read, write, close, lseek
├── Device Management: ioctl, read, write
├── Information: getpid, time, alarm
├── Communication: pipe, socket, shmget, msgget
└── Protection: chmod, chown, setuid
Execution Flow:
User Program → Library → Trap → Kernel → Handler → Return
Mode Bit:
0 = Kernel Mode (privileged)
1 = User Mode (restricted)