Mike Frysinger | e8ad463 | 2010-08-17 18:18:35 -0400 | [diff] [blame] | 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. |