System Calls

In Linux, a system call is a request made by an application (process) to the operating system's kernel to perform a specific operation that requires privileged access or manipulation of low-level system resources. System calls provide a way for applications to interact with the kernel and access system services.

System calls are implemented as a set of APIs (Application Programming Interfaces) that allow applications to request services from the kernel, such as:

1. Process management: creating, managing, or terminating processes.

2. File system operations: reading, writing, creating, deleting files, etc.

3. Device I/O: interacting with devices, such as reading/writing data to/from them.

Example: Read/Write System Calls

Let's use the read() and write() system calls as examples. Suppose we have a user application that wants to write some data to a device file /dev/ttyS0, which is a serial port. The application will make a system call to the kernel, requesting to write data to this file. The flow of events is as follows:

1. Application: The user application (e.g., a shell script or a C program) makes a system call to the kernel to request a write() operation.

2. Kernel: The kernel receives the system call and checks if it's valid. If so, it looks up the device file /dev/ttyS0 in the virtual file system (VFS).

3. Device File: The VFS returns the major and minor numbers of the device file to the kernel.

4. Kernel: Based on these numbers, the kernel determines which driver should handle the request.

The kernel then calls the corresponding driver with the request. The driver receives the request and performs the necessary operations. In this case, the it will write data to the serial port device.

How does the driver read/write into hardware?

When a system call is made to read or write data from/to /dev/ttyS0, the kernel passes the request to the corresponding driver. The driver then performs the necessary operations to read or write data to/from the serial port hardware. Here's a high-level overview of how this might work:

Read

When reading from /dev/ttyS0, the driver will:

  1. Check if there is any incoming data available in the receive buffer.
  2. If so, return the received data to the application.

Write

When writing to /dev/ttyS0, the driver will:

  1. Take the data provided by the application and prepare it for transmission over the serial port.
  2. Use the serial port hardware (e.g., UART) to send the data.

In this example, the driver acts as a bridge between the kernel's virtual file system and the physical serial port device. The driver handles the low-level details of reading and writing data from/to the hardware, allowing applications to interact with the device in a platform-independent way.

Privilege Changes: Supervisor Calls (SVC)

In Linux, the system calls made by the user application leads to a privilege change using software exceptions or supervisor calls (SVC).

Supervisor Calls (SVC)

When an application needs to perform a privileged operation, it must make a supervisor call (SVC) to request kernel-level privileges. SVC is a software based method of raising an exception that the Kernel is supposed to catch and service. Which service the user application is seeking is defined by the request ID provided as part of a pre-defined (by the spec) CPU register. Some other registers may hold the input parameters for the service handler to consider. Here is how the top level flow is -

  1. Application Request: The application requests a SVC to access a hardware resource with elevated privileges by performing an operation on the device file (virtual file). The operation results in a software based exception (SVC).
  2. Kernel: Gets and exception, the handler reads the CPU registers and infers the service requested by the user application.
  3. Kernel: Identifies the exact driver that is tied to the file operations on the device file User space application tried accessing, and calls the file operation routine implemented by the driver.
  4. Driver: The driver function correctly performs the operation on the actual hardware by writing/reading into the right addresses dedicated to the hardware.

The kernel consumes or passes data to and from the user space using shared buffers. The success failure of the driver is conveyed back to the user application using the CPU registers. This way, the software exception and the CPU registers serve as a way to pass service request and status between the user and kernel space.


If you would like to learn how to develop/write Linux Device Drivers, be sure to check our FREE Course here -

Linux Device Drivers 101
Introduction to Linux Device Drivers Development.