Prev: fundamental-concepts Next: file-io-the-universal-io-model
A system call allows a process in user space to request that the kernel takes some action in kernel space on the process’ behalf.
Some points:
The lifecycle of a system call might look like the following:
%rax
for x86_64.syscall
or
int 0x80
, which causes the processor to switch from user
mode to kernel mode.System calls have some overhead, as they require a lot more work than a simple user space function.
In graphical form:
A library function is a function that is provided by the C standard library.
Most applications require the C standard library. One popular
implementation is GNU’s, glibc. The current version is called
libc.so.6
. You can check the dependencies of a program with
ldd
, list dynamic dependencies.
To test for the glibc version, there is a static way and a dynamic way of doing so:
A static way involves reading the constants __GLIBC__
and __GLIBC_MINOR__
, which return two numbers that indicate
the major and minor version of glibc.
At runtime, you can use:
#include <gnu/libc-version.h>
const char* gnu_version = gnu_get_libc_version();
Which returns a pointer to a null terminated string that has the glibc version.
Some system calls (like getpid
or _exit
)
never fail, but almost all other system calls can.
To check the system call for errors, you can check if the return code
is -1
.
int fd = open(pathname, flags, mode);
if (fd == -1) {
// handle the error
}
When a system call fails, it sets the integer value
errno
to a positive value. After including it, you can
check for errors like:
#include <errno.h>
= read(fd, buf, nbytes);
count // error happened
if (cnt == -1) {
if (errno == EINTR) {
(stderr, "read was interrupted by a signal.\n");
fprintf} else {
(stderr, "other error.\n");
fprintf}
}
The perror
function prints the string passed in, as well
as a message corresponding to the value of errno
.
#include <stdio.h>
= open(pathname, flags, mode);
fd if (fd == -1) {
("open");
perror(EXIT_FAILURE);
exit}
If you want a string corresponding to the error in
errno
, you can pass it to strerror
. The error
messages are locale sensitive.
#include <string.h>
char* strerror(int errnum);
Since some features aren’t always available everywhere, a set of feature macros are popularly used.
One way to activate macros is to define them before including header files like so:
#define _BSD_SOURCE 1
Or you can pass them to the compiler:
$ cc -D_BSD_SOURCE main.c
Some example macros include:
_POSIX_C_SOURCE
This sets the version of POSIX
compatibility to use.
_XOPEN_SOURCE
This exposes Sus macros.
Some glibc exclusives:
_BSD_SOURCE
causes BSD definitions to be favored_SVID_SOURCE
exposes System V definitions_GNU_SOURCE
exposes both BSD and SysV definitions, as
well as GNU exclusive macrosSome data types don’t use C specific types like int
or
long
since they vary by the word size on the processor.
Thus, there are some wrapper types used for important system data types,
like process ids:
typedef int pid_t;
Standard types generally are in <sys/types.h>
.
Since different unix implementations may have some fields in a structure typedef’d in or out:
struct sembuf {
unsigned short sem_num;
short sem_op;
short sem_flg;
#ifdef _GNU_SOURCE
short sem_extras; // some extra flags for GNU only
#endif
};
You cannot use a structure initializer portably:
struct sembuf s = { 3, -1, SEM_UNDO }; // this doesn't work
You can use the C89 compliant:
struct sembuf s;
.sem_num = 3;
s.sem_op = -1;
s.sem_flg = SEM_UNDO; s
Or the C99 named structure initializer feature:
struct sembuf s= { .sem_num = 3, .sem_op = -1, .sem_flg = SEM_UNDO };
To use features that might not be on all implementations, you can use
#ifdef
.
#ifdef WCOREDUMP
// use WCOREDUMP() macro
#endif
Prev: fundamental-concepts Next: file-io-the-universal-io-model