Home | History | Annotate | Download | only in doc
      1 -------------
      2 --- Intro ---
      3 -------------
      4 
      5 Linux running on processors without a memory management unit place certain
      6 restrictions on the userspace programs.  Here we will provide some guidelines
      7 for people who are not familiar with such systems.
      8 
      9 If you are not familiar with virtual memory, you might want to review some
     10 background such as:
     11 	http://en.wikipedia.org/wiki/Virtual_Memory
     12 	/usr/src/linux/Documentation/nommu-mmap.txt
     13 
     14 ----------------------------
     15 --- No memory protection ---
     16 ----------------------------
     17 
     18 By virtue of every process getting its own virtual memory space, applications
     19 are protected from each other.  So a bad memory access in one will not affect
     20 the memory of another.  When processors forgo virtual memory, they typically
     21 do not add memory protection back in to the hardware.  There are one or two
     22 exceptions to this rule, but for now, we'll assume no one supports it.
     23 
     24 In practical terms, this means you cannot dereference bad pointers directly
     25 and expect the kernel to catch and kill your application.  However, you can
     26 expect the kernel to catch some bad pointers when given to system calls.
     27 
     28 For example, this will "work" in the sense that no signal will be sent:
     29 	char *foo = NULL;
     30 	foo[0] = 'a';
     31 	foo[1] = 'b';
     32 
     33 However, the kernel should return errors when using "standard" bad pointers
     34 with system calls.  Such as:
     35 	char *foo = NULL;
     36 	write(1, foo, 10);
     37 	-> kernel will return EFAULT or similar
     38 The other bad pointer you can rely on in your tests is -1:
     39 	char *foo = (void *)-1;
     40 	read(0, foo, 10);
     41 	-> kernel will return EFAULT or similar
     42 
     43 Otherwise, no bad pointer may reliably be tested, either directly or
     44 indirectly via the kernel.  This tends to be a large part of the UCLINUX
     45 ifdef code that shows up in LTP.
     46 
     47 ----------------
     48 --- No forks ---
     49 ----------------
     50 
     51 The ubiquitous fork() function relies completely on the Copy On Write (COW)
     52 functionality provided by virtual memory to share pages between processes.
     53 Since this isn't feasible without virtual memory, there is no fork() function.
     54 You will either get a linker error (undefined reference to fork) or you will
     55 get a runtime failure of ENOSYS.
     56 
     57 Typically, fork() is used for very few programming paradigms:
     58 	- daemonization
     59 	- run a program
     60 	- parallelism
     61 
     62 For the daemonization functionality, simply use the daemon() function.  This
     63 works under both MMU and NOMMU systems.
     64 
     65 To run a program, simply use vfork() followed by an exec-style function.
     66 And change the error handler in the child from exit() to _exit().  This too
     67 works under both MMU and NOMMU systems.  But be aware of vfork() semantics --
     68 since the parent and child share the same memory process, the child has to be
     69 careful in what it does.  This is why the recommended construct is simply:
     70 	pid_t child = vfork();
     71 	if (vfork == 0)
     72 		_exit(execl(....));
     73 
     74 For parallelism where processes use IPC to work together, you have to options,
     75 neither of which are easy.  You can rewrite to use threads, or you can re-exec
     76 yourself with special flags to pass along updated runtime state.  This is what
     77 the self_exec() helper function in LTP is designed for.
     78 
     79 -------------------------
     80 --- No overcommitting ---
     81 -------------------------
     82 
     83 Virtual memory allows people to do malloc(128MiB) and get back a buffer that
     84 big.  But that buffer is only of virtual memory, not physical.  On a NOMMU
     85 system, the memory comes immediately from physical memory and takes it away
     86 from anyone else.
     87 
     88 Avoid large mallocs.
     89 
     90 ---------------------
     91 --- Fragmentation ---
     92 ---------------------
     93 
     94 On a MMU system, when physical memory gets fragmented, things slow down.  But
     95 they keep working.  This is because every new process gets a clean virtual
     96 memory address space.  While processes can fragment their own virtual address
     97 space, this usually takes quite a long time and a lot of effort, so generally
     98 it is not a problem people hit.
     99 
    100 On a NOMMU system, when physical memory gets fragmented, access to large
    101 contiguous blocks becomes unavailable which means requests fail.  Even if your
    102 system has 40MiB _total_ free, the largest contiguous block might only be 1MiB
    103 which means that allocations larger than that will always fail.
    104 
    105 Break up your large memory allocations when possible.  Generally speaking,
    106 single allocations under 2MiB aren't a problem.
    107 
    108 -----------------
    109 --- No paging ---
    110 -----------------
    111 
    112 No virtual memory means you can't mmap() a file and only have the pages read in
    113 (paged) on the fly.  So if you use mmap() on a file, the kernel must allocate
    114 memory for it and read in all the contents immediately.
    115 
    116 ---------------------
    117 --- No swap space ---
    118 ---------------------
    119 
    120 See the "No paging" section above.  For the same reason, there is no support
    121 for swap partitions.  Plus, nommu typically means embedded which means flash
    122 based storage which means limited storage space and limited number of times
    123 you can write it.
    124 
    125 -------------------------
    126 --- No dynamic stacks ---
    127 -------------------------
    128 
    129 No virtual memory means that applications can't all have their stacks at the
    130 top of memory and allowed to grown "indefinitely" downwards.  Stack space is
    131 fixed at process creation time (when it is first executed) and cannot grow.
    132 While the fixed size may be increased, it's best to avoid stack pressure in
    133 the first place.
    134 
    135 Avoid the alloca() function and use malloc()/free() instead.
    136 
    137 Avoid declaring large buffers on the stack.  Some people like to do things
    138 such as:
    139 	char buf[PATH_MAX];
    140 This will most likely smash the stack on nommu systems !  Use global variables
    141 (the bss), or use malloc()/free() type functions.
    142 
    143 -------------------------------
    144 --- No dynamic data segment ---
    145 -------------------------------
    146 
    147 No virtual memory means that mappings cannot arbitrarily be extended.  Another
    148 process might have its own mapping right after yours!  This is where the brk()
    149 and sbrk() functions come into play.  These are most often used to dynamically
    150 increase the heap space via the C library, but a few people use these manually.
    151 
    152 Best if you simply avoid them, and if you're writing tests to exercise these
    153 functions specifically, make them nops/XFAIL for nommu systems.
    154 
    155 -------------------------------
    156 --- Limited shared mappings ---
    157 -------------------------------
    158 
    159 No virtual memory means files cannot be mmapped in and have writes to it
    160 written back out to disk on the fly.  So you cannot use MAP_SHARED when
    161 mmapping a file.
    162 
    163 -------------------------
    164 --- No fixed mappings ---
    165 -------------------------
    166 
    167 The MAP_FIXED option to mmap() is not supported.  It doesn't even really work
    168 all that well under MMU systems.
    169 
    170 Best if you simply avoid it, and if you're writing tests to exercise this
    171 option specifically, make them nops/XFAIL for nommu systems.
    172