Introduction
System calls act as intermediaries between applications and the kernel. They provide an abstraction layer that shields essential system components, like the CPU and memory, from inadvertent errors and targeted attacks.
Learn how different system calls work, understand their features, and ensure your system runs securely and predictably.
What Is a System Call?
A system call is an interface between a program running in user space and the operating system (OS). Application programs use system calls to request services and functionalities from the OS’s kernel. This mechanism allows the program to call for a service, like reading from a file, without accessing system resources directly.
When a program invokes a system call, the execution context switches from user to kernel mode, allowing the system to access hardware and perform the required operations safely. After the operation is completed, the control returns to user mode, and the program continues its execution.
This layered approach facilitated by system calls:
- Ensures that hardware resources are isolated from user space processes.
- Prevents direct access to the kernel or hardware memory.
- Allows application code to run across different hardware architectures.
What Is the Purpose of the System Call?
System calls serve several important functions, which include:
- User-Kernel Boundary. System calls serve as the authorized gateway for user programs when requesting services from the kernel. They ensure that user programs cannot arbitrarily access kernel functions or critical system resources.
- Resource Management. User programs can request and manage vital resources like CPU time, memory, and file storage via system calls. The OS oversees the process and guarantees that it is completed in an organized manner.
- Streamlined Development. System calls abstract the complexities of hardware. This allows developers to perform operations like reading and writing to a file or managing network data without needing to write hardware-specific code.
- Security and Access Control. System calls implement checks to ensure that requests made by user programs are valid and that the programs have the necessary permissions to perform the requested operations.
- Inter-Process Communication (IPC). System calls provide the mechanisms for processes to communicate with each other. They offer features like pipes, message queues, and shared memory to facilitate this inter-process communication.
- Network Operations. System calls provide the framework for network communications between programs. Developers can devote their attention to building their application’s logic instead of focusing on low-level network programming.
How Do System Calls Work?
This high-level overview explains how system calls work:
1. System Call Request. The application requests a system call by invoking its corresponding function. For instance, the program might use the read()
function to read data from a file.
2. Context Switch to Kernel Space. A software interrupt or special instruction is used to trigger a context switch and transition from the user mode to the kernel mode.
3. System Call Identified. The system uses an index to identify the system call and address the corresponding kernel function.
4. Kernel Function Executed. The kernel function corresponding to the system call is executed. For example, reading data from a file.
5. System Prepares Return Values. After the kernel function completes its operation, any return values or results are prepared for the user application.
6. Context Switch to User Space. The execution context is switched back from kernel mode to user mode.
7. Resume Application. The application resumes its execution from where it left off, now with the results or effects of the system call.
Note: The exact number of steps and the functionality of system calls can vary depending on the operating system.
What Are the Features of System Calls?
The following features are indicative of system calls:
- Security. System calls ensure that user-space applications cannot harm the system or interfere with other processes.
- Abstraction. For example, programs do not need to know the specifics of network hardware configurations to send data over the internet or disk operations to read a file, as the OS handles these tasks.
- Access Control. System calls enforce security measures by checking whether a program has the appropriate permissions to access resources.
- Consistency. Interactions between the OS and program remain consistent, regardless of the underlying hardware configuration. The same program can run on different hardware if the operating system supports it.
- Synchronous Operation. Many system calls operate synchronously, blocking the calling process until the operation is complete. However, there are also asynchronous system calls that allow processes to continue execution without waiting.
- Process Control. System calls facilitate stable process management and multitasking through process creation, termination, scheduling, and synchronization mechanisms.
- File Management. System calls support file operations such as reading, writing, opening, and closing files.
- Device Management. System calls enable processes to request device access, perform read or write operations on these devices, and release them afterward.
- Resource Management. System calls help allocate and deallocate resources like memory, CPU time, and I/O devices.
- Maintenance. System calls are used to obtain or configure system information, such as the date and time or process status.
- Communication. System calls allow processes to communicate with each other and synchronize their actions.
- Error Handling. When a system call cannot be completed, it returns an error code that the calling program can process.
Types of System Calls
The following list categorizes system calls based on their functionalities:
1. Process Control
System calls play an essential role in controlling system processes. They enable you to:
- Create new processes or terminate existing ones.
- Load and execute programs within a process’s space.
- Schedule processes and set execution attributes, such as priority.
- Wait for a process to complete or signal upon its completion.
2. File Management
System calls support a wide array of file operations, such as:
- Reading from or writing to files.
- Opening and closing files.
- Deleting or modifying file attributes.
- Moving or renaming files.
3. Device Management
System calls can be used to facilitate device management by:
- Requesting device access and releasing it after use.
- Setting device attributes or parameters.
- Reading from or writing to devices.
- Mapping logical device names to physical devices.
4. Information Maintenance
This type of system call enables processes to:
- Retrieve or modify various system attributes.
- Set the system date and time.
- Query system performance metrics.
5. Communication
The communication call type facilitates:
- Sending or receiving messages between processes.
- Synchronizing actions between user processes.
- Establishing shared memory regions for inter-process communication.
- Networking via sockets.
6. Security and Access Control
System calls contribute to security and access control by:
- Determining which processes or users get access to specific resources and who can read, write, and execute resources.
- Facilitating user authentication procedures.
Examples of System Calls
The table below lists common Unix and Windows system calls and their descriptions.
Note: System call behavior, parameters, and return values might differ depending on the OS and version. Consult the OS’s manual or documentation for detailed information.
UNIX SYSTEM CALLS | DESCRIPTION | WINDOWS API CALLS | DESCRIPTION |
---|---|---|---|
Process Control | |||
fork() |
Create a new process. | CreateProcess() |
Create a new process. |
exit() |
Terminate the current process. | ExitProcess() |
Terminate the current process. |
wait() |
Make a process wait until its child processes terminate. | WaitForSingleObject() |
Wait for a process or thread to terminate. |
exec() |
Execute a new program in a process. | CreateProcess() or ShellExecute() |
Execute a new program in a new process. |
getpid() |
Get the unique process ID. | GetCurrentProcessId() |
Get the unique process ID. |
File Management | |||
open() |
Open a file (or device). | CreateFile() |
Open or create a file or device. |
close() |
Close an open file (or device). | CloseHandle() |
Close an open object handle. |
read() |
Read from a file (or device). | ReadFile() |
Read data from a file or input device. |
write() |
Write to a file (or device). | WriteFile() |
Write data to a file or output device. |
lseek() |
Change the read/write location in a file. | SetFilePointer() |
Set the position of the file pointer. |
unlink() |
Delete a file. | DeleteFile() |
Delete an existing file. |
rename() |
Rename a file. | MoveFile() |
Move or rename a file. |
Directory Management | |||
mkdir() |
Create a new directory. | CreateDirectory() |
Create a new directory. |
rmdir() |
Remove a directory. | RemoveDirectory() |
Remove an existing directory. |
chdir() |
Change the current directory. | SetCurrentDirectory() |
Change the current directory. |
stat() |
Get file status. | GetFileAttributesEx() |
Get extended file attributes. |
fstat() |
Get status of an open file. | GetFileInformationByHandle() |
Get file information using a file handle. |
link() |
Create a link to a file. | CreateHardLink() |
Create a hard link to an existing file. |
symlink() |
Get the status of an open file. | CreateSymbolicLink() |
Create a symbolic link. |
Device Management | |||
brk() or sbrk() |
Increase/decrease the program’s data space. | VirtualAlloc() orVirtualFree() |
Reserve, commit, or free a region of memory. |
mmap() |
Map files or devices into memory. | MapViewOfFile() |
Map a file into the application’s address space. |
Information Maintenance | |||
time() |
Get the current time. | GetSystemTime() |
Get the current system time. |
alarm() |
Get the status of an open file. | SetWaitableTimer() |
Set a timer object. |
getuid() |
Set an alarm clock for the delivery of a signal. | GetUserName() or LookupAccountName() |
Get the username or ID. |
getgid() |
Get the group ID. | GetTokenInformation() |
Get the group information of a security token. |
Communication Calls | |||
socket() |
Create a new socket. | socket() |
Create a new socket. |
bind() |
Bind a socket to a network address. | bind() |
Bind a socket to a network address. |
listen() |
Bind a socket to a network address. | listen() |
Listen for connections on a socket. |
accept() |
Accept a new connection on a socket. | accept() |
Accept a new connection on a socket. |
connect() |
Initiate a connection on a socket. | connect() |
Initiate a connection on a socket. |
send() or recv() |
Send and receive data on a socket. | send() or recv() |
Send and receive data on a socket. |
Security and Access Control | |||
chmod() or umask() |
Change the permissions/mode of a file. | SetFileAttributes() or SetSecurityInfo() |
Change the file attributes or security info. |
chown() |
Change the owner and group of a file. | SetSecurityInfo() |
Set the security information. |
What Are the Rules for Passing Parameters to the System Call?
When a user-space program invokes a system call, it typically needs to pass additional parameters to specify the request. System performance depends on how efficiently these parameters are passed between user and kernel space.
The method for passing parameters depends on the system architecture, but some general rules apply:
- Limited Number of Parameters. System calls are often designed to accept a limited number of parameters. This rule is intended to streamline the interface and compel users to utilize data structures or memory blocks.
- Leveraging CPU Registers. CPU registers are the fastest accessible memory locations. The number of CPU registers is limited, which restricts the number of call parameters that can be passed. Use CPU registers when passing a small number of system call parameters.
- Using Pointers for Data Aggregation. Instead of passing many parameters or large data sizes, use pointer variables to point to memory blocks or structures (containing all the parameters). The kernel uses the pointer to access this memory block and retrieve the parameters.
- Data Integrity and Security Checks. The kernel must validate any pointers passed from user space. It checks that these pointers only target areas the user program can access. It also double-checks all data coming from user programs before using it.
- Stack-based Parameter Handling. Some systems push parameters onto a stack and allow the kernel to remove them for processing. This method is less common than using CPU registers and pointers as it is more challenging to implement and manage.
- Data Isolation Through Copying. The kernel often copies data from user space to kernel space (and vice versa) to protect the system from erroneous or harmful data. Data passed between user space and kernel space should not be shared directly.
- Return Values and Error Handling. The system call returns a value, typically a simple success/error code. In case of an error, always seek out additional information about the error. Error responses are often stored in specific locations, like the errno variable in Linux.
Note: The rules and methods above vary based on the architecture (x86, ARM, MIPS, etc.) and the specifics of the operating system. Always refer to the OS documentation or source code for precise information.
Conclusion
This article explained how system calls work and why they are essential for ensuring seamless operations, security, and managing hardware and software resources.
Continue exploring system calls by reading about the role of the select() system call in Linux, which is essential for handling multiple I/O operations simultaneously.