Using a File as Shared Memory

Most versions of Linux-UNIX also support the mmap system call, which can be used to map a file to a process's virtual memory address space. In many ways mmap is more flexible than its shared memory system call counterpart . Once a mapping has been established, standard system calls rather than specialized system calls can be used to manipulate the shared memory object (Table 8.11). Unlike memory, the contents of a file are nonvolatile and will remain available even after a system has been shut down (and rebooted).

Table 8.11. Summary of the mmap System Call.

Include File(s)

 

Manual Section

2

Summary

#ifdef _POSIX_MAPPED_FILES <-- 1 void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); #endif

(1) If _POSIX_MAPPED_FILES has been defined.

Return

Success

Failure

Sets errno

A pointer to the mapped area

MAP_FAILED ((void *) -1)

Yes

The mmap system call requires six arguments. The first, start , is the address for attachment. As with the shmat system call, this argument is most often set to 0, which directs the system to choose a valid attachment address. The number of bytes to be attached is indicated by the second argument, length . While the call will allow the user to specify a number of bytes for length that will extend beyond the end of the mapped file, an actual reference to these locations will generate an error (a SIGBUS signal). The third argument, prot , is used to set the type of access (protection) for the segment. The specified access should not be in conflict with the access permissions for the associated file descriptor. The prot argument uses the defined constants found in the include file . These constants are shown in Table 8.12.

Table 8.12. Defined Protection Constants.

Defined Constant

Access

PROT_READ

Read access to specified region.

PROT_WRITE

Write access to specified region.

PROT_EXEC

Execute access to specified region.

PROT_NONE

No access.

Constants can be OR ed to provide different combinations of access. The manual page for mmap notes that on some systems PROT_WRITE is implemented as PROT_READ PROT_WRITE, and PROT_EXEC as PROT_READ PROT_EXEC. In any case, PROT_WRITE must be set if the process is to write to the mapped segment. The fourth argument, flags , specifies the type of mapping. Mapping types are also indicated using defined constants from the include file. These constants are shown in Table 8.13.

Table 8.13. Defined Mapping Type Constants.

Defined Constant

Mapping Type

MAP_SHARED

Share all changes.

MAP_PRIVATE

Do not share changes.

MAP_FIXED

Interpret the value for the start argument exactly.

The first two constants specify whether write s to the shared memory will be shared with other processes or be private. MAP_SHARED and MAP_PRIVATE are exclusionary. When specifying MAP_PRIVATE, a private copy is not generated until the first write to the mapped object has occurred. These specifications are retained across a fork system call but not across a call to exec . MAP_FIXED directs the system to explicitly use the address value in start . When MAP_FIXED is indicated, the values for start and length should be a multiple of the system's page size . Specifying MAP_FIXED greatly reduces the portability of a program, and its use is discouraged. When specifying the flags argument, either MAP_SHARED or MAP_PRIVATE must be indicated. Linux also supports the flags shown in Table 8.14.

Table 8.14. Linux-Specific Defined Mapping Type Constants.

Defined Constant

Mapping Type

MAP_GROWSDOWN

Treat the segment as a stack.

MAP_EXECUTABLE

Mark the segment as executable.

MAP_DENYWRITE

Do not allow writing.

MAP_NORESERVE

Do not check for reservations .

MAP_LOCKED

Lock the mapped segment.

The fifth argument, fd , is a valid open file descriptor. Once the mapping is established, the file can be closed. The sixth argument, offset , is used to set the starting position for the mapping.

If the mmap system call is successful, it returns a reference to the mapped memory object. If the call fails, it returns the defined constant MAP_FAILED (which is actually the value -1 cast to a void * ). A failed call will set the value in errno to reflect the error encountered . The errors for mmap are shown in Table 8.15.

Table 8.15. mmap Error Messages.

#

Constant

perror Message

Explanation

6

ENXIO

No such device or address

The values for off or off + len are illegal for the specified device.

9

EBADF

Bad file descriptor

The file referenced by fd is invalid.

11

EAGAIN

Resource temporarily unavailable

  • Insufficient swap space for the mapping.
  • Mapping could not be locked in memory.
  • Mapped file is already locked.

12

ENOMEM

Cannot allocate memory

Insufficient address space to implement the mapping.

13

EACCES

Permission denied

  • MAP_PRIVATE indicated and file descriptor is not open for reading.
  • File descriptor is not open for writing, and PROT_WRITE was indicated with a mapping type of MAP_SHARED.

19

ENODEV

No such device

fd references an invalid device (such as a terminal).

