48

I am studying operating systems and the x86 architecture, and while I was reading about segmentation and paging I naturally was curious how modern OSes handle memory management. From what I found Linux and most other operating systems essentially shun segmentation in favor of paging. A few of the reasons for this that I found were simplicity and portability.

What practical uses are there for segmentation (x86 or otherwise) and will we ever see robust operating systems using it or will they continue to favor a paging based system.

Now I know this is a loaded question but I am curious how segmentation would be handled with newly developed operating systems. Does it make so much sense to favor paging that no one will consider a more 'segmented' approach? If so, why?


And when I say 'shun' segmentation I am implying that Linux only uses it as far as it has to. Only 4 segments for user and kernel code/data segments. While reading the Intel documentation I just got the feeling that segmentation was designed with more robust solutions in mind. Then again I was told on many occasions how over complicated the x86 can be.


I found this interesting anecdote after being linked to Linux Torvald's original 'announcement' for Linux. He said this a few posts later:

Simply, I'd say that porting is impossible. It's mostly in C, but most people wouldn't call what I write C. It uses every conceivable feature of the 386 I could find, as it was also a project to teach me about the 386. As already mentioned, it uses a MMU, for both paging (not to disk yet) and segmentation. It's the segmentation that makes it REALLY 386 dependent (every task has a 64Mb segment for code & data - max 64 tasks in 4Gb. Anybody who needs more than 64Mb/task - tough cookies).

I guess my own experimentation with x86 led me to ask this question. Linus didn't have StackOverflow, so he just implemented it to try it out.

5
  • What book did you read?
    – osgx
    Aug 10, 2011 at 14:23
  • 1
    I am reading a number of books. I started to ask myself this while reading the Intel Systems Programming manual (vol 3), but I read a little about Linux memory management in "Understanding the Linux Kernel" and other sources online. Aug 10, 2011 at 14:25
  • In particular I was reading the section on Local Descriptor Tables, and I was curious how operating systems used these. Aug 10, 2011 at 14:32
  • 1
    OpenBSD combines x86 segmentation and paging to get NX bit simulation (security feature to prohibit execution of data pages). May be PaX used this too.
    – osgx
    Aug 10, 2011 at 14:33
  • I know next to nothing on the subject. I just typed in a search question to see answers for complaints about all currently used operating systems. Looking at the complaints, most people use pc's and now tablets for a few specific tasks. So why not allocate more memory usage to do those tasks quicker as opposed to giving all of the peripheral crap that is running access to it.
    – user97211
    Jul 20, 2013 at 3:08

8 Answers 8

36

With segmentation it would be, for example, possible to put each dynamically allocated object (malloc) in its own memory segment. Hardware would check segment limits automatically, and the whole class of security bugs (buffer overruns) would be eliminated.

Also, since all segment offsets start at zero, all compiled code would automatically be position independent. Calling into another DLL would boil down to a far call with constant offset (depending on the called function). This would greatly simplify linkers and loaders.

With 4 protection rings, it is possible to devise more fine-grained access control (with paging you have only 2 protection levels: user and supervisor) and more robust OS kernels. For example, only ring 0 has full access to the hardware. By separating the core OS kernel and device drivers into rings 0 and 1, you could make a more robust and very fast microkernel OS where most of the relevant access checks would be done by HW. (Device drivers could get access to hardware through I/O access bitmap in the TSS.)

However.. x86 is a bit limited. It has only 4 "free" data segment registers; reloading them is rather expensive, and it is possible to simultaneously access only 8192 segments. (Assuming you want to maximize the number of accessible objects, so the GDT holds only system descriptors and LDT descriptors.)

Now, with 64-bit mode segmentation is described as "legacy" and hardware limit checks are done only in limited circumstances. IMHO, a BIG mistake. Actually I don't blame Intel, I mostly blame developers, the majority of which thought that segmentation was "too complicated" and longed for flat address space. I also blame the OS writers who lacked the imagination to put segmentation to good use. (AFAIK, OS/2 was the only operating system which made full use of segmentation features.)

