System calls

System calls are low-level functions that directly interact with the Linux kernel. You would usually use them in assembly, but assembly code is out of the scope of this course. We will use C instead.

πŸ“š See also: syscall.sh.

They are defined in man 2, such as man 2 open. See man 2 syscalls. You will find inside:

  • 🎯 imports
  • πŸ’ͺ signatures

➑️ See Multitasking for: fork, wait, pipe, sleep, kill, exec*, dup*...


perror - print a system call's error message

This is not a system call, but let's make one exception.

#include <stdio.h>

void perror(const char *s);

If a system call fails, an error code will be set. You can use perror to print the error message, and you can even append your own error message with the parameter s.

NOTE: system calls are usually returning 0 upon success, and -1 upon failure. If an error occurred, they set the global variable errno with a code indicating what error occurred. perror will use this code to display a human-readable error message.


exit - kill the process

#include <unistd.h>

void exit(int status);

You will use this to terminate the program.


open - open a file

#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

This function returns an int, usually called fd, short for file descriptor, and is used by other system calls. If you didn't know, there are already three open file descriptors:

  • stdin, standard input: 0
  • stdout, standard output: 1
  • stderr, standard error output: 2
  • pathname is the path to your file

  • flags are a list of one, or mode options, separated with a pipe (|), used to determine how the file is opened.

    • O_RDONLY: read only
    • O_WRONLY: write only
    • O_CREAT: create if it does not exist
    • O_TRUNC: truncate/clear content
  • If you are creating a file, you must add a third argument, which is the permissions given to the file, such as 0600.


read - read a file descriptor

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

For instance, here is an example

char str[11];
ssize_t res = read(0, &str, 10);

The system call returns -1 upon failure (see perror), or the number of characters read.

  • fd is the file descriptor from which you want to read

  • buf is where you will store what you read. It's usually a char array, but it could be something else.

  • count is the size of what you want to read.

You will most likely enter n to read n chars, but you should note that you implicitly wrote 10 * sizeof(char), and as the sizeof(char) is 1, then you can only write 10.


write - write in a file descriptor

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

For instance, here is an example

ssize_t res = write(1, "Hello, World", 13);

The system call returns -1 upon failure (see perror), or the number of characters written.

  • fd is the file descriptor in which you want to write

  • buf is what you want to write. It's usually a string, but it could be something else.

  • count is the size of what you want to write. Like read, you should use sizeof(xxx) if you are writing something else than chars (for which sizeof is usually omitted because it's 1).


lseek - move through a file

#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

Note that the function is returning your new offset in the file. To get your offset at any time, you can use this trick:

... = lseek(xxx, 0L, SEEK_CUR);
  • fd is the file descriptor
  • offset is a number (long) of characters you want to skip starting from "whence"
  • whence is the location we are moving to before applying the offset.
    • SEEK_CUR: do not move, use the current position
    • SEEK_SET: move to the start of the file, and apply an offset
    • SEEK_END: move to the end of the file, and apply an offset

If you understood right, to move back to the start, you would do

... = lseek(xxx, 0L, SEEK_SET);

close - close a file descriptor

#include <unistd.h>

int close(int fd);

Aside from 0, 1, and 2, every file descriptor should be closed.

  • fd: the file descriptor you want to close
  • return 0, or -1

stat - close a file descriptor

#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);

stat is taking a path to a file or a file descriptor for fstat, and an empty structure, and will fill it with info about the file. If the function failed to do its job, then -1 is returned.

struct stat s;
int res = stat("path", &s);
res = fstat(xxx, &s);
struct stat {
 dev_t     st_dev;     /* ID of device containing file */
 ino_t     st_ino;     /* inode number */
 mode_t    st_mode;    /* protection */
 nlink_t   st_nlink;   /* number of hard links */
 uid_t     st_uid;     /* user ID of owner */
 gid_t     st_gid;     /* group ID of owner */
 dev_t     st_rdev;    /* device ID (if special file) */
 off_t     st_size;    /* total size, in bytes */
 lksize_t st_blksize; /* blocksize for file system I/O */
 blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
 time_t    st_atime;   /* time of last access */
 time_t    st_mtime;   /* time of last modification */
 time_t    st_ctime;   /* time of last status change */
};

πŸ‘» To-do πŸ‘»

Stuff that I found, but never read/used yet.

  • adding exercises from texas