Creating Memory Mappings

A memory mapping is established with the help of the mmap(2) function call. The function synopsis for it is as follows:

#include <sys/types.h>
#include <sys/mman.h>

void *mmap(void *addr,size_t len,int prot,int flags,int fd,off_t offset);

The argument addr is normally specified as a null pointer unless a specific mapping address must be used. When addr is null, the mmap(2) call returns the system-selected memory address.

When addr is not null, the argument flags influences the final result: The MAP_FIXED flag indicates that the specified address must be used, or an error indication is to be returned instead. When the flag MAP_FIXED is not present and addr is not null, the mmap(2) function will attempt to accommodate the requested address. Otherwise, it will substitute another address if the requested address cannot be successful.

Argument len is the size of the mapping in bytes. This usually corresponds to the length of the mapped file when files are involved. This length may be larger than the mapped file, but accesses beyond the last allocated page will cause a SIGBUS (bus error) signal to occur.

The prot argument indicates the type of memory protection required for this memory region. With the exception of PROT_NONE, which must be specified alone, the prot argument is specified with the following macros ORed together:

PROT_NONE Region grants no access (this flag is used exclusively of the other flags.)
PROT_READ Region grants read access.
PROT_WRITE Region grants write access.
PROT_EXEC Program instructions may be executed in the memory-mapped region.

Argument flags specifies a number of optional features for the mmap(2) function call. The portable flag bits are

MAP_FIXED Map to the address specified in argument addr or return an error if this cannot be satisfied. Normally when addr is not null, a different mapping address is substituted if the requested one is not acceptable.
MAP_PRIVATE Modifications to the mapped file are kept private. Unmodified pages are shared by all processes mapping the same file. When one of these memory pages is modified, a private page is created as a copy of the original, which is referenced only by the current process.
MAP_SHARED Modifications to the mapped file are eventually written back to the file. All processes share the changes.

Normally, the fd argument must be an open file descriptor (except with flag MAP_ANON). This represents the regular file or character special file to be mapped to memory. Once the mmap(2) call has returned, however, you may close the file descriptor, since the kernel maintains its own reference to that open file.

The argument offset is normally specified as 0. When other offsets are used, it must be a multiple of the page size returned by the function getpagesize(3). Otherwise, the error EINVAL is returned.

When mmap(2) is successful, the starting address for the mapping of at least len bytes is returned. Otherwise, the value MAP_FAILED is returned instead, with the error code deposited into errno.

FreeBSD mmap(2) supports additional features, which are selected by the following flag bits:

MAP_ANON Creates mapped memory that is not associated with any file. The file descriptor parameter must contain the value -1, and the offset argument is ignored.
MAP_INHERIT Allows a process to retain the memory mapping after an execve(2) system call.
MAP_HASSEMAPHORE Notifies the UNIX kernel that the memory-mapped region may have semaphores present. This allows the kernel to take special precaution for mutexes.
MAP_STACK Requests a stack region that grows downward to be created that is at most len bytes in size. The top of the stack is the returned pointer plus len bytes. This flag implies MAP_ANON and insists that the fd argument is -1, and offset must be 0. The prot argument must include at least PROT_READ and PROT_WRITE.

The flag value MAP_ANON is not supported directly by all UNIX platforms. Linux and IBM AIX 4.3 use the macro name MAP_ANONYMOUS instead.

Tip

To perform the equivalent of MAP_ANON for platforms without this mmap(2) feature, memory-map device /dev/zero using the MAP_PRIVATE flag.


The MAP_INHERIT flag allows you to retain a memory mapping after the execve(2) system call has successfully completed. This is a slick way to pass data from one executable program to another within the same process but suffers from the fact that other UNIX platforms do not support this feature.

The MAP_HASSEMAPHORE flag allows the programmer to hint to the kernel that mutex flags are present in the memory mapping. This allows the kernel to change its handling of the mapping, affecting the way changes are synchronized and perhaps the swapping status of the pages. This is a BSD UNIX feature.

