Section 9.2. Virtual Address Spaces


9.2. Virtual Address Spaces

The virtual address space of a process is the range of memory addresses that are presented to the process as its environment; some addresses are mapped to physical memory, some are not. A process's virtual address space skeleton is created by the kernel at the time the fork() system call creates the process. (See Section 2.7.) The virtual address layout within a process is set up by the dynamic linker and sometimes varies across different hardware platforms. As we can see in Figure 9.2, virtual address spaces are assembled from a series of memory mappings. Each process has at least four mappings:

  • Executable text. The executable instructions in the binary reside in the text mapping. The text mapping is mapped from the on-disk binary and is mapped read-only, with execute permissions.

  • Executable data. The initialized variables in the executable reside in the data mapping. The data mapping is mapped from the on-disk binary and is mapped read/write/private. The private mapping ensures that changes made to memory within this mapping are not reflected out to the file or to other processes mapping the same executable.

  • Heap space. Scratch, or memory allocated by malloc(), is allocated from anonymous memory and is mapped read/write.

  • Process stack. The stack is allocated from anonymous memory and is mapped read/write.

Figure 9.2. Process Virtual Address Space


Figure 9.2 illustrates a process's virtual address space.

The figure shows how the /sbin/sh process has its executable mapped in near the bottom address, with the heap adjoining it, the stack at the top, and a hole between the heap and the stack. The heap grows upward as more memory is allocated through malloc(), and the stack grows downward as more frames are placed on the stack. Not all of the virtual address space within a process is mapped, and the process can legally access memory only within the areas with valid mappings; a process's attempt to access memory outside of the mappings causes a page fault. A more sophisticated process may have more mappings; those that make use of shared libraries or mapped files will have additional mappings between the heap and stack.

9.2.1. Sharing Executables and Libraries

The Solaris kernel supports sharing of memory, files, libraries, and executables. For example, the Solaris kernel shares libraries by dynamically mapping the library file into the address space during program startup. The libraries are mapped into the address space between the stack and the heap, at different positions on different platforms.

When a shared library object is mapped into a process's address space, it can be mapped shared so that all processes share the same physical memory pages. Executable text and data are shared in the same manner, by simply mapping the same executable file into every address space.

We see more about how mapping of files and sharing of memory occur when we explore the vnode segment driver, which is responsible for mapping files into address spaces.

9.2.2. Address Spaces on SPARC Systems

The process address space on SPARC systems varies across different SPARC platforms according to the MMU on that platform. SPARC has three different address space layouts:

  • The SPARC V7 combined 32-bit kernel and process address space, found on sun4c, sun4d, and sun4m machines. Note that support for SPARC V7 exists only in Solaris 9 and earlier.

  • The SPARC V9 32-bit separated kernel and process address space model, found on sun4u machines

  • The SPARC V9 64-bit separated kernel and process address space model, found on sun4u machines

The SPARC V7 systems use a shared address space between the kernel and process and use the processor's privilege levels to prevent user processes from accessing the kernel's address space. The kernel occupies the top virtual memory addresses, and the process occupies the lower memory addresses. This means that part of the virtual address space available to the process is consumed by the kernel, limiting the size of usable process virtual memory to between 3.5 and 3.75 Gbytes, depending on the size of the kernel's virtual address space. This also means that the kernel has a limited size, ranging between 128 and 512 Mbytes. The SPARC V7 combined 32-bit kernel and process address space is shown in Figure 9.3.

Figure 9.3. SPARC 32-Bit Shared Kernel/Process Address Space


The SPARC V9 (UltraSPARC, sun4u) microprocessor allows the kernel to operate in an address space separate from user processes, so the process can use almost all of the 32-bit address space (a tiny bit is reserved at the top for the Open Boot PROM) and also allows the kernel to have a similar, large address space. This design removes the 512-Mbyte limit for kernel address space, which was a major problem for large machines such as the older SPARCcenter 2000 machines. The process address space looks similar to the shared kernel/process address space, except that the kernel area is missing and the stack and libraries are moved to the top of memory.

The UltraSPARC processor also supports the SPARC V9 64-bit mode, which allows a process to have a virtual address space that spans 64 bits. The Ultra-SPARC-I and -II implementations, however, support only 44 bits of the address space, which means that there is a virtual address space hole in the middle of the address space. This area of memory creates a special type of UltraSPARC trap when accessed. Some future generations of SPARC V9 processors will not have the same hole in the address space. The UltraSPARC V9 32-bit and 64-bit address spaces are shown in Figure 9.4.

