You said you are interested in why:
In real mode, the segment is a 64K “window” into physical memory, and these windows are spaced 16 bytes apart. In protected mode, a segment is a window into physical or virtual memory, the size and location of which is determined by the OS, and it has many other properties, including the privilege level that a process must have to access it.
From now on, everything that I say relates to the protected regime.
There is a table in memory called the global descriptor table (GDT), which stores information about these window sizes and locations and other properties. There may also be local descriptor tables for each process, and they work in a similar way, so I just focus on GDT.
The value that you load into the segment register is called the segment selector. This is an index in GDT or LDT with some additional security information. Naturally, if a program tries to load a handle that goes beyond the GDT, an exception is thrown. Also, if the process does not have sufficient authority to access the segment or something else is invalid, an exception is thrown.
When an exception occurs, the kernel processes it. Such an exception is likely to be classified as a segmentation error. Thus, the OS kills your program.
There is one final caveat: in the x86 instruction set, you cannot load instantaneous values into segment registers. You must use an intermediate register or memory operand or POP in the segment register.
MOV DS, 160; INVALID - won't assemble
MOV AX, 160; VALID - assembles, but will probably result in an
MOV DS, AX; exception, and thus the death of your program
I think it should be noted that architecture allows heaps of segments. But AFAIK, when it comes to major x86 operating systems, segment registers serve only a few purposes:
- Security mechanisms, such as storing user space processes from harm to each other or the OS
- Work with multiple / multi-core processors
- Stream local storage: as an optimization, some operating systems (including Linux and Windows) use segment registers for local stream storage (TLS). Since streams use the same address space, it is difficult for a stream to know where its TLS area does not use a system call or lose case ... but since segment registers are practically useless, there is no harm in wasting them, for the sake of fast TLS. Please note that when setting up the OS, the OS can skip segment registers and write directly to descriptor cache registers, which are “hidden” registers used to cache GDT / LDT requests caused by references to segment registers, in which case, if you try to reading from segment registers you will not see it.
In addition to the segment per thread for TLS, in fact, only a few segments are used (times the number of processors) and only the OS. Applications can completely ignore segment registers.
This is due to the design of the OS, and not to technical limitations. There may be embedded operating systems that require user-space programs to work with segment registers, although I don't know anything.