22

EINVAL

Invalid argument

  • MAP_FIXED specified, and value for start or offset are not multiples of the system's page size.
  • Illegal flag value.
  • Argument length is less than 1.

26

ETXTBSY

Text file busy

MAP_DENYWRITE was set but fd is open for writing.

While the system will automatically unmap a region when a process terminates, the system call munmap , shown in Table 8.16, can be used to explicitly unmap pages of memory.

Table 8.16. Summary of the munmap System Call.

Include File(s)

 

Manual Section

2

Summary

#ifdef _POSIX_MAPPED_FILES int munmap(void *start, size_t length); #endif

Return

Success

Failure

Sets errno

-1

Yes

The munmap system call is passed the starting address of the memory mapping (argument start ) and the size of the mapping (argument length ). If the call is successful, it returns a value of 0. Future references to unmapped addresses generate a SIGVEGV signal. If the munmap system call fails, it returns the value -1 and sets the value in errno to EINVAL. The interpretation of munmap - related error is given in Table 8.17.

Table 8.17. munmap Error Messages.

#

Constant

perror Message

Explanation

22

EINVAL

Invalid argument

  • Argument length is less than 1.
  • Argument start is not a multiple of the system page size.
  • Argument start or start + length is outside the process's address space.

The msync system call is used in conjunction with mmap to synchronize the contents of mapped memory with physical storage (Table 8.18). A call to msync will cause the system to write all modified memory locations to their as sociated physical storage locations. If MAP_SHARED is specified with mmap , the storage location is a file. If MAP_PRIVATE is specified, then the storage location is the swap area.

Table 8.18. Summary of the msync Library Function.

Include File(s)

 

Manual Section

2

Summary

#ifdef _POSIX_MAPPED_FILES #ifdef _POSIX_SYNCHRONIZED_IO int msync(const void *start, size_t length, int flags); #endif #endif

Return

Success

Failure

Sets errno

-1

Yes

The start argument for msync specifies the address of the mapped memory; the length argument specifies the size (in bytes) of the memory. The flags argument directs the system to take the actions shown in Table 8.19.

Table 8.19. Defined Flag Constants for msync .

Defined Constant

Action

MS_ASYNC

Return immediately once all writes have been scheduled.

MS_SYNC

Return once all writes have been performed.

MS_INVALIDATE

Invalidate cached copies of memorysystem reloads memory from the associated storage location.

If msync fails, it returns a -1 and sets errno (Table 8.20). If the call is successful, it returns a value of 0.

Table 8.20. mmap Error Messages.

#

Constant

perror Message

Explanation

1

EPERM

Operation not permitted

MS_INVALIDATE indicated but some of the referenced locations are locked in memory.

14

EFAULT

Bad address

Invalid address reference.

16

EBUSY

Device or resource busy

MS_SYNC and MS_INVALIDATE specified but some of the referenced addresses are currently locked.

22

EINVAL

Invalid argument

  • Argument addr is not a multiple of the page size.
  • Argument flags not a combination of MS_ASYNC MS_INVALIDATE MS_SYNC.

Program 8.6 demonstrates the use of the mmap system call.

Program 8.6 Using mmap .

File : p8.6.cxx /* Using the mmap system call */ #define _GNU_SOURCE + #include #include #include #include #include 10 #include #include #include #include #include + using namespace std; int main(int argc, char *argv[]){ int fd, changes, i, random_spot, kids[2]; struct stat buf; 20 char *the_file, *starting_string="ABCDEFGHIJKLMNOPQRSTUVWXYZ"; if (argc != 3) { cerr << "Usage " << *argv << " file_name #_of_changes" << endl; return 1; } + if ((changes = atoi(argv[2])) < 1) { cerr << "# of changes < 1" << endl; return 2; } if ((fd = open(argv[1], O_CREAT O_RDWR, 0666)) < 0) { 30 perror("file open"); return 3; } write(fd, starting_string, strlen(starting_string)); // Obtain size of file + if (fstat(fd, &buf) < 0) { perror("fstat error"); return 4; } // Establish the mapping 40 if ((the_file = (char *) mmap(0, (size_t) buf.st_size, PROT_READ PROT_WRITE, MAP_SHARED, fd, 0)) == (void *) - 1) { perror("mmap failure"); exit(5); + } for (i = 0; i < 2; ++i) if ((kids[i] = (int) fork()) == 0) while (1) { cout << "Child " << getpid() << " finds: " << the_file << endl; 50 sleep(1); } srand((unsigned) getpid()); for (i = 0; i < changes; ++i) { random_spot = (int) (rand() % buf.st_size); + *(the_file + random_spot) = '*'; sleep(1); } cout << "In parent, done with changes" << endl; for (i = 0; i < 2; ++i) 60 kill(kids[i], 9); cout << "The file now contains: " << the_file << endl; return 0; }

