Memory
map/address space: The map of memory locations available to a
particular Linux thread. The address space of a thread is defined
through a mm struct, pointed to by the thread.
For Linux OS ported to the MIPS architecture (hereinafter, "Linux/ MIPS") on a
32-bit processor, the high half of the address space (addresses with bit 31 set) can be
read and written only in kernel-privilege mode.
The kernel code/data is normally in the corner of this, known as
kseg0, which means the kernel itself does not depend on addresses
translated through the TLB.
The user part of the address space is mapped differently for each
Application - only threads that collaborate in an explicitly
multithreaded application share the user address space (i.e., they
point to the same mm struct). But all Linux threads share the same
kernel map.
A thread running a conventional single-threaded application runs in
an address space that is distinct from all other threads and is exactly
what older UNIX-like systems called a "process."
At any given time, much of an application's address space may not in
fact be mapped, or even not represented by any data present in physical
memory at all.
An attempt to access that will cause a TLB exception, which will be
handled by the OS, which will load any missing data and set up an
appropriate mapping before it returns to the application. That is, of
course, virtual memory.
Thread group: The
collection of threads within the same memory map is called a thread
group. Where a group has two or more members, those threads are
cooperating to run the same program. The thread group is another good
approximation in Linux to what is called a "process" in old UNIX
systems.
High memory:
Physical memory above 512 MB (whether real read/write memory or
memory-mapped I/O locations) is not directly accessible through the
kseg0 (cached) or kseg1 (uncached) windows.
On a 32-bit CPU physical addresses above the low 512 MB are "high
memory" in the Linux sense and can only be accessed through TLB
mappings. With a MIPS CPU, you can create a few permanent mappings by
defining "wired" TLB entries, protected from replacement.
But Linux tries to avoid using resources that will quickly run out,
so mainstream kernel code avoids wired entries completely. For
Linux/MIPS, high-memory mappings are maintained dynamically by TLB
entries created on demand.
Libraries and
applications: Long ago, applications running on UNIX-like
systems were monolithic pieces of code, which were loaded as required.
You built them by compiling some source code and gluing in some library
functions - prebuilt binaries provided with your tool chain.
But there are two things wrong with that. One is that the library
code is often bigger than the application that attaches to it, bloating
all the programs. The other is that if a supplier fixes a bug in a
library function, you don't get full benefit from the fix until every
software maintainer rebuilds his or her application.
Instead, the application is built without the library functions. The
names of the missing libraries are built into the application, so the
loader can find the required libraries and stitch them in when the
application is loaded.
So long as the library continues to provide identical functions,
everything should be fine (there's a library version"tracking system to
allow libraries to evolve functionally, too, but that's beyond our
scope).
That carries a penalty. When you link a program at load time out of
pieces (each of which may get separately updated), the exact address of
the components is unpredictable at build time. You can't predict in
advance which locations will be available for loading a particular
library.
The runtime loader can do no better than to load each library in the
next space available, so even the starting address for a library is
unpredictable. A library binary has to be position-independent code or
PIC - it must run correctly wherever its code and data are positioned
in virtual address space.