C++ || File Copier Using Memory Mapped Files
The following is another homework assignment which was presented in an Operating Systems Concepts class. Using commandline arguments, the following is a program which implements a file copier using memory mapped files. This program makes use of the “mmap” function call provided on Unix based systems.
REQUIRED KNOWLEDGE FOR THIS PROGRAM
How To Use Memory Mapped Files
How To Get The Size Of A File
How To Create A File Of Any Size
==== 1. OVERVIEW ====
A memory mapped file is a segment of virtual memory which has been assigned a direct byte for byte correlation with some portion of a file or file like resource. The primary benefit of memory mapping a file is increasing I/O performance, especially when used on large files. Accessing memory mapped files is faster than using direct read and write operations. Memory mapped files are designed to simplify and optimize file access.
The program demonstrated on this page works with any file type (i.e: txt, cpp, jpg, png, mp4, flv, etc.) and copies the contents of one file, and saves it into another separate output file.
==== 2. TECHNICAL DETAILS ====
This program has the following flow of control:
1. The program is invoked with the source file name and the destination file name as commandline arguments.
2. The program uses the mmap() system call to map both files into memory.
3. The program uses the memory-mapped file memory to to copy the source file to the destination file.
This program is also modified in such a way that you cannot do an mmap on the same file. For example, if you have an input file “A” as the source file, the destination file “B” needs to have a different filename. If both the source and the destination files have the same filename, an error message is displayed, and the program exits. In order for this program to work, the source and the destination files must have different names.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
// ============================================================================ // Author: K Perkins // Date: Oct 5, 2013 // Taken From: http://programmingnotes.org/ // File: Mcp.cpp // Description: This program implements a file copier based on memory // mapped files. // This program has the following flow of control: // (1) The program is invoked with the source file name and the // destination file name as parameters. // (2) The program uses the mmap() system call to map both files into // memory. // (3) The program uses the memory-mapped file memory to copy the // source file to the destination file. // ============================================================================ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/mman.h> using namespace std; int main(int argc, char* argv[]) { // declare variables struct stat statInfo; int srcFd = -1; int destFd = -1; int pagesize = -1; int mappingSize = -1; int leftToWrite = -1; off_t offset = 0; char* srcBuff = NULL; char* destBuff = NULL; const char* srcFileName = NULL; const char* destFileName = NULL; FILE* destFp = NULL; // check if theres enough commandline args if(argc < 3) { cerr<<"n** ERROR NOT ENOUGH ARGS!n" <<"nUSAGE: "<<argv[0]<<" <SOURCE FILE NAME> <DESTINATION FILE NAME>nn"; exit(1); } // set the pointers to the source and destination files srcFileName = argv[1]; destFileName = argv[2]; // check if the source and the destination file have the same name if(strncmp(srcFileName, destFileName, sizeof(srcFileName)) == 0) { cerr<<"n** ERROR - The source and destination files both have the " <<"same name..nnPlease select a name other than "" <<srcFileName<<"" for the destination file!nn"; exit(1); } // get the file information if(stat(srcFileName, &statInfo) < 0) { perror("stat error"); exit(1); } // open the destination file destFp = fopen(destFileName, "w+"); // make sure it opened ok if(!destFp) { perror("open error"); exit(1); } // display info to the screen cerr<<"nThe source file ""<<srcFileName<<"" is " <<statInfo.st_size<<" bytesn"; // set the pointer to the end of the file if(fseek(destFp, statInfo.st_size - 1, SEEK_SET) < 0) { perror("fseek error"); exit(1); } // write a single byte to make the destination file // the same size as the "source" file if(fwrite("", 1, sizeof(""), destFp) < 0) { perror("write error"); exit(1); } // close the destination file fclose(destFp); // open the source file for reading srcFd = open(srcFileName, O_RDONLY); // make sure the file was opened successfully if(srcFd < 0) { perror("open error"); exit(1); } // open the destination file again, this time for actual writing destFd = open(destFileName, O_RDWR); // make sure the destination file was opened successfully if(destFd < 0) { perror("open error"); exit(1); } // get the size of the page pagesize = getpagesize(); // how many bytes left to write leftToWrite = statInfo.st_size; // copy the bytes of the source file to the destination file while(leftToWrite) { // if there is more than pagesize bytes, // then map pagesize bytes. Otherwise, // map the remaining bytes if(leftToWrite > pagesize) { mappingSize = pagesize; } else { mappingSize = leftToWrite; } //cerr<<"mappingSize = "<<mappingSize<<endl; // map the pagesized source file if((srcBuff = (char*)mmap(NULL, mappingSize, PROT_READ, MAP_SHARED, srcFd, offset)) == (void*)-1) { perror("mmap1 error"); exit(1); } // map the pagesized destination file if((destBuff = (char*)mmap(NULL, mappingSize, PROT_READ|PROT_WRITE, MAP_SHARED, destFd, offset)) == (void*)-1) { perror("mmap2 error"); exit(1); } // copy the source to destination memcpy(destBuff, srcBuff, mappingSize); // update the number of bytes left to write leftToWrite -= mappingSize; // update the offset offset += mappingSize; // unmap both buffers if(munmap((void*)srcBuff, mappingSize) < 0) { perror("munmap1 error"); exit(1); } if(munmap((void*)destBuff, mappingSize) < 0) { perror("munmap2 error"); exit(1); } } // display info to the screen cerr<<"nThe destination file "" <<destFileName<<"" has been created and is "<<offset<<" bytesnn"; // close both file descriptors close(srcFd); close(destFd); return 0; }// http://programmingnotes.org/ |
QUICK NOTES:
The highlighted lines are sections of interest to look out for.
The code is heavily commented, so no further insight is necessary. If you have any questions, feel free to leave a comment below.
The following is sample output:
./mcp 1.png 2.png
The source file "1.png" is 42004 bytes
The destination file "2.png" has been created and is 42004 bytes
Leave a Reply