5
  • 1
    This is why I left this opened. There is sure to be a few different takes on the issue... Aug 11, 2011 at 16:42
  • 1
    @zvrba: What a superb explanation!!! Thanx for that. Now I have a doubt: Don't you think that INTEL could have won the big prize by making segments non overlapping and 4GB capable with help from paging? I mean, as I understood, "segmentation with paging" is only capable of addressing a max of 4GB virtual memory address space. And that's 'peanuts'!!! Imagine being able to have a Code, Stack, Data segments as big as 4GB each and non-overlapping or overlapping as you would need! And that would have been a major success at the time, without having to call for a full 64bit architecture as nowdays.
    – fante
    Dec 3, 2016 at 22:09
  • 1
    Fantastic explanation of why segmentation is good. It's a terrible shame that it has fallen by the wayside. Here is an elaboration with more details for those who are curious to learn more.
    – GDP2
    Nov 12, 2017 at 1:14
  • 1
    No wonder I loved OS/2! What a sad loss of a truly valuable technology thanks to ignorance and marketing.
    – ylluminate
    Nov 12, 2017 at 22:17
  • 2
    Anyone that thinks segmentation is a good idea must not be old enough to remember how awful segmentation is. It's awful. Practically all C code ever written expects a flat address space. It is convenient to be able to look at a pointer and just see its address, not have to go dig into the segment base, assuming that is even possible, which it isn't in x86 protected mode segmentation, unless the kernel lets you see it somehow, most likely with a very expensive system call. Swapping isn't possible with segments, unless you swap out entire segments. Paging is far, far superior.
    – doug65536
    Oct 1, 2019 at 10:52
27

The short answer is that segmentation is a hack, used to make a processor with a limited ability to address memory exceed those limits.

In the case of the 8086, there were 20 address lines on the chip, meaning that it could physically access 1Mb of memory. However, the internal architecture was based around 16 bit addressing, probably due to the desire to retain consistency with the 8080. So the instruction set included segment registers that would be combined with the 16-bit indexes to allow addressing of the full 1Mb of memory. The 80286 extended this model with a true MMU, to support segment-based protection and addressing of more memory (iirc, 16Mb).

In the case of the PDP-11, later models of the processor provided a segmentation into Instruction and Data spaces, again to support the limitations of a 16-bit address space.

The problem with segmentation is simple: your program must explicitly work around the limitations of the architecture. In the case of the 8086, this meant that the largest contiguous block of memory that you could access was 64k. if you needed to access more than that, you would have to change your segment registers. Which meant, for a C programmer, that you had to tell the C compiler what sort of pointers it should generate.

It was a lot easier to program the MC68k, which had a 32-bit internal architecture and a 24-bit physical address space.

11
  • 5
    Ok, that all makes sense. However, reading the Intel documents one would be inclined to think segments could actually be used for greater hardware level protection against program bugs. Specifically section 3.2.3 of the Systems Programming Guide - are there advantages to the multi-segment model? Would it be correct to say Linux uses the protected flat model? (section 3.2.2) Aug 10, 2011 at 17:22
  • 3
    It's been a long time since I paid attention to the details of the Intel memory architecture, but I don't think that the segmented architecture would provide any greater hardware protection. The only real protection that an MMU can give you is to separate code and data, preventing buffer overrun attacks. And I believe that's controllable without segments, via page-level attributes. You could theoretically restrict access to objects by creating a separate segment for each, but I don't think that's reasonable.
    – parsifal
    Aug 10, 2011 at 17:37
  • 1
    Thanks, you have brought back all the repressed memories of doing image processing on segmented memory - this is going to mean more therapy! Aug 10, 2011 at 19:15
  • 11
    You have completely misunderstood the segmentation. In 8086 it might have been a hack; 80286 introduced protected mode where it was crucial for protection; in 80386 it was even further extended and segments can be larger than 64kB, still with the benefit of hardware checks. (BTW, 80286 did NOT have an MMU.)
    – zvrba
    Aug 11, 2011 at 9:59
  • 2
    Back in 1985 when the 386 was introduced, a 4 GiB address space was considered to be enormous. Remember that a 20 MiB hard disk was rather large at the time, and it still wasn't entirely uncommon for systems to come with only floppy disk drives. The 3.5" FDD was introduced in 1983, sporting a formatted capacity of a whopping 360 KB. (1.44 MB 3.5" FDDs became available in 1986.) To within experimental error, everybody back then thought of a 32 bits address space as we now think of 64 bits: physically approachable, but so large so as to be practically infinite.
    – user
    Jun 12, 2017 at 13:32
