14.9. Memory-Mapped I/OSection 2.5 of McKusick et al. [1996]). Gingell, Moran, and Shannon [1987] describe one implementation of mmap. The mmap function is included in the memory-mapped files option in the Single UNIX Specification and is required on all XSI-conforming systems; most UNIX systems support it.To use this feature, we have to tell the kernel to map a given file to a region in memory. This is done by the mmap function.[View full width]#include <sys/mman.h> void *mmap(void *addr , size_t len , int prot , int ![]() off_t off ); |
Returns: starting address of mapped region if OK, MAP_FAILED on error |
Figure 14.31. Example of a memory-mapped file
[View full size image]

MAP_FIXED | The return value must equal addr . Use of this flag is discouraged, as it hinders portability. If this flag is not specified and if addr is nonzero, then the kernel uses addr as a hint of where to place the mapped region, but there is no guarantee that the requested address will be used. Maximum portability is obtained by specifying addr as 0. Support for the MAP_FIXED flag is optional on POSIX-conforming systems, but required on XSI-conforming systems. | ||||||
MAP_SHARED | This flag describes the disposition of store operations into the mapped region by this process. This flag specifies that store operations modify the mapped filethat is, a store operation is equivalent to a write to the file. Either this flag or the next (MAP_PRIVATE), but not both, must be specified. | ||||||
MAP_PRIVATE | This flag says that store operations into the mapped region cause a private copy of the mapped file to be created. All successive Section 2.5.4) with an argument of _SC_PAGESIZE or _SC_PAGE_SIZE. Since off and addr are often specified as 0, this requirement is not a big deal.Since the starting offset of the mapped file is tied to the system's virtual memory page size, what happens if the length of the mapped region isn't a multiple of the page size? Assume that the file size is 12 bytes and that the system's page size is 512 bytes. In this case, the system normally provides a mapped region of 512 bytes, and the final 500 bytes of this region are set to 0. We can modify the final 500 bytes, but any changes we make to them are not reflected in the file. Thus, we cannot append to a file with mmap. We must first grow the file, as we will see in Figure 14.32.Figure 14.32. Copy a file using memory-mapped I/O#include "apue.h" #include <fcntl.h> #include <sys/mman.h> int main(int argc, char *argv[]) { int fdin, fdout; void *src, *dst; struct stat statbuf; if (argc != 3) err_quit("usage: %s <fromfile> <tofile>", argv[0]); if ((fdin = open(argv[1], O_RDONLY)) < 0) err_sys("can't open %s for reading", argv[1]); if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE)) < 0) err_sys("can't creat %s for writing", argv[2]); if (fstat(fdin, &statbuf) < 0) /* need size of input file */ err_sys("fstat error"); /* set size of output file */ if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) err_sys("lseek error"); if (write(fdout, ", 1) != 1) err_sys("write error"); if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0)) == MAP_FAILED) err_sys("mmap error for input"); if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == MAP_FAILED) err_sys("mmap error for output"); memcpy(dst, src, statbuf.st_size); /* does the file copy */ exit(0); } Two signals are normally used with mapped regions. SIGSEGV is the signal normally used to indicate that we have tried to access memory that is not available to us. This signal can also be generated if we try to store into a mapped region that we specified to mmap as read-only. The SIGBUS signal can be generated if we access a portion of the mapped region that does not make sense at the time of the access. For example, assume that we map a file using the file's size, but before we reference the mapped region, the file's size is truncated by some other process. If we then try to access the memory-mapped region corresponding to the end portion of the file that was truncated, we'll receive SIGBUS.A memory-mapped region is inherited by a child across a fork (since it's part of the parent's address space), but for the same reason, is not inherited by the new program across an exec.We can change the permissions on an existing mapping by calling mprotect.
ExampleThe program in Section 4.13.)Extending a file with ftruncate works on the four platforms discussed in this text.We then call mmap for each file, to map the file into memory, and finally call memcpy to copy from the input buffer to the output buffer. As the bytes of data are fetched from the input buffer (src), the input file is automatically read by the kernel; as the data is stored in the output buffer (dst), the data is automatically written to the output file.Exactly when the data is written to the file is dependent on the system's page management algorithms. Some systems have daemons that write dirty pages to disk slowly over time. If we want to ensure that the data is safely written to the file, we need to call msync with the MS_SYNC flag before exiting.1992] describe an alternative to the standard I/O library (Chapter 5) that uses memory-mapped I/O.We return to memory-mapped I/O in Section 15.9, showing an example of how it can be used to provide shared memory between related processes. |