In this Linuxhint article, we will explain what system calls are, what they are used for, and what makes them different from the other functions in the standard C libraries. We’ll also show you what function they serve for the kernel in the system and how the processes use the services they provide. We will also show you a list of the most commonly used system calls with a brief description of each function and the header in which they are declared. In another section, we will show you how to monitor the operation of system calls through the command console.
Conceptually, all systems are made up of layers in which each has its own function and privileges. This stack of layers that makes up a system starts at the lowest level with the hardware components and ends at the top layer which is known as the user mode.
All the programs that we run on our computer and all the codes, we write and compile the result in a process that runs in the user mode of the system. In this mode, the user works and performs his daily tasks, launches and uses the various applications, manages the files, etc. At the security level, this is the most superficial layer of the system and therefore the most restricted and, by default, is completely limited to manage and use the system resources directly.
As mentioned earlier, the hardware layer is the lowest level of a system. Immediately above it is the Linux kernel. The kernel is the heart of the operating system where it integrates with the hardware and strictly manages it, being the only one with privileges to do so. For example, the processes and memory are managed here, the data is accessed from the hard disk or SSD, the network devices are controlled, etc. Everything that the OS and the processes do with the hardware is done through the kernel. Therefore, you could say that it is a kind of firmware that integrates the software and the hardware.
Given the restrictions on resource access and management that the operating system imposes on the user mode, and because access to these resources is only possible through the kernel, a mechanism that allows the processes to interact with the resources that they need to function is essential.
System calls, or SysCalls, are a set of functions provided by the operating system that serve as an interface between a user process and the kernel. These functions allow us to interact with the OS and request a service through it that requires the use of system resources. The syscalls do not grant a direct access to the resources or their management, but the kernel is the one that manages them and makes them available to the process that calls them, but always under its strict management.
In practice, the system call functions look like ordinary functions and are implemented that way. They have input arguments to which the data to be processed is sent, and output arguments to which the operating system returns the results.
The method for calling this type of function is the same as any other function that we find in the C libraries. Therefore, the question of whether or not it is a calling function is, in practice, a purely conceptual question for the programmer.
The arguments of these functions use the standard types of the C language. Although, by the nature of their task, they are typically pointers that store the memory addresses, descriptors, paths, and so on.
On Linux, there are currently about 350 system calls. Also, there are a large number of functions that are not system calls but manage the resources through them such as the malloc(), free(), calloc(), and realloc() functions that manage the memory dynamically using syscalls in your code.
How to Monitor the System Calls with the Strace Command in the Linux Console
Now that you know what system call functions are all about, let’s show you how to monitor them in real time from the Linux command console using the “strace” command. The “strace” command is very flexible. It is used to monitor the system and provides several options that you can see with the following command:
When you run this command, the list of monitoring options that is offered by “strace” is displayed in the command console.
To monitor the syscalls of a process, we need to run it with the following command:
strace ./process
e -h
To test this, we compile the following code that we gave as an example in our article entitled “Connect Function in C language”. This code, although very simple, is very interesting because it connects us to the Google host through the HTTP service and allows us to send the commands and receive the response.
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
int main (){
//step 1
int socket_id ;
int port = 80;
int error;
char buffer [1025];
struct hostent *server;
struct sockaddr_in client;
memset(&server, 0, sizeof(server));
//step 2
server = gethostbyname ( "www.google.com" );
if(server == NULL)
{
printf("\nError getting domain data.\n");
return 1;
}
//step 3
socket_id = socket ( AF_INET, SOCK_STREAM, 0 );
//step 4
client.sin_family = AF_INET;
client.sin_port = htons(port);
//step 5
bcopy((char *) server->h_addr,
(char *) &client.sin_addr.s_addr,
sizeof(server->h_length));
error=connect(socket_id, (struct sockaddr *) &client, sizeof(client));
if (error <0){
printf ("\nCould not establish connection to the server\n");
close( socket_id);
return 1;
}
printf ("\nConnect to : %s\n", inet_ntoa( client.sin_addr ));
while (1){
printf("To exit press Ctrl+c \nSend http command: ");
fgets(buffer, 1025, stdin);
send(socket_id, buffer, 1025, 0);
memset(&buffer, '\0', 1025);
recv(socket_id, buffer, 1025, 0);
printf("%s", buffer);
memset(&buffer, '\0', 1025);
}
}
We compile the code and give it the “connect” output name. Then, we execute it with the “./connect” command and prepend the “strace” command to be able to monitor the syscalls.
This command displays the syscalls of the processes in the command console. As seen in the following figure, although our code only used the socket(), connect(), send() and recv() syscalls, its execution requires the other system calls that occur in the code of the functions that we use.
The following command displays a summary of the system calls for a process when it terminates:
System call functions for process management:
Function | Description | Header |
---|---|---|
execl()
execlp() execle() execv() execvp() execvpe() |
Family of system functions calls that replace one process with another by inheriting the PID and resources from the parent process. | unistd.h |
fork() | Duplicates a process. | unistd.h |
getpid() | Gets the PID of the calling process. | unistd.h |
getppid() | Gets the PID of the parent process of the calling process. | unistd.h |
exit() | Reports the exit status of a process. | stdlib.h |
getcwd()
getwd() |
Returns a string with the current working directory of the calling process. | unistd.h |
pause() | Pauses the process until a signal arrives. | unistd.h |
wait() | Wait for the output of a child process from a parent process. | wait.h |
System call functions for file management:
Function | Description | Header |
open()
openat() |
Open files. | fcntl.h |
close() | Close files. | unistd.h |
read() | Read files. | unistd.h |
poll() | Polls the availability of files for a specific operation. | poll.h |
write() | Write files. | unistd.h |
fcntl() | Manipulates file descriptors. | fcntl.h |
truncate()
ftruncate() |
Truncate a file to specified length | unistd.h |
creat() | Create a file. | fcntl.h |
chmod()
fchmod() |
Sets permissions of a file | sys/stat.h |
unlink() | Delete a file. | unistd.h |
System call functions for signal handling:
Function | Description | |
signal() | Links a signal with a handler. | signal.h |
kill() | Send a signal to a process. | signal.h |
killpg() | Send a signal to a group of processes. | signal.h |
raise() | Performs the action of a signal in the process that invokes it. | signal.h |
sigaction() | Sets the action of a signal. | signal.h |
sigwait() | Suspends execution of the calling thread until the specified signal arrives. | signal.h |
sighold() | Add signal to the calling process’ signal mask. | signal.h |
sigignore() | Ignore a signal. | signal.h |
sigblock() | Block a signal. | signal.h |
psignal()
psiginfo() |
Prints a message with the description of a signal. | signal.h |
System call functions for socket handling:
Function | Description | |
socket() | Create a socket. | socket.h |
bind() | Bind a name to a socket | socket.h |
connect() | Connect a socket. | socket.h |
listen() | Listen for connections on a socket. | socket.h |
accept() | Accepts a connection on a socket. | socket.h |
recv()
recvfrom() recvmsg() |
Receives a message from a socket. | socket.h |
send()
sendto() sendmsg() |
Send a message from a socket. | socket.h |
getsockname() | Returns the address of the specific socket by its descriptor. | socket.h |
getsockopt() | Set or get options on sockets. | socket.h |
Conclusion
In this Linuxhint article, we explained what system call functions are and how they are used to request the kernel services that involve the use of system resources. We showed you a brief explanation of what the user processes are and how they interact with the kernel via the system calls.
To help you quickly put these features into practice, we include a list of the most commonly used system calls along with a brief description and the header that you need to include in your code to use them. We also included a section where we showed you how to monitor a process’ syscalls via the command console.