|Next: GCC Up: The Linux Edge Previous: From Alpha to Portability|
With a monolithic kernel such as the Linux kernel, it's important to be very cautious about allowing new code and new features into the kernel. These decisions can affect a number of things later on in the development cycle beyond the core kernel work.
The first very basic rule is to avoid interfaces. If someone wants to add something that involves a new system interface you need to be exceptionally careful. Once you give an interface to users they will start coding to it and once somebody starts coding to it you are stuck with it. Do you want to support the exact same interface for the rest of your system's life?
Other code is not so problematic. If it doesn't have an interface, say a disk driver, there isn't much to think about; you can just add a new disk driver with little risk. If Linux didn't have that driver before, adding it doesn't hurt anyone already using Linux, and opens Linux to some new users.
When it comes to other things, you have to balance. Is this a good implementation? Is this really adding a feature that is good? Sometimes even when the feature is good, it turns out that either the interface is bad or the implementation of that feature kind of implies that you can never do something else, now or in the future.
For example -- though this is sort of an interface issue -- suppose somebody has some stupid implementation of a filesystem where names can be no longer than 14 characters. The thing you really want to avoid having these limitations in an interface that is set in stone. Otherwise when you look to extend the filesystem, you are screwed because you have to find a way to fit within this lesser interface that was locked in before. Worse than that, every program that requests a filename may only have space in a variable for, say, 13 characters, so if you were to pass them a longer filename it would crash them.
Right now the only vendor that does such a stupid thing is Microsoft. Essentially, in order to read DOS/Windows files you have this ridiculous interface where all files had eleven characters, eight plus three. With NT, which allowed long filenames, they had to add a complete set of new routines to do the same things the other routines did, except that this set can also handle larger filenames. So this is an example of a bad interface polluting future works.
Another example of this happened in the Plan 9 operating system. They had this really cool system call to do a better process fork -- a simple way for a program to split itself into two and continue processing along both forks. This new fork, which Plan 9 called R-Fork (and SGI later called S-Proc) essentially creates two separate process spaces that share an address space. This is helpful for threading especially.
Linux does this too with its clone system call, but it was implemented properly. However, with the SGI and Plan9 routines they decided that programs with two branches can share the same address space but use separate stacks. Normally when you use the same address in both threads, you get the same memory location. But you have a stack segment that is specific, so if you use a stack-based memory address you actually get two different memory locations that can share a stack pointer without overriding the other stack.
While this is a clever feat, the downside is that the overhead in maintaining the stacks makes this in practice really stupid to do. They found out too late that the performance went to hell. Since they had programs which used the interface they could not fix it. Instead they had to introduce an additional properly-written interface so that they could do what was wise with the stack space.
While a proprietary vendor can sometimes try to push the design flaw onto the architecture, in the case of Linux we do not have the latitude to do this.
This is another case where managing the development of Linux and making design decisions about Linux dictate the same approach. From a practical point of view, I couldn't manage lots of developers contributing interfaces to the kernel. I would not have been able to keep control over the kernel. But from a design point of view this is also the right thing to do: keep the kernel relatively small, and keep the number of interfaces and other constraints on future development to a minimum.
Of course Linux is not completely clean in this respect. Linux has inherited a number of terrible interfaces from previous implementations of Unix. So in some cases I would have been happier if I did not have to maintain the same interface as Unix. But Linux is about as clean as a system can be without starting completely from scratch. And if you want the benefit of being able to run Unix applications, then you get some of the Unix baggage as a consequence. Being able to run those applications has been vital to Linux's popularity, so the tradeoff is worth it.