The MAP_STACK flag bit allows you to create stack regions that have memory pages allocated as the stack grows downward. Many platforms, including HPUX 11 and UnixWare 7 do not support this option.

Note

On some hardware platforms, specifying a protection of PROT_EXEC also grants PROT_READ. This is due to platform hardware restrictions.


Table 26.1 shows a cross-referenced list of flag values that are supported by the various UNIX platforms.

Table 26.1. A Cross-Referenced Table of Supported mmap(2) Features
nmap(2) Flag Platform
 FreeBSD SGI IRIX 6.5 HPUX 11 UnixWare 7 Solaris 8 IBM AIX 4.3 Linux
MAP_SHARED X X X X X X X
MAP_PRIVATE X X X X X X X
MAP_FIXED X X X X X X X
MAP_ANON X    X   
MAP_ANONYMOUS      X X
MAP_HASSEMAPHORE X       
MAP_INHERIT X       
MAP_STACK X       
MAP_GROWSDOWN       X
MAP_AUTOGROW  X      
MAP_NORESERVE     X   
MAP_AUTORESRV  X      
MAP_LOCKED       X
MAP_ADDR32   X     
MAP_LOCAL  X      
MAP_FILE      X  
MAP_VARIABLE      X  

Listing 26.2 shows a program that uses memory-mapped files to select the language of system error messages.

