Defined. File mapping is the association of a file's contents with a portion of the virtual address space of a process.
mmap(). In Linux, file mapping is accomplished with the mmap() system call:
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
The mmap() function asks the system to map length bytes starting at offset from the file (or other object) specified by the file descriptor fd into memory, preferably at address start. This latter address is a hint only, and is usually specified as 0. The actual place where the object is mapped is returned by mmap. The prot argument describes the desired memory protection.
NOTE: You can actually do much more with mmap(). Using the flags and prot options, you can get other behaviors from mmap(). Mappings do not have to be shared and mappings can be made to allocated memory instead of files. mmap() is often used simply to allocate memory. In this paper, we will restrict our consideration to the use of mmap() to provide shared file mappings.
Why mmap()? File mapping may be done for a number of reasons:
Loading Binary Files File mapping is also used within the Linux kernel. In particular, file mapping is used the by the binary loaders (see linux/fs/binfmt_elf.c, linux/fs/binfmt_aout.c, etc.). These are the kernel components that load an executable file, either a program or a shared library, into memory in preparation for their execution. They do this by simply calling mmap() to map the executable file into the user memory address space.
Shared and Private Program Segments. A Linux program consists of two classes of memory regions: (1) Read-only regions that can be shared by all executing copies of a program, and (2) Write-able regions which are unique to each copy of the program. The most important read-only region is the text section of a program that contains the executable instructions comprising the program. The text section is share-able by all executing instances of a program; many programs may be running, but they can all share the same in-memory copy of the program text section.
busybox. busybox is a wonderful example that exploits these shared text sections to conserve memory in an embedded system. busybox combines small versions of most, standard Unix utilities together in one, relatively big executable file. Although the single busybox executable is large, it is much smaller than the combined sizes of all of the individual utilities. And, since the text is shared via file mapping, there is never more instance of the busybox. The memory savings from busybox is one of most important enablers of embedded Linux today. (in addition to uClibc, both from Eric Anderson. The embedded Linux community owes a great debt to Eric and other key collaborators for all of their efforts!)
In addition to the share-able, read-only memory region, each program instance must have a private memory region where write-able, data is stored. Such private memory regions are necessary for program variables (for example, in .data and .bss sections), for program stack, and for privately allocated memory (such as from malloc()). These private sections allow each copy of a program to behave independently. The incremental cost for each copy of a program in memory is only this private data section (plus other, smaller kernel allocations).
Libraries. are collections of precompiled functions that have been written to be reusable. Typically, they consist of sets of related functions to perform a common task.
Static Libraries. The simplest library is a static library. A static library is a type of archive. An archive is a single file holding a collection of other files in a format that makes it possible to retrieve the original individual files (called members of the archive). A static library is an archive whose members are object files.
Disadvantages of Static Libraries. One disadvantage of static libraries is that many programs may use objects from the same library and, as a result, there may be many copies of the same objects. Multiple copies of the same objects consume a large amount of valuable storage resources (RAM, ROM, disk space, etc.). Also, when a static library is updated, all programs that use the library must be recompiled in order to take advantage of the updated logic. Shared libraries can overcome both of these disadvantages.
NOTE: There is a third, non-technical problem with the use of static shared libraries: The licensing for certain software packages (including GPL licensed software) may compromise the legal status of intellectual properties if software containing that property is linked directly with such software packages.
Shared Libraries. A Linux shared library is similar to a Linux program in its memory usage: The shared library's read-only segment (in particular, its .text section) can be shared among all processes; while its write-able sections (such as .data and .bss) can be allocated uniquely for each executing process.
uClinux. uClinux is a special version of Linux that is available for processors that do not have a memory management unit (MMU). Unfortunately, uClinux (2.4) does not support file mapping. This limitation is because the MMU is necessary to implement true file mapping! The MMU is required to map the user-space memory to the file position. The MMU is necessary to perform updates to file when writes are made to the mapped file memory region. The MMU is necessary to enforce protections on the memory (such as the read-only nature of a program mapping).
So without an MMU there can be no traditional mapping. But without file mapping, there can be no file sharing. There can be no sharing of program code sections. There can be no shared libraries. Busybox loses all of its appeal because multiple copies of busybox in memory actually consume more memory than the individual utilities may have.
But that can't be the case! How does the MMU-less embedded Linux overcome this limitation of uClinux?
XIP. The traditional solution to the file mapping limitation of uClinux (2.4) is to use an eXecute In Place (XIP) file system. With an XIP file system, programs do not have to be copied from the file system into system memory; rather, the program can execute directly on the file system image. The Linux file system subsystem supports special hooks into the file system (the MAGIC_ROM_PTR) that allows certain files systems function as XIP file system.
XIP and Shared TEXT Section. With XIP, there is no need to support file mapping to achieve text section sharing as described above. With XIP, full support for shared program sections and for shared libraries is obtained because the program/library image on the XIP file system is always unique. The program/library is never copied to RAM, therefore, there is only one shared copy of the read-only portions of the program/library.
XIP File System Limitations. Of course, the XIP file system must have special properties if it is going to be used in this way: The file system image must be provided on an underlying medium that will support execution, i.e., it must be a ROM, RAM, or FLASH based file system. Also, the file system probably must be read-only; you cannot permit modification of the contiguous program images. Keeping the program image contiguous precludes many write-able file system strategies. The popular romfs file system is an XIP file system that has these properties.
The Cadenux uClinux 2.4 BSP. Cadenux, LLC, has recently incorporated a limited file mapping capability into the uClinux kernel based on some initial uClinux file mapping ideas published on the uClinux-dev mailing list.
Credit Where Credit Is Due. The original patch set for uClinux 2.5 was developed by Bernardo Innocenti and appeared in the uClinux-dev mailing list in June of 2003 (see [uClinux-dev] PATCH: strip dead swap and file mapping code and again [uClinux-dev] PATCH: enable read-only filemapping for !CONFIG_MMU (2.5.73-uc0)) but were not incorporated into uClinux 2.6.
Diego Dompe of Cadenux, LLC, used these initial ideas as a spring board, back-ported the logic to the 2.4 kernel, made substantial improvements and fully tested them. The implementation is limited, of course, in that: (1) the entire file must be mapped into memory (rather than on demand),(2) writes to the file image will not be reflected in the stored file, and (3) no protections can be enforced.
uClinux File Mapping and Shared TEXT Sections. Although the file mapping provided in the uClinux (2.4) kernel is limited, it is sufficient to support read-only, shared file mappings on a non-XIP file system. This means that program TEXT sections are always shared, no matter what file system or block driver you are using. TEXT sharing will work with ext2 file systems, FAT file systems, JFFS file systems, whatever file system you choose to use. And TEXT sharing will work no matter what block driver you are using or what the underlying media is: It will work with RAM, ROM, IDE devices (such as compact flash or a rotating disk), MMC/SD,... whatever device you are using.
XFLAT Shared Library Technology. Cadenux, LLC, also develped an open-source shared library technology, called XFLAT, that provides shared library support for uClinux on MMU-less processors. At present, XFLAT is provided for the ARM processor family, however, the XFLAT solution is easily ported to any processor without changes to the core toolchain (GCC, LD).
Combined with the file mapping changes to the uClinux (2.4) kernel, XFLAT makes a very powerful solutions. The Cadenux uClinux 2.4 kernel now supports true shared libraries on all file systems and on all block drivers.
<<Table of Contents>>