20

For 80x86 there are 4 options - "nothing", segmentation only, paging only, and both segmentation and paging.

For "nothing" (no segmentation or paging) you end up with no easy way to protect a process from itself, no easy way to protect processes from each other, no way to handle things like physical address space fragmentation, no way to avoid position independent code, etc. Despite all these problems it might (in theory) be useful in some situations (e.g. embedded device that only runs one application; or maybe something that uses JIT and virtualises everything anyway).

For segmentation only; it almost solves the "protect a process from itself" problem, but it takes a lot of work-arounds to make it usable when a process wants to use more than 8192 segments (assuming one LDT per process), which makes it mostly broken. You almost solve the "protect processes from each other" problem; but different pieces of software running at the same privilege level can load/use each other's segments (there's ways to work around that - modifying GDT entries during control transfers and/or using LDTs). It also mostly solves the "position independent code" problem (it can cause a "segment dependant code" problem but that's much less significant). It doesn't do anything for the "physical address space fragmentation" problem.

For paging only; it doesn't solve the "protect a process from itself" problem much (but let's be honest here, this is only really a problem for debugging/testing code written in unsafe languages, and there's much more powerful tools like valgrind anyway). It completely solves the "protect processes from each other" problem, completely solves the "position independent code" problem, and completely solves the "physical address space fragmentation" problem. As an added bonus it opens up some very powerful techniques that aren't anywhere near as practical without paging; including things like "copy on write", memory mapped files, efficient swap space handling, etc.

Now you'd think that using both segmentation and paging would give you the benefits of both; and in theory it can, except that the only benefit you gain from segmentation (that isn't done better by paging) is a solution to the "protect a process from itself" problem that nobody really cares about. In practice what you do get is the complexities of both and the overhead of both, for very little benefit.

This is why almost all OSs designed for 80x86 don't use segmentation for memory management (they do use it for things like per-CPU and per-task storage but that's mostly just for convenience to avoid consuming a more useful general purpose register for these things).