Program 8.6 uses a parent/two-child process arrangement to demonstrate the use of mmap . The parent process modifies the contents of a memory-mapped file. Each child process repetitively displays the contents of the mapped files to allow verification of the changes. The program is passed two command-line arguments. The first argument is the name of a file that it will use for mapping. The second argument indicates the number of modifications that should be made to the file. Upon execution of the program, the validity of the command-line arguments is checked. If problems are encountered, an appropriate error message is generated and the program exits. If the command-line arguments are good, the program opens, for reading and writing, the file whose name was passed as the first command-line argument. As the O_CREAT flag is specified, if the file does not exist, it will be created. Next, the string "ABCDEFGHIJKLMNOPQRSTUVWXYZ" is written to the first part of the file. Following this, the fstat call is used to determine the size of the file.

In our example, if we start with an empty file, the size of the file is actually the length of the string that is written to the file. However, this would not be true if the file contained previous data. In many cases we will want to know the full size of the file to be mapped fstat provides us with a handy way of determining the file's size (it is returned as part of the stat structure). The call to mmap (line 40) establishes the actual mapping. We allow the system to pick the address and indicate that we want to be able to read from and write to the mapped memory region. We also specify the region be marked as shared, be associated with the open file descriptor fd , and have an offset (starting position within the file) of 0. Two child processes are then generated. Each child process displays the contents of the memory-mapped file using the the_file reference which was returned from the initial call to mmap . It is important to note that a call to read was not needed. The child process then sleep s one second and repeats the same sequence of activities until a terminating signal is received. The parent process loops for the number of times specified by the second command-line argument. Within this loop the parent process randomly picks a memory-mapped location and changes it to an asterisk ( * ). Again, this is done by direct reference to the location using the the_file reference; notice no write function is used. Between changes, the parent sleeps one second to slow down the processing sequence. Once the parent process is done, it displays the final contents of the memory-mapped file, removes the child processes, and exits. A sample run of the program is shown in Figure 8.10.

Figure 8.10 A sample run of Program 8.6.

linux$ p8.6 demo 7 Child 16592 finds: ABCDEFGHIJKLMNOPQRSTUVWXYZ Child 16593 finds: ABCDEFGHIJKLMNOPQRSTUVWXYZ Child 16592 finds: ABCDEFG*IJKLMNOPQRSTUVWXYZ Child 16593 finds: ABCDEFG*IJKLMNOPQRSTUVWX*Z Child 16592 finds: ABCDEFG*IJKLMNOPQRSTUVWX*Z Child 16593 finds: ABCDEF**IJKLMNOPQRSTUVWX*Z Child 16592 finds: ABCDEF**IJKLMNOPQRSTUVWX*Z Child 16593 finds: ABCDEF**IJ*LMNOPQRSTUVWX*Z Child 16592 finds: ABCDEF**IJ*LMNOPQRSTUVWX*Z Child 16593 finds: ABCDEF**I**LMNOPQRSTUVWX*Z Child 16592 finds: ABCDEF**I**LMNOPQRSTUVWX*Z Child 16593 finds: ABCDEF**I**LMNOPQRS*UVWX*Z Child 16592 finds: ABCDEF**I**LMNOPQRS*UVWX*Z Child 16593 finds: ABCDEF**I**L*NOPQRS*UVWX*Z Child 16592 finds: ABCDEF**I**L*NOPQRS*UVWX*Z In parent, done with changes The file now contains: ABCDEF**I**L*NOPQRS*UVWX*Z

In this invocation the child processes, PIDs 16592 and 16593, initially find the mapped location to contain the unmodified starting string. A second check of the mapped location shows that each child now sees the string with a single ' * ' replacing the letter H . Additional passes reveal further modifications. When all of the processes have terminated , we will find that the file demo will contain the fully modified string.

EXERCISE

If we replace MAP_SHARED with MAP_PRIVATE, will the output from Program 8.6 remain the same? Why, why not? Will the file test contain the * modified string or the original string? Why, why not? Why is the MAP_SHARED specification retained across a fork but not an exec system call?

EXERCISE

What if the file that we map resides on a shared file system? Can we then have unrelated processes residing on different workstations use this file as a means of communication? Support your answer with a program example.

Категории