diff --git a/03-mmap/Makefile b/03-mmap/Makefile
index 3285da1..43879b0 100644
--- a/03-mmap/Makefile
+++ b/03-mmap/Makefile
@@ -1,8 +1,8 @@
mmap: mmap.c
- gcc mmap.c -o mmap -static
+ gcc mmap.c -o mmap -static -O3
run: ./mmap
./mmap
strace: ./mmap
- strace -ff ./mmap thread
+ strace -ff ./mmap
diff --git a/03-mmap/mmap.c b/03-mmap/mmap.c
index 99d6b06..63d57ba 100644
--- a/03-mmap/mmap.c
+++ b/03-mmap/mmap.c
@@ -7,61 +7,117 @@
#include <sys/mman.h>
#include <stdlib.h>
-// We define two CPP macros to hide the ugliness of these compiler
-// attributes.
-//
-// - section: This variable attribute pushes the variable into the
-// named ELF section. Thereby, we let the linker collect
-// variable definitions and concentrate them in one place
+// We define a CPP macro to hide the ugliness of compiler attributes.
//
// - aligned: Normally, variables can be densely packed in the
// data/BSS section. However, as we want to replace parts
-// of our data section with a file mapping, we obey the
-// MMU's granularity (4096 bytes).
-#define persistent __attribute__((section("persistent")))
+// of our data section with a file mapping, we have to obey
+// the MMU's granularity (4096 bytes).
#define PAGE_SIZE 4096
#define page_aligned __attribute__((aligned(PAGE_SIZE)))
-// This is a special variable that we use in setup_persistent to
-// locate the start of the persistent section. We page align it, such
-// that its address is placed at the beginning of a page. This is
-// equivalent to an HEX address ending in 3 zeros.
-//
-// For example: &persistent_start = 0x4b8000
-page_aligned persistent int persistent_start;
+// This is anonymous struct describes our persistent data and a
+// variable with that type, named psec, is defined.
+struct /* anonymous */ {
+ // We mark the first variable in our struct as being page aligned.
+ // This is equivalent to a start address an address ending in 3
+ // hexadecimal zeros.
+ //
+ // By aligning the first variable in the struct, two things happen:
+ //
+ // 1. Global variables of struct persistent_section are placed at
+ // the beginning of a page.
+ // 2. The size of the section is padded to the next multiple of
+ // PAGE_SIZE, such that array-address calculations and pointer
+ // arithmetik work correctly.
+ page_aligned int persistent_start;
-// This is our actual persistent variable. We will use it as a
-// counter, how often the program was executed.
-persistent int foobar = 23;
+ // This is our persistent variable. We will use it as a counter,
+ // how often the program was executed.
+ int foobar;
+} psec;
-// Although this variable is placed between persistent_{start,end}, it
-// does *NOT* have the persistent variable, whereby it is place not
-// within the persistent section.,
+// For comparision, we also create another global variable, that is
+// initialized from the program ELF on every program start.
int barfoo = 42;
-// A second special variable that marks the end of the persistent
-// region. We also make it page aligned, whereby the linker will not
-// place other
-page_aligned persistent int persistent_end;
-
int setup_persistent(char *fn) {
- // FIXME: Install persistent mapping and return 0;
- return -1;
+ // We first stat the file, before we open it with the O_CREAT
+ // flag, whereby we can determine lateron, if the file has
+ // existsted beforehand.
+ struct stat s;
+ int rc = stat(fn, &s);
+ int fd = open(fn, O_RDWR|O_CREAT, 0600);
+
+ // We could not open/create the file. This is not good and we
+ // cannot and do not want to recover from it.
+ if (fd < 0)
+ return -1;
+
+ // If stat failed, the file did not exist beforehand, and we want
+ // to initialize our backing storage. We also initialize the file,
+ // if its size is different from out persistent size.
+ if (rc == -1 || s.st_size != sizeof(psec)) {
+ // First, we shrink/expand the file to the correct size. We
+ // use ftruncate(2) for this
+ if (ftruncate(fd, sizeof(psec)) != 0)
+ return -1;
+
+ // We copy the contents of the initialized persistent section
+ // from the DRAM to the file, by writing it into the file descriptor
+ if (write(fd, &psec, sizeof(psec)) != sizeof(psec))
+ return -1;
+
+ // We have initialized the file.
+ }
+
+ // We replace the memory at &psec with the contents of our file.
+ // But unlike read(), resulting mapping is synchronized with the
+ // file contents.
+ //
+ // addr: Where do we want our mapping to start. Usually this is
+ // only a hint.
+ // length: We want to map that many bytes starting at addr
+ // prot: Yes, we want that memory to be read and writable
+ // flags/MAP_SHARED: The mapping should not be a private copy, but
+ // our modifications should be shared with everybody else.
+ // (this includes persistence)
+ // flags/MAP_FIXED: The addr is not only a hint, but mmap should fail
+ // in case the OS cannot install a mapping there.
+ // file: Where should the content for the mapping come from?
+ // offset: offset within the file descriptor
+ int *map = mmap(/* addr */ &psec,
+ /* length*/ sizeof(psec),
+ /* prot */ PROT_READ|PROT_WRITE,
+ /* flags */ MAP_SHARED|MAP_FIXED,
+ /* file */ fd,
+ /* offset */ 0);
+ // mmap returns a special pointer (MAP_FAILED) to inidicate that
+ // the operation did not succeed.
+ if (map == MAP_FAILED)
+ return -1;
+
+ // This is interesting: We can close the file descriptor and our
+ // mapping remains in place. Internally, the struct file is not
+ // yet destroyed as the mapping keeps a reference to it.
+ close(fd);
+
+ return 0;
}
int main(int argc, char *argv[]) {
- printf("section persistent: %p--%p\n", &persistent_start, &persistent_end);
+ printf("psec: %p--%p\n", &psec, &psec + 1);
// Install the persistent mapping
if (setup_persistent("mmap.persistent") == -1) {
perror("setup_persistent");
return -1;
}
- // For foobar, we see that each invokation of the programm will
+ // For psec.foobar, we see that each invokation of the programm will
// yield an incremented result.
// For barfoo, which is *NOT* in the persistent section, we will
// always get the same result.
- printf("foobar(%p) = %d\n", &foobar, foobar++);
+ printf("foobar(%p) = %d\n", &psec.foobar, psec.foobar++);
printf("barfoo(%p) = %d\n", &barfoo, barfoo++);
{// This is ugly and you should not do this in production code.