Code Listing 26.2. messages.c—A Program That Uses mmap(2) to Select Messages by Language
1:   /* messages.c */
2:
3:   #include <stdio.h>
4:   #include <unistd.h>
5:   #include <stdlib.h>
6:   #include <fcntl.h>
7:   #include <errno.h>
8:   #include <string.h>
9:   #include <sys/types.h>
10:  #include <sys/stat.h>
11:  #include <sys/mman.h>
12:
13:  #define MAX_MSGS    12              /* Limit of univ_errlist[] */
14:
15:  const char *univ_errlist[MAX_MSGS]; /* Universal sys_errlist[] */
16:  const char *univ_maclist[MAX_MSGS]; /* A list of errno macro names */
17:
18:  static void *msgs = 0;              /* Pointer to the mapping */
19:  static size_t msgs_len = 0;         /* Size of the mapping */
20:
21:  /*
22:   * Parse error messages from the memory mapped file, that
23:   * begins at address msgs for msgs_len bytes :
24:   */
25:  static void
26:  parse_messages() {
27:      char *mp = (char *)msgs;        /* Mapped messages address */
28:      char *macro, *error, *msg;
29:      int e;
30:
31:      mp[msgs_len] = 0;               /* Store a null byte at the end */
32:
33:      for ( ;; mp = NULL ) {
34:
35:          macro = strtok(mp," ");     /* Extract macro name */
36:          if ( !macro )
37:              break;
38:
39:          error = strtok(NULL," ");   /* Extract error # */
40:          if ( !error )
41:              break;
42:          if ( (e = atoi(error)) < 0 || e >= MAX_MSGS )
43:              break;                  /* Bad errno value */
44:
45:          msg = strtok(NULL,"
");    /* Extract message */
46:          if ( !msg )
47:              break;
48:
49:          univ_errlist[e] = msg;      /* Store message */
50:          univ_maclist[e] = macro;    /* Macro name */
51:      }
52:  }
53:
54:  /*
55:   * Map the messages file to memory, and establish
56:   * pointers to them by calling parse_messages() :
57:   */
58:  static void
59:  load_messages() {
60:      int x;                              /* Iterator */
61:      char *lang = getenv("MSG_LANG");    /* Get language */
62:      char path[256];                     /* File name */
63:      struct stat sbuf;                   /* stat(2) info */
64:      int fd = -1;                        /* Open file descriptor */
65:
66:      /*
67:       * Load default messages :
68:       */
69:      for ( x=0; x<MAX_MSGS; ++x ) {
70:          univ_errlist[x] = sys_errlist[x];
71:          univ_maclist[x] = "?";
72:      }
73:
74:      /*
75:       * Get message file's size :
76:       */
77:      sprintf(path,"./errors.%s",lang ? lang : "english");
78:
79:      if ( stat(path,&sbuf) != 0 )
80:          return;     /* Cannot stat(2) file, so use default msgs */
81:      msgs_len = sbuf.st_size;
82:
83:      /*
84:       * Open the message file for reading :
85:       */
86:      if ( (fd = open(path,O_RDONLY)) == -1 )
87:          return;     /* Cannot open(2) file, so use default msgs */
88:
89:      /*
90:       * Map the language file to memory :
91:       */
92:      msgs = mmap(NULL,msgs_len+1,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
93:
94:      if ( msgs == (void *) MAP_FAILED ) {
95:          fprintf(stderr,"%s: mmap('%s')
",strerror(errno),path);
96:          close(fd);
97:          return;     /* Failed, use default */
98:      }
99:
100:     close(fd);      /* no longer require file to be open */
101:
102:     /*
103:      * Now parse the messages :
104:      */
105:     parse_messages();
106: }
107:
108: /*
109:  * Main program :
110:  */
111: int
112: main(int argc,char **argv) {
113:     int x;
114:
115:     /*
116:      * Memory map the language file :
117:      */
118:     load_messages();
119:    
120:     /*
121:      * Report messages :
122:      */
123:     for ( x=1; x<MAX_MSGS; ++x )
124:         printf("errno=%d (%s) : %s
",
125:             x,univ_maclist[x],univ_errlist[x]);
126:
127:     return 0;
128: }

The main() program starts by calling upon load_messages() to select and load the error messages file in line 118. The load_messages() function initially attempts to find environment variable MSG_LANG by calling getenv(3) in line 61. This variable influences the language being used by the error messages and defaults to English (see line 77).

Only the first 12 system error messages are used in this demonstration program, and the limit is established by the macro value MAX_MSGS in line 13. This macro also defines the pointer array length of univ_errlist[] and macro name array univ_maclist[]. Lines 69–72 initialize defaults for the arrays univ_errlist[] and univ_maclist[]. These defaults are used if no memory mapping succeeds.

The pathname of the message file to be mapped is formed in line 77. The size of the file is determined by a call to stat(2) in lines 79–81. If the file cannot be stat(2), then the function simply returns, causing the defaults for the error messages to be used (line 80).

The message file is opened for reading in line 86. The file is mapped by calling mmap(2) in line 92. Since no address was given, the kernel will select a suitable address, which will be assigned to variable msgs. The mapped region was specified to be at least msgs_len+1 bytes in length. The extra byte was requested so that the program can plug in a terminating null byte to simplify the code.

The access to the region will allow both reading and writing. The flag MAP_PRIVATE was used so that any changes made by the program would be kept separate from both the file and other processes mapping this file. The reason for allowing write access will be clear when the function parse_messages() is explained.

Line 100 closes the message file, since it no longer needs to be open. The UNIX kernel maintains its own reference to the file after a successful mmap(2) call, so your application is free to close the file.

Now examine the function parse_messages() in lines 25–52. Line 27 casts the (void *) pointer msgs into the character pointer mp in line 27. This points to the beginning of our mapped file. Line 31 places a null byte at the end of the file to simplify the work in this routine. Note that this extra byte was allowed for in the original mmap(2) call.

Before looking at how the code is parsed, look at a sample message file in English first:

$ cat errors.english
EPERM 1 Operation not permitted
ENOENT 2 No such file or directory
ESRCH 3 No such process
EINTR 4 Interrupted system call
EIO 5 Input/output error
ENXIO 6 Device not configured
E2BIG 7 Argument list too long
ENOEXEC 8 Exec format error
EBADF 9 Bad file descriptor
ECHILD 10 No child processes
EDEADLK 11 Resource deadlock avoided
$

Each line of the message file is divided into a macro name field, an errno value, and the text of the message itself. Only the first 12 codes are covered in this demonstration.

The first time strtok(3) is called, in line 35, the argument mp is not null. This starts the entire parsing process, but note that successive iterations provide a null pointer here (see the for loop in line 33). Lines 35–47 parse the three fields of the input line. Lines 49–50 store pointer references to these messages.

The protection PROT_WRITE was necessary for this application because strtok(3) modifies the memory it is parsing. Recall that it places a null byte at the end of the token found. However, to prevent these changes from being written back to the messages file, the flag MAP_PRIVATE keeps the changes local to the process memory.

An improvement would be to use one application to create a message image file that does not require parsing. Then the second application could simply map the resulting generated memory image into its memory with read-only access. This will be left for you as an exercise.

Compiling the program in Listing 26.2 and invoking it without any language setting causes it to display its defaults:

$ make messages
cc -c  -Wall messages.c
cc -o messages messages.o
$ ./messages
errno=1 (EPERM) : Operation not permitted
errno=2 (ENOENT) : No such file or directory
errno=3 (ESRCH) : No such process
errno=4 (EINTR) : Interrupted system call
errno=5 (EIO) : Input/output error
errno=6 (ENXIO) : Device not configured
errno=7 (E2BIG) : Argument list too long
errno=8 (ENOEXEC) : Exec format error
errno=9 (EBADF) : Bad file descriptor
errno=10 (ECHILD) : No child processes
errno=11 (EDEADLK) : Resource deadlock avoided
$

The default is to assume the English language, so the message file errors.english is used in this example run. If you change the environment variable MSG_LANG to the German language, you get different results:

$ MSG_LANG=german ./messages
errno=1 (EPERM) : Operation nicht die Erlaubnis gehabt
errno=2 (ENOENT) : Keine solche Datei oder Verzeichnis
errno=3 (ESRCH) : Kein solches Prozeß
errno=4 (EINTR) : Unterbrochener Systemaufruf
errno=5 (EIO) : Input/Output Fehler
errno=6 (ENXIO) : Einheit nicht konfiguriert
errno=7 (E2BIG) : Argumentliste zu lang
errno=8 (ENOEXEC) : Formatfehler Exec
errno=9 (EBADF) : Falscher Dateibeschreiber
errno=10 (ECHILD) : Keine Kindprozesse
errno=11 (EDEADLK) : Hilfsmittelsystemblockade vermieden
$

In this example, the input file errors.german was mapped to memory instead. This file has the following content:

$ cat errors.german
EPERM 1 Operation nicht die Erlaubnis gehabt
ENOENT 2 Keine solche Datei oder Verzeichnis
ESRCH 3 Kein solches Prozeß
EINTR 4 Unterbrochener Systemaufruf
EIO 5 Input/Output Fehler
ENXIO 6 Einheit nicht konfiguriert
E2BIG 7 Argumentliste zu lang
ENOEXEC 8 Formatfehler Exec
EBADF 9 Falscher Dateibeschreiber
ECHILD 10 Keine Kindprozesse
EDEADLK 11 Hilfsmittelsystemblockade vermieden

With the help of the following, you could create yet another message file, such as for French: http://babelfish.altavista.com/raging/translate.dyn

Another use for memory-mapped files might be to save your application's workspace. For example, with memory-mapped files, the shared global variables in the globvar utility discussed in Chapter 25 could be saved to a file for future use after a system reboot. Regions for just-in-time executable code can be placed into executable memory regions. This would be performed without actually using a file (recall that the flag MAP_ANON or the mapping of /dev/zero effectively provides this capability).

If memory-mapped regions are used for interprocess communication, keep in mind that synchronization is still required. You may need the assistance of a semaphore set, for example.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.77.208