sunfishcode's blog
A blog by sunfishcode


Measuring System Interface Complexity

Posted on

This post is in a series about "Everything Is A File":


One of the most easily quantifiable ways to measure the "simplicity" or "elegance" or an operating system this is to count the number of functions in its system interface, or "syscalls". Linux has over 300. That's lot! For comparison, Plan 9 has around 30. It would seem that Plan 9 is, like, 10x more elegant than Linux.

Ok, so listen to this. That's cool. But guess what.

I have a design for an OS which is 30x more elegant than even Plan 9. It's 300x more elegant than Linux!

Introducing "String OS".

String OS logo, very garish

What is the secret to String OS' amazing simplicity and elegance? I'll tell you.

It has only one syscall!

fn the_syscall(arg: String) -> String;

That's it! That's the entire interface.

The way it works is, you describe requests you want to make in strings, and you call the_syscall and pass them in. You get back a string, which describes the result. That's it.

It's trivial to interface with from any programming language; all you need is a string type and the ability to call functions!

And it's fully serializable, loggable, extensible, interposable, replayable, remotable, and network-transparent.

🤯

Other operating systems have "shells" to provide interactive experiences. String OS doesn't hide behind a shell. You can type commands straight into the OS.

No Shells! (image of cockle shells with a no symbol over them)

What's that you say? You want to know the list of commands?

...

Seriously though

String OS is a made-up example, but the real world sometimes isn't that far off. Consider Unix's ioctl:

int ioctl(int fd, unsigned long request, ...);

Here, instead of a string, we're passing an integer, but it's not that different. The way ioctl is implemented, it does a big switch on the request argument, and dispatches to one of hundreds of different functions, depending on the values of its arguments. When OS's do this kind of thing, it fools our little "number of syscalls" metric.

But perhaps there's a way we can fix the metric by looking a little closer. Perhaps instead of looking at the literal number of system calls, we count the number of underlying implementation functions you can reach. We can look through the big switch. Or for String OS, we can look through the parsing and dispatch code. If we did that for Linux, we'd add over a thousand more syscalls. Yeah, there's a lot of ioctl codes.

Is this Plan 9's cue to take the stage? Shall we sound the fanfare? After all, Plan 9 famously doesn't even have an ioctl function. It avoids ioctl entirely. So elegant!

Pythagorean proof (1), as an example of extreme elegance

(this picture illustrates an elegant mathematical proof; have we reached this level of elegance in system interface design yet?)

Or does it?

Now, come to think of it, how...

er, how...

Confused man

How can Plan 9 have only 30 syscalls? How does it work?

It turns out, Plan 9 has a thing called a "ctl file". Plan 9 file descriptors can have a special file associated with them named "ctl". The way you use it is, you open that file, and write a specially-formatted ASCII string into it, containing a command and parameters to pass to it. From the link:

Writing the string connect 2048 to the ctl file sets the packet type to 2048 and configures the connection to receive all IP packets sent to the machine.

This means that we're using the existing write system call to send a serialized message, which effectively encodes independent system calls and their parameters. We've avoided an ioctl system call, but we've effectively just re-introduced new logical syscalls using dynamic dispatch within the "ctl" file driver.

So while Plan 9 has a very small number of direct system calls in its system interface, part of how it achieves this is by hiding logical functions behind a string parsing and dynamic dispatch layer.

In short, it works like String OS!

And more

Even when they aren't literally serializing commands into strings, Unix's and Plan 9's pervasive use of filesystem namespaces means that the system calls they do have are very string-oriented. Strings identify resources in the namespaces.

And more broadly, the Everything Is A File design focuses everything on a relatively small set of file-like operations, where features that don't fit into file-like usage patterns get accessed through integer or string values and dynamic dispatch.

So what?

So a few things.

First, counting the number of direct syscalls is a very unreliable way to measure OS design elegance.

Second, is String OS a good way to design system interfaces? I'll write more about this in a future post. As a sneak preview, using strings to identify resources is a sign that there may be ghosts present, but there's more to say 😃.