Can a system call happen in a C program? Consider this:
int main()
{
int f = open("/tmp/test.txt", O_CREAT | O_RDWR, 0666);
write(f, "hello world", 11);
close(f);
return 0;
}
In this sample code, open
, write
, and close
are library functions. During my searches I conclude that they are functions not system calls. Each of these functions (open
, write
, and close
) make a system call.
Questions
write
and read
system calls are made directly, and if we compile it with different options, it calls library functions instead?A system call, according to Wikipedia, is a "programmatic way in which a computer program requests a service from the kernel of the operating system on which it is executed".
Another way of understanding a system call is as a user space program making a request to the operating system kernel to perform some task on behalf of the user space program. The full set of system calls provided by the kernel is analogous (in some ways) to an API provided by the kernel to user space.
As system calls are a low level interface to the kernel, correctly providing their arguments can be error prone or even dangerous. For these reasons, C library authors provide simpler and safer wrapper functions for a significant portion of a kernel's set of system calls.
These wrapper functions take a simplified argument set and then derive the appropriate values to pass on to the kernel so the system call can be executed.
Note: This example is based on compiling and running a C program with gcc
on Linux. The system calls, library functions, and output may differ on other POSIX or non-POSIX operating systems.
I will attempt to show how to see when system calls are being made with a simple example.
#include <stdio.h>
int main() {
write(1, "Hello world!\n", 13);
}
Above we have a very simple C program that writes the string Hello world!\n
to stdout
. If we compile and then execute this program with strace
, we see the following (note the output may look different on other computers):
$ strace ./hello > /dev/null
execve("./hello", ["./hello"], 0x7fff083a0630 /* 58 vars */) = 0
<a bunch of output we aren't interested in>
write(1, "Hello world!\n", 13) = 13
exit_group(0) = ?
+++ exited with 0 +++
strace
is a Linux program that intercepts and displays all system calls made by a program, as well as the arguments provided to the system calls and their return values.
We can see here that, as expected, the write
system call was made with the expected arguments. Nothing strange yet.
Another Linux tracing program is ltrace
, which intercepts dynamic library calls made by a program, and displays their arguments and return values.
If we run the same program with ltrace
, we see this:
$ ltrace ./hello > /dev/null
write(1, "Hello world!\n", 13) = 13
+++ exited (status 0) +++
This tells us that the write
library function was executed. This means that the C code first called the write
library function, which then in turn called the write
system call.
Suppose now that we want to explicitly make a write
system call without calling the write
library function. (This is inadvisable in normal use, but useful for illustration.)
Here is the new code:
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main() {
syscall(SYS_write, 1, "Hello world!\n", 13);
}
Here we directly call the syscall
library function, telling it we want to execute the write
system call.
After recompiling, here is the output of strace
:
$ strace ./hello > /dev/null
execve("./hello", ["./hello"], 0x7ffe3790a660 /* 58 vars */) = 0
<a bunch of output we aren't interested in>
write(1, "Hello world!\n", 13) = 13
exit_group(0) = ?
+++ exited with 0 +++
We can see the write
system call is made as before as expected.
If we run ltrace
we see the following:
$ ltrace ./hello > /dev/null
syscall(1, 1, 0x560b30e4d704, 13) = 13
+++ exited (status 0) +++
So the write
library function is no longer being called, but we are still making a library function call. Now we are making a call to the syscall
library function instead of the write
library function.
There may be a way to directly make a system call from a user space C program without calling any library functions, and if there is a way I believe it would be very advanced.
In general, nearly every non-trivial C program makes at least one system call. This is because user space does not have direct access to kernel memory or to the computer's hardware. User space programs have indirect access to kernel memory and the hardware through system calls.
To identify if a compiled C program (or any other program on Linux) makes a system call, and to identify which system calls it makes, simply use strace
.
You can compile your C program (assuming you are using gcc
) with the -nostdlib
option. This will prevent linking the C standard library as part of producing your executable. However, then you would need to write your own code to make system calls.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With