Lecture 4: System calls

Note: Reading these lecture notes is not a substitute for watching the lecture. I frequently go off script, and you are responsible for understanding everything I talk about in lecture unless I specify otherwise.

Layers of Abstraction in Filesystems

The Unix V6 filesystem comes from the 1970s, yet, as you can see, there is already a large amount of complexity. One common paradigm for dealing with complexity is layering. Here’s a breakdown of layering involved in this filesystem:

On top of these 6 layers sit many application layers that use the filesystem without having to think about how it works.

Not only does layering provide us with a means of breaking down complexity, but it also has some nice properties if we ever want to modify the system to do something new. Let’s say we want to create a networked filesystem. Instead of having to write it from scratch, we can keep everything except for the hardware and block layers, replacing those with some layers that deal with network communication.

Unix employs this principle everywhere. As you will see later in the course, many resources are made to look like files (even though they aren’t files) so that we can control them using the file abstractions we’ve developed. Your computer interacts with your terminal window, printer, Bluetooth radio, and even (to a certain extent) CPU as if they were files, even though that is certainly not the case. As we will see towards the end of the class, the layering principle is equally pervasive in the land of networking.

System Calls

There is a reason you have probably never written code that manages raw sectors on disk: you can’t. It would be dangerous if you could; you might unintentionally corrupt some critical sectors, rendering your entire filesystem unusable. Worse, malicious code could access or alter data that it isn’t supposed to have access to. For example, unprivileged code isn’t allowed to read or modify the /etc/passwd or /etc/shadow files (which store information about passwords on your system), but if a program were allowed to access the raw filesystem, it could circumvent those permission checks.

We need the operating system to perform these privileged operations on our behalf, and to mediate access to the filesystem so that it can block malicious behavior. We interact with the operating system, asking it to do privileged operations on our behalf, through functions known as system calls (syscalls for short).

printf might seem like some magical core function to you, but it’s actually built on top of syscalls in ways that you can easily understand now. I can write a “hello world” program without using printf:

int main(int argc, char *argv[]) {
    char* output = "Hello world\n";
    write(STDOUT_FILENO, output, 12);
    return 0;
}

(Note: 12 is the length of the output string.)