Recall the aspects of the VM system: * Isolation (illusion of -- cf. debugging support) * Page sharing between apps * Demand paging * VM as file I/O cache Isolation is achieved with help from the harware (see previous lecture for pointers to Intel's address translation documents, or the "x86 internals" pp. 79--83). The other properties above are all due to the modern VM design, which (a) maintains a "reverse" mapping for all physical pages (b) uses the page fault handler as the main workhorse for paging in blocks from block devices and delegates to it whenever possible (e.g., "open" means "mmap", and "mmap" means page table entry setup; a "read" will then cause a #PF handler to actually call the driver's block reading code) (c) relies on the ELF format's rich knowledge about the structure of executables and libraries. Consider dynamic linking/loading design as motivated by the "economics" of reusing and remapping library code. As code gets more and more functionality, there is a break-even point between statically compiling all the needed function code into executables (only needed functions will be pasted into the final executable), and factoring shared code into dynamically loaded libraries (aka shared objects). Shared object code is trimmed off the executables, but now one must load the entire page of a dynamically linked library where a needed function is located. Hoewever, these loaded pages can be shared between multiple virtual address spaces if needed by them. Thus the VM trade-offs happen. Aside: The ELF format enables loaders to load code and global data sections at different addresses than suggested by the ELF section/segment headers. This is done by adding a *relocation section* for each section that may need to be loaded at a different address. Of course, these are emitted by the compiler based on its knowledge of all the instances of global address uses throughout the code and date. Relocation allows weird "hackish" used, such as almost arbitrary transformations of the malcode to confuse an antivirus, as demoed by LOCREATE: http://www.uninformed.org/?a=3&t=sumry&v=6 This article should explain quite a bit about the ELF format capabilities. For more ELF info, see http://althing.cs.dartmouth.edu/secref/resources/elf-hackery.shtml --------------------------------------------------------------------- Anatomy of address spaces: Chapter 9.2 explains the theory of address spaces. Chapter 9.4 explains how address spaces are implemented and handled: proc_t.p_as -> struct as -> (AVL Tree) -> "struct seg" Cf.: http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/vm/seg.h Observe seg_ops, the dispatch table of driver-specific operations that will handle consecutive memory segment mapping for the device: http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/vm/seg.h#103 We've seen this pattern for vnodes in VFS: http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/sys/vnode.h#900 "struct as"s' life cycle: as_alloc() [once, at system boot/init time] -> as_dup() [by fork()] as_dup() is how all address spaces after init's get created. Observe how AVL structures get copied: http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/vm/vm_as.c#782 Look for calls to as_dup() in fork() etc. Consider the distribution of work: pagefault() is called from the cmnint() and the #PF stub; it contains simple process-related checks, but all the real driver-specific work is done in as_fault(). Note how as_fault() updates the kernel's HAT structures (see Ch. 12.3 on HAT; we will go there next time): http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/vm/vm_as.c#867 Suggestion: Using DTrace's vminfo::: provider, observe all four kinds of page mappings in Fig. 9.2 (described on p. 457) in action for your favorite process. E.g., write simple "memory hog" programs to malloc() a lot of anonymous pages, or call functions with lots of stack-allocated local arrays. Observe file sharing between processes. Notice how *minor faults* are handled (see 9.4.4 for definitions) See Table 9.3 for address space manipulation functions.