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