5.1 System Calls
1. System Calls
System calls are the mechanisms through which a program requests services from the operating system’s kernel. In simpler terms, system calls act as an interface between a user-space program (like a program you’re running) and the kernel (the core part of the operating system that manages hardware resources).
When a program needs to perform tasks that require access to hardware or system resources (like reading or writing files, creating processes, or allocating memory), it can’t do this directly. Instead, it must make a system call to ask the kernel to perform these operations on its behalf.
1.1 Types of System Calls
There are several categories of system calls, depending on the type of service they provide:
-
Process Control:
- These system calls deal with process creation, termination, and management.
- Example calls:
fork()
— Create a new process by duplicating the calling process.exec()
— Replace the current process image with a new one.exit()
— Terminate the calling process.wait()
— Wait for a child process to terminate.
-
File Management:
- These system calls allow programs to interact with files and directories.
- Example calls:
open()
— Open a file.read()
— Read from a file.write()
— Write to a file.close()
— Close an open file.unlink()
— Delete a file.
-
Memory Management:
- These system calls handle the allocation, deallocation, and management of memory.
- Example calls:
mmap()
— Map files or devices into memory.brk()
— Change the end of the data (heap) segment.sbrk()
— Adjust the program’s data space.
-
Device Manipulation:
- These system calls interact with hardware devices, such as reading from or writing to devices.
- Example calls:
ioctl()
— Control a device.read()
/write()
— Perform I/O operations.
-
Information Maintenance:
- These system calls retrieve or modify information about the system or processes.
- Example calls:
getpid()
— Get the process ID of the calling process.gettimeofday()
— Get the current time.
-
Communication:
- These system calls facilitate inter-process communication (IPC) and synchronization between processes.
- Example calls:
pipe()
— Create a pipe for communication between processes.msgget()
— Create or access a message queue.semop()
— Perform operations on semaphores.
1.2 How System Calls Work
When a system call is made, the program transitions from user mode to kernel mode. Here’s a high-level view of the process:
- Program Execution: A program runs in user mode, where it has limited access to system resources.
- System Call Request: When the program needs to request a system service (like reading a file or allocating memory), it triggers a system call.
- Mode Switch: The system call triggers a switch from user mode to kernel mode, where the operating system has full control over hardware resources.
- Kernel Action: The kernel handles the request, accessing system resources as needed.
- Return to User Mode: After completing the requested task, the kernel returns control to the program in user mode, possibly providing data or status as needed.
1.3 Example of a System Call
Consider a simple example where a program wants to read a file. The steps might look like this:
- The program uses the
open()
system call to request the kernel to open a file. - The kernel checks if the file exists and if the program has permission to access it.
- If everything is valid, the kernel returns a file descriptor to the program.
- The program then uses the
read()
system call to read the content of the file. - The kernel reads the data from the disk and returns it to the program.
- Finally, the program calls
close()
to close the file.
1.4 Why Are System Calls Important?
- Security: System calls act as a boundary between user applications and the operating system, preventing direct access to sensitive resources. The operating system ensures that only authorized operations are performed.
- Resource Management: System calls allow the operating system to manage resources (like memory and CPU time) efficiently and securely.
- Abstraction: They provide an abstraction layer between user programs and the underlying hardware, so applications don’t need to worry about low-level hardware details.
1.5 Examples of Common System Calls
- Process Control:
fork()
,exec()
,exit()
- File Operations:
open()
,read()
,write()
,close()
- Memory Management:
mmap()
,brk()
,sbrk()
- Networking:
socket()
,connect()
,send()
,recv()
2. Library Calls
Library calls (also known as standard library functions) are functions provided by the programming language’s standard library that allow programs to perform commonly-used operations, like manipulating data, performing calculations, handling input/output, or managing memory. These library functions are part of a higher-level abstraction built on top of system calls.
While system calls provide a direct interface to the operating system for performing tasks like file handling, memory management, and process control, library calls are higher-level functions that simplify the process of interacting with these system calls. Library calls typically abstract away many of the low-level details, making it easier for programmers to write code.
2.1 How Library Calls Work
- Abstraction over System Calls: Library calls provide a higher-level, often easier-to-use interface over system calls. They are implemented in the language’s runtime or standard library and internally may invoke one or more system calls to perform the underlying tasks.
- Linking: When you compile a program, the code calls the appropriate functions from the standard library. These library functions are typically linked to your program during the compilation process. Some library calls are static (linked at compile time), while others may be dynamically linked during runtime.
2.2 Examples of Library Calls
To give you an understanding of how library calls work, here are examples in C, one of the most common programming languages:
-
Input/Output Operations:
printf()
: A library function in C that prints output to the console. Internally, it uses system calls likewrite()
to send data to the terminal.printf("Hello, World!");scanf()
: A library function that reads user input from the console. It abstracts the use of system calls likeread()
.int x;scanf("%d", &x);
-
String Manipulation:
strlen()
: A library function that returns the length of a string. It abstracts low-level memory operations and system calls.size_t len = strlen("Hello");
-
Memory Management:
malloc()
: Allocates a block of memory. It typically wraps system calls likebrk()
ormmap()
to request memory from the operating system.int *arr = (int*) malloc(10 * sizeof(int));free()
: Frees a previously allocated memory block.free(arr);
-
File Handling:
fopen()
: Opens a file. Underneath, this library function calls lower-level system calls likeopen()
.FILE *file = fopen("example.txt", "r");fread()
: Reads data from a file. It uses system calls likeread()
internally.fread(buffer, sizeof(char), size, file);
-
Mathematical Functions:
sqrt()
: Calculates the square root of a number. It’s part of the math library and abstracts away the use of low-level mathematical operations.double result = sqrt(25.0);
2.3 Difference Between System Calls and Library Calls
-
Level of Abstraction:
- System Calls: Provide a low-level interface directly to the operating system, typically requiring careful attention to error handling, security, and system resources.
- Library Calls: Provide a higher-level interface that abstracts much of the complexity, making it easier to perform common tasks like input/output, memory management, and string manipulation.
-
Performance:
- System Calls: Often involve a context switch from user space to kernel space, which can be relatively slow and is subject to system overhead.
- Library Calls: Are generally faster, as they might not involve any switching to kernel mode (except in cases where they wrap system calls).
-
Usage:
- System Calls: Are used for tasks that require direct interaction with the operating system, such as file system access, process management, or network communication.
- Library Calls: Are used for higher-level tasks that are built on top of system calls, such as file I/O, string handling, mathematical computations, and memory allocation.
2.4 Example Comparison
Here’s an example where a system call and a library call are used to perform a task:
System Call (open()
and read()
in C)
#include <fcntl.h>#include <unistd.h>
int main() { int fd = open("file.txt", O_RDONLY); if (fd == -1) { perror("open"); return 1; }
char buffer[100]; ssize_t bytesRead = read(fd, buffer, sizeof(buffer)); if (bytesRead == -1) { perror("read"); close(fd); return 1; }
write(1, buffer, bytesRead); // Write to stdout
close(fd); return 0;}
- Here,
open()
,read()
, andwrite()
are system calls. They interact directly with the operating system to open the file, read its contents, and write to standard output.
Library Call (fopen()
and fread()
in C)
#include <stdio.h>
int main() { FILE *file = fopen("file.txt", "r"); if (file == NULL) { perror("fopen"); return 1; }
char buffer[100]; size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer), file); if (bytesRead == 0) { perror("fread"); fclose(file); return 1; }
fwrite(buffer, sizeof(char), bytesRead, stdout); // Write to stdout
fclose(file); return 0;}
- Here,
fopen()
,fread()
, andfwrite()
are library calls that internally use system calls likeopen()
,read()
, andwrite()
to perform the same tasks, but they simplify the interface for the programmer.