Operating Systems 2019F Lecture 10
Video
Video from the lecture given on October 4, 2019 is now available.
Code
Note: The code below is not the same code that was shown in lecture. Both have been improved with better error handling, and the mmap version now works. The key problems that had to be fixed were:
- If you do a writable mmap, the mmap has to be read/write and the file must be opened read/write.
- You can mmap a region larger than the underlying file; but if you do so, you'll get bus errors when you access the part of memory that has no corresponding part of the file. The solution is to seek out to the length you want minus one and then write one byte - the file will then have the length you want.
3000copy-rw.c
/* 3000copy-rw.c */
/* v0.1 October 4, 2019 */
/* Licenced under the GPLv3, copyright Anil Somayaji */
/* You really shouldn't be incorporating parts of this in any other code,
it is meant for teaching, not production */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define BUFSIZE 4096
void report_error(char *context, char *error)
{
fprintf(stderr, "Error in %s: %s\n", context, error);
exit(-1);
}
int main(int argc, char *argv[])
{
char *source_fn, *dest_fn;
struct stat statbuf;
int source_fd, dest_fd;
char buf[BUFSIZE];
ssize_t len;
int done;
if (argc < 3) {
fprintf(stderr, "Usage: %s <source file> <dest file>\n",
argv[0]);
report_error("command line", "Not enough arguments");
}
source_fn = argv[1];
dest_fn = argv[2];
printf("Copying %s to %s\n", source_fn, dest_fn);
source_fd = open(source_fn, O_RDONLY);
if (source_fd == -1) {
report_error("opening source", strerror(errno));
}
if (stat(dest_fn, &statbuf) == -1) {
printf("%s does not exist, creating\n", dest_fn);
} else {
printf("%s exists, overwriting\n", dest_fn);
}
dest_fd = open(dest_fn, O_RDWR|O_CREAT|O_TRUNC, 0666);
if (dest_fd == -1) {
report_error("opening dest", strerror(errno));
}
printf("Copying bytes...\n");
done = 0;
while (!done) {
len = read(source_fd, buf, BUFSIZE);
if (len == -1) {
report_error("reading source", strerror(errno));
}
if (len > 0) {
if (write(dest_fd, buf, len) == -1) {
report_error("writing dest", strerror(errno));
}
} else {
done = 1;
}
}
if (close(source_fd) == -1) {
report_error("closing source", strerror(errno));
}
if (close(dest_fd) == -1) {
report_error("closing dest", strerror(errno));
}
printf("Done!\n");
return 0;
}
3000copy-mmap.c
/* 3000copy-mmap.c */
/* v0.1 October 4, 2019 */
/* Licenced under the GPLv3, copyright Anil Somayaji */
/* You really shouldn't be incorporating parts of this in any other code,
it is meant for teaching, not production */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#define BUFSIZE 4096
void report_error(char *context, char *error)
{
fprintf(stderr, "Error in %s: %s\n", context, error);
exit(-1);
}
int main(int argc, char *argv[])
{
char *source_fn, *dest_fn;
struct stat statbuf;
int source_fd, dest_fd;
ssize_t len, i;
char *source_map, *dest_map;
if (argc < 3) {
fprintf(stderr, "Usage: %s <source file> <dest file>\n",
argv[0]);
report_error("command line", "Not enough arguments");
}
source_fn = argv[1];
dest_fn = argv[2];
printf("Copying %s to %s\n", source_fn, dest_fn);
source_fd = open(source_fn, O_RDONLY);
if (source_fd == -1) {
report_error("opening source", strerror(errno));
}
if (fstat(source_fd, &statbuf)) {
report_error("stat", strerror(errno));
}
len = statbuf.st_size;
source_map = (char *) mmap(NULL, len,
PROT_READ, MAP_SHARED, source_fd, 0);
if (source_map == MAP_FAILED) {
report_error("source map", strerror(errno));
}
if (stat(dest_fn, &statbuf) == -1) {
printf("%s does not exist, creating\n", dest_fn);
} else {
printf("%s exists, overwriting\n", dest_fn);
}
dest_fd = open(dest_fn, O_RDWR|O_CREAT|O_TRUNC, 0666);
if (dest_fd == -1) {
report_error("opening dest", strerror(errno));
}
if (lseek(dest_fd, len-1, SEEK_SET) == -1) {
report_error("lseek", strerror(errno));
}
if (write(dest_fd, "", 1) == -1) {
report_error("write after lseek", strerror(errno));
}
dest_map = (char *) mmap(NULL, len,
PROT_READ|PROT_WRITE,
MAP_SHARED, dest_fd, 0);
if (dest_map == MAP_FAILED) {
report_error("dest map", strerror(errno));
}
printf("Copying bytes...\n");
for (i = 0; i < len; i++) {
dest_map[i] = source_map[i];
}
if (munmap(source_map, len) == -1) {
report_error("source munmap", strerror(errno));
}
if (munmap(dest_map, len) == -1) {
report_error("dest munmap", strerror(errno));
}
if (close(source_fd) == -1) {
report_error("closing source", strerror(errno));
}
if (close(dest_fd) == -1) {
report_error("closing dest", strerror(errno));
}
printf("Done!\n");
return 0;
}