Of course CPU manufacturers aren't silly - they aren't going to spend time and money optimising something that they know nobody uses (they're going to optimise something that almost everyone uses instead). For this reason CPU manufacturers don't optimise segmentation, which makes segmentation slower than it could be, which makes OS developers want to avoid it even more. Mostly they only kept segmentation for backward compatibility (which is important).

Eventually, AMD designed long mode. There was no old/existing 64 bit code to worry, so (for 64-bit code) AMD got rid of as much segmentation as they could. This gave OS developers yet another reason (no easy way to port code designed for segmentation to 64-bit) to continue avoiding segmentation.

16

I'm rather stunned that in all the time since this question was posted that nobody has mentioned the origins of segmented memory architectures and the true power that they can afford.

The original system which either invented, or refined into useful form, all the features surrounding the design and use of segmented paged virtual memory systems (along with symmetric multi-processing and hierarchical filesystems) was Multics (and see also the Multicians site). Segmented memory allows Multics to offer a view to the user that everything is in (virtual) memory, and it allows the ultimate level of sharing of everything in direct form (i.e. directly addressable in memory). The filesystem becomes simply a map to all the segments in memory. When properly used in a systematic way (as in Multics) segmented memory frees the user from the many burdens of managing secondary storage, and sharing of data, and inter-process-communications. Other answers have made some hand-wavy claims that segmented memory is more difficult to use, but this is simply not true, and Multics proved that with resounding success decades ago.

Intel created a hobbled version of segmented memory the 80286 that although it is quite powerful, its limitations prevented it from being used for anything truly useful. The 80386 improved upon these limitations, but market forces at the time pretty much prevented the success of any system that could truly take advantage of these improvements. In the years since it seems all too many people have learned to ignore the lessons of the past.

Intel also tried early on to build a more capable super-micro called the iAPX 432 that would have far surpassed anything else at the time, and it had a segmented memory architecture and other features strongly oriented towards object oriented programming. The original implementation was just too slow though, and no further attempts were made to fix it.

A more detailed discussion of how Multics used segmentation and paging can be found in Paul Green's paper Multics Virtual Memory - Tutorial and Reflections

3
  • 1
    Great info and superb arguments. Thanx for the links, they are priceless!!!
    – fante
    Dec 3, 2016 at 23:27
  • 1
    Thanks for linking to Multics and for the very informative answer! Clearly segmentation was superior in many ways to what we do now.
    – GDP2
    Nov 12, 2017 at 1:15
  • 1
    Your answer is a true gem in the rough. Thank you so much for sharing these insights that we've lost. It makes me yearn to see a return to segmentation via the development of a proper OS that could prompt the refinement of hardware. Truly so many issues could be ameliorated with this approach! It even sounds as though we could get true OOP languages at much higher performance levels and at the bare metal with segmentation.
    – ylluminate
    Nov 12, 2017 at 22:15
7

Some architectures (like ARM) don't support memory segments at all. If Linux had been source-dependent on segments, it couldn't have been ported to those architectures very easily.

Looking at the broader picture, the failure of memory segments has to do with the continuing popularity of C and pointer arithmetic. C development is more practical on an architecture with flat memory; and if you want flat memory, you choose memory paging.

There was a time around the turn of the 80's when Intel, as an organization, was anticipating future popularity of Ada and other higher-level programming languages. This is basically where some of their more spectacular failures, like the awful APX432 and 286 memory segmentation, came from. With the 386 they capitulated to flat memory programmers; paging and a TLB was added and the segments were made resizable to 4GB. And then AMD basically removed segments with x86_64 by making the base reg a dont-care/implied-0 (except for fs? for TLS I think?)

Having said that, the advantages of memory segments are obvious - switching address spaces without having to repopulate a TLB. Maybe someday someone will make a performance-competitive CPU that supports segmentation, we can program a segmentation-oriented OS for it, and programmers can make Ada/Pascal/D/Rust/another-langage-that-doesn't-require-flat-memory programs for it.

6

Segmentation was a hack / workaround to allow up to 1MB of memory to be addressed by a 16 bit processor - normally only 64K of memory would have been accessible.

When 32 bit processors came along you could address up to 4GB of memory with a flat memory model and there was no longer any need for segmentation - The segment registers were re-purposed as selectors for the GDT / paging in protected mode (although you can have protected mode 16-bit).

Also a flat memory mode is far more convenient for compilers - you can write 16-bit segmented programs in C, but its a tad cumbersome. A flat memory model makes everything simpler.

2
  • Is there much to be said of the 'protection' provided by segmentation when we can just use paging instead? Aug 10, 2011 at 17:35
  • 1
    @Mr. Shickadance Segmentation doesn't provide any sort of memory protection - For memory protection you need protected mode where you can protect memory using either the GDT or paging.
    – Justin
    Aug 11, 2011 at 8:13
2

Segmentation is a huge burden for applications developers. This is where the big push came from to do away segmentation.

Interestingly I often wonder how much better i86 could be if Intel striped out all legacy support for these old modes. Here better would imply lower power and maybe faster operation.

I guess one could argue that Intel soured the milk with 16bit segments leading to a developer revolt of sorts. But let's face it a 64k address space is nothing especially when you look at modern app. In the end they had to do something because the competition could and did market effectively against the address space issues of i86.

2

Segmentation leads to slower page translations and swapping

For those reasons, segmentation was largely dropped on x86-64.

The main difference between them is that:

  • paging splits memory into fixed sized chunks
  • segmentation allows different widths for each chunk

While it might appear smarter to have configurable segment widths, as you increase memory size for a process, fragmentation is inevitable, e.g.:

|   | process 1 |       | process 2 |                        |
     -----------         -----------
0                                                            max

will eventually become as process 1 grows:

|   | process 1        || process 2 |                        |
     ------------------  -------------
0                                                            max

until a split is inevitable:

|   | process 1 part 1 || process 2 |   | process 1 part 2 | |
     ------------------  -----------     ------------------
0                                                            max

At this point:

  • the only way to translate pages is to do binary searches over all pages of process 1, which takes an unacceptable log(n)
  • a swap out of process 1 part 1 could be huge since that segment could be huge

With fixed sized pages however:

  • every 32-bit translation does only 2 memory reads: directory and page table walk
  • every swap is an acceptable 4KiB

Fixed sized chunks of memory are simply more manageable, and have dominated current OS design.

See also: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.