Figure 9.4. SPARC sun4u 32- and 64-Bit Process Address Space


On all SPARC platforms, the bottom of the virtual address space is not mapped. Null pointer references cause a segmentation fault rather than return spurious contents of whatever was at the bottom of the address space.

9.2.3. x86 and x64 Address Space Layout

The Intel x86 32-bit user address space also includes a mapping of the kernel. The main difference with the Intel address space is that the space is reserved at the top of the address space for the kernel and the stack is mapped underneath the executable binary, growing down toward the bottom. The x64 address space is a closer representation of the SPARC 64-bit address space. The x86 and x64 address spaces are shown in Figure 9.5.

Figure 9.5. x86/x64 Process Address Spaces


9.2.4. Growing the Heap

Process virtual memory for user data structures is allocated from the heap mapping, which resides above the executable data mapping. The heap starts out small and then grows as virtual memory is allocated. The heap grows in units of pages; it is simply a large area of virtual memory available for reading and writing. A single, large, virtual memory area is difficult to program to, so a general-purpose memory allocator manages the heap area; thus, arbitrarily sized memory objects can be allocated and freed. The general-purpose memory allocator is implemented with malloc() and related library calls.

A process grows its heap space by making the sbrk() system call. The sbrk() system call grows the heap mapping by the amount requested each time it is called. A user program does not need to call sbrk() directly because the malloc() library calls sbrk() when it needs more space to allocate from. The sbrk() system call is shown below.

void *sbrk(intptr_t incr); 


The heap mapping is virtual memory, so requesting memory with malloc and sbrk does not allocate physical memory; it merely allocates the virtual address space. Only when the first reference is made to a page within the allocated virtual memory is physical memory allocated, one page at a time. The memory system transparently achieves this "zero fill on demand" allocation because a page fault occurs the first time a page is referenced in the heap, and the segment driver then recognizes the first memory access and simply creates a page at that location on-the-fly.

Memory pages are allocated to the process heap by zero-fill-on-demand and then remain in the heap mapping until the process exits or until they are stolen by the page scanner. Calls to the memory allocator free() function do not return physical memory to the free memory pool; free() simply marks the area within the heap space as free for later use. For this reason, the amount of physical memory allocated to a process typically grows, but unless there is a memory shortage, it will not shrink, even if free() has been called.

The heap can grow until it collides with the memory area occupied by the shared libraries. The maximum size of the heap depends on the platform virtual memory layout and differs on each platform. In addition, on 64-bit platforms, processes may execute in either 32- or 64-bit mode. As shown in Figure 9.4, the size of the heap can be much larger in processes executing in 64-bit mode. Table 9.1 shows the maximum heap sizes and the operating system requirements that affect the maximum size.

Table 9.1. Maximum Heap Sizes

Solaris Version

Maximum Heap Size

Notes

Solaris x86 32-bit mode

2 Gbytes by default

Boot option kernel base can be moved to allow larger process address space.

Solaris x64 64-bit mode

16 Ebytes

Virtually unlimited.

SPARC 32-bit mode

3.75 Gbytes

3.90 Gbytes

(Non-sun4u platform).

(sun4u platforms).

SPARC 64-bit mode

16 Tbytes onUltraSPARC I and II

16 Ebytes on UltraSPARC III onwards.

Virtually unlimited.


9.2.5. The Stack

The process stack is mapped into the address space with an initial allocation and then grows downward. The stack, like the heap, grows on demand, but no library grows the stack; instead, a different mechanism triggers this growth.

Initially, a single page is allocated for the stack, and as the process executes and calls functions, it pushes the program counter, arguments, and local variables onto the stack. When the stack grows larger than one page, the process causes a page fault, and the kernel notices that this is a stack-mapping page fault and grows the stack mapping.

9.2.5.1. Memory Mapped Files

The address space mapping architecture makes it easy for one or more processes to map the same file into their address space. When files are mapped into one or more processes, seg_vn mappings are created in each process that points to the same vnode. Each process has its own virtual memory mapping to the file, but they all share the same physical memory pages for the files. The first mapping to cause a page fault reads a page into physical memory, and then the second and subsequent mappings simply create a reference to the existing physical memory pageas attaching.

Figure 9.6 shows how two processes can map the same file. Each process creates its own mapping object, but both mappings point to the same file and are mapped to the same physical pages. Notice that the second process need not have all the pages attached to the mapping, even if both mappings map the same parts of the file. In this case, the second process would attach to these pages when they are referenced. A minor fault is used to describe this event. You can see minor faults by using vmstat.

Figure 9.6. Shared Mapped Files


Several options govern how a file is shared when it is mapped between two or more processes. These options control how changes are propagated across the shared file. For example, if one process wants to modify one of the pages mapped into the process, should the other process see exactly the same change or should the change remain private to the process that made the change? The options allow you to choose which behavior you desire. The options are those that can be passed to the protection and flags argument of mmap() when the file is mapped. The behavior for the different flags is listed in Table 9.2.

Table 9.2. mmap Shared Mapped File Flags

Flag

Protection Mode

Result

MAP_SHARED

PROT_

READ|PROT_

WRITE

Modifications are reflected among all processes sharing the mapping.

MAP_PRIVATE

PROT_

READ|PROT_

WRITE

Modifications are seen only by the process mapping the file. The copy-on-write process creates a page of anonymous memory and gives a private copy to the process.


9.2.6. Using pmap to Look at Mappings

Use the pmap command to inspect the mappings for a process. One line of output is shown for each mapping, along with descriptive data.

sol9$ pmap 102905 102905:    sh 00010000    192K r-x--   /usr/bin/ksh                        [ Text Mapping ] 00040000      8K rwx--   /usr/bin/ksh                        [ Data Mapping ] 00042000     40K rwx--     [ heap ]                           [ Heap] FF180000    664K r-x--   /usr/lib/libc.so.1                  [ C Library Text ] FF236000     24K rwx--   /usr/lib/libc.so.1                  [ C Library Data ] FF23C000      8K rwx--   /usr/lib/libc.so.1                  [ C Library Data ctd... ] FF250000      8K rwx--     [ anon ]                          [ Misc anon mapping ] FF260000     16K r-x--   /usr/lib/en_US.ISO8859-1.so.2       [ Library mappings contunue..] FF272000     16K rwx--   /usr/lib/en_US.ISO8859-1.so.2 FF280000     560K r-x--  /usr/lib/libnsl.so.1 FF31C000      32K rwx--  /usr/lib/libnsl.so.1 FF324000      32K rwx--  /usr/lib/libnsl.so.1 FF340000      16K r-x--  /usr/lib/libc_psr.so.1 FF350000      16K r-x--  /usr/lib/libmp.so.2 FF364000       8K rwx--  /usr/lib/libmp.so.2 FF380000      40K r-x--  /usr/lib/libsocket.so.1 FF39A000       8K rwx--  /usr/lib/libsocket.so.1 FF3A0000       8K r-x--  /usr/lib/libdl.so.1 FF3B0000       8K rwx--    [ anon ] FF3C0000     152K r-x--  /usr/lib/ld.so.1 FF3F6000       8K rwx--  /usr/lib/ld.so.1 FFBFC000      16K rw---    [ stack ]                         [ Stack ]  total      188 


As shown in the example, the program's address space comprises several mappings. At the top is the program's binary, mapped as a read-only text mapping followed by a writable data mapping, continuing through to the process stack. Without any further options, pmap simply shows the starting address, the virtual address size, protection modes, and a description of each mapping. The columns are explained as follows:

  • Starting address. The starting virtual address of the mapping.

  • Size. The size of the virtual address mapping. This is typically the size between the start and end of the mappings.

  • Flags. One or more of the allowable permissions or flags are shown for the mapping:

    r. The mapping may be read by the process.

    w. The mapping may be written by the process.

    x. Instructions that reside within the mapping may be executed by the process.

    s. The mapping is shared such that changes made in the observed address space are committed to the mapped file and are visible from all other processes sharing the mapping.

    R. Swap space is not reserved for this mapping. Mappings created with MAP_NORESERVE and System V ISM shared memory mappings do not reserve swap space.





SolarisT Internals. Solaris 10 and OpenSolaris Kernel Architecture
Solaris Internals: Solaris 10 and OpenSolaris Kernel Architecture (2nd Edition)
ISBN: 0131482092
EAN: 2147483647
Year: 2004
Pages: 244

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net