Making Linux Work in a Tiny Flash
There are two areas that you can modify to reduce the size of your Linux distribution: your root filesystem (RFS) and your kernel. This document discusses how to select the various components of a Linux system, and describes ways to reduce its size while retaining needed functionality. Various aspects of configuring your RFS include:
- Selecting a starting shell, utilities, and library
- Selecting a starting RFS, if any
- Removing deadweight from the RFS
- Adding packages back in that are specialized for embedded systems
- Minimizing the size of the kernel
Selecting a Starting Shell, Utilities, and Library
There’s no magic tool! Each system requires thought and experimentation, but the following tools can quickly get you started down the right path.
BusyBox
http://www.busybox.net/ – BusyBox is a multi-call binary providing a reduced-functionality implementation of most GNU command-line tools. BusyBox combines tiny versions of many common UNIX utilities into a single small executable, and is extremely modular, so you can easily include or exclude \commands (or features) at compile time. This is an excellent alternative to the full Bash and core utilities provided with most Linux systems.
Alternative C Libraries
Because glibc is unsuitable for many embedded development projects, several alternative C libraries are commonly used in embedded development projects, described in Alternative C Libraries for Embedded Development. TimeSys currently provides pre-built glibc and uClibc toolchains.
Selecting a Starting RFS
Most space-efficient filesystems currently in use implement compression schemes that result in an even smaller filesystem image. The following comparison lists alternatives to ext3 and jffs2.
Benefits | Limitations | |
cramfsSourceforge: |
Compresses files to save space Metadata not compressed, but stored efficiently Performance hit when reading data, as it must be decompressed |
Files < 16MB Maximum filesystem size 256 MB Does not store timestamps Read-only |
romfsSourceforge: |
Simple and fast Driver does not need overhead of compression library Fast access times |
No last access, UID/GID information stored Read-only No compression |
initramfsDebian package: |
Well supported for 2.6 Low kernel overhead (layer over page cache) Records all metadata Space efficient (allocates only what’s needed) Easy-to-create filesystem |
You need to create device nodes after mounting UID/GID stored only as numbers Sporadic support outside of 2.6 |
squashfsSourceforge: |
Wide architecture support Can generate a filesystem with different endianess than the host system Compresses all data, not just file data Supported across many different kernel versions |
Read-only Slower access time due to decompression overhead |
yaffs2 |
Designed for NAND devices Performs wear-leveling Records all metadata Quick mounting time |
No Compression Not as widely supported as JFFS2 |
Starting with a 2.1 MB root filesystem, TimeSys engineers compared these filesystems. They used the embedded development utilities from the LinuxLink Repository to create a filesystem image, added a kernel, and compared the final size.
As you would expect, there is a tradeoff between size and functionality. You will generally have to optimize for two of the following three desirable qualities: speed, small size, or low memory usage.
The squashfs filesystem produced the smallest RFS size, but has a little more overhead in the kernel. The smallest system was just under 2 MB in size:
- Kernel (BUGboot image): 1,111 KB
- Root Filesystem: 700 KB
- Total: 1,800 KB
The RFS could have been made even smaller by reducing the features in BusyBox and uClibc; BUGboot seems to be a bit larger than other formats.
Strategies for Reducing RFS Size
Reducing the size of an RFS depends both on how small an RFS you need, and what RFS you start with. It is, however, easier and faster if you work up from zero rather than down from the default RFS size. This tactic forces you to think about every byte. The following table includes suggested approaches for different types of small filesystems:
Minimal |
Small |
Tiny |
Minuscule |
|
Ending Size | 6 - 10 MB | 2 - 2.5 MB | 1.5 - 2 MB | <1 MB |
Starting Library | glibc and BusyBox | uClibc and BusyBox | Pared-down uClibc and BusyBox | No BusyBox (or very minimal), no shared libraries |
Approach | Strip glibc libraries and remove locale data Use BusyBox to supply user-land RFS programs |
Use the uClibc as your C library and recompile applications Use BusyBox Manually create device nodes |
Put effort into reducing what’s included in uClibc Write your own system initialization scripts |
init=<your program> |
Notes | This is surprisingly easy. Given the size of flash modules in some of today’s devices, it might be the most appropriate option. | Most people using limited-resource systems find themselves in this category. This process is easier than it sounds, since most users save significant space just by writing a minimal initialization script, which eliminates the need for many support binaries in the RFS. | This approach produces minimal functionality, but if you are only running one application, it is a great solution. |
Remove Unneeded Symbols
Running strip to remove unnecessary debugging and other symbols can greatly reduce the size of a library or an executable.
Eliminate Scripts
In order to run, shell scripts require a shell (which takes up space) and require boot time. To eliminate scripts, create files, directories, and device nodes in advance, if possible. Note that for almost all embedded systems, the devices remain fixed.
Create Device Nodes in Advance
In addition to removing the need for a shell script, creating device nodes ahead of time reduces the time required to boot the device and eliminates the need for other software, such as udev.
Use Static Linking
Even a small shared library has a lot of code that’s never called. Using static linking eliminates the need for a C library, and you save space by removing unused code. Many applications use only a fraction of the C library, and functionality that is not used is wasted space.
Change the Default init to Your Program
The default behavior for a Linux startup is to run the /sbin/init script, which typically calls other scripts and programs to boot the system. Most of this process is designed for flexibility, which is unnecessary and wastes space for most embedded systems. Change the kernel’s initialization script by adding the following to the command line:
init=<your program here>
If your system needs to start a few services, you can do that yourself. This might not be the right solution for more complex systems, but is a big space saver for smaller, single-purpose devices.
Adding to BusyBox Packages
BusyBox provides a great starting point for an embedded system, but there’s still a wealth of other packages for embedded systems. With LinuxLink, you can get many of these packages cross-compiled and ready to use for your processor. Some packages you might find useful are:
- Dropbear – A relatively small SSH2 server and client (available in the LinuxLink Repository and at http://matt.ucc.asn.au/dropbear/dropbear.html)
- Boa – A small, fast, single-tasking HTTP server (available in the LinuxLink Repository and at http://www.acme.com/software/thttpd/)
- thhpd – A simple, small, fast, and secure HTTP server (available in the LinuxLink Repository and at http://www.boa.org/)
- Zebra – GNU Zebra manages TCP/IP-based routing protocols; supports BGP-4, RIPv1, RIPv2, and OSPFv2 (available at http://www.zebra.org/)
- TAO/ACE – An open source implementation of Corba (available in the LinuxLink Repository and at http://deuce.doc.wustl.edu/)
Strategies for Minimizing the Kernel
Once you have a root filesystem, you can remove support for functionality that you won’t need, and use a couple of methods to further reduce kernel size.
Compile Using the -Os Switch
This switch optimizes your code for size. The compiler might generate slower code, but it will be smaller. When you use -Os, the compiler forgoes optimizations (such as loop unrolling) that usually result in faster execution time, but produce more generated code. Like all optimizations, this will reduce the association between generated code and a particular line of source code, thus making debugging more difficult.
Use Linux-Tiny
Matt Mackall’s Linux-Tiny is a series of patches against the 2.6 mainline Linux kernel designed to reduce its memory and disk footprint, as well as to add optional features to help in working on small systems. Linux-Tiny patches have (mostly) been included in 2.6 distributions, so you will probably already have them included. If not, they are recommended.
Don’t Load Modules
If you compile modules directly into the kernel, you can remove support for them, plus you can save space in the RFS because insmod is not required (if that is the method used to load the module).
Consider Removing Other Features
The following features are often included in a kernel, but not generally useful in embedded development:
- Video support
- IDE support
- NFS filesystem support
- Debugging symbols
- DHCP/BOOTP support – if you’re not using your network device frequently (or at all in production)
- sysctl – if you don’t need to dynamically change your kernel configuration
- Ext2/Ext3 support – if you’re not using this filesystem; most configurations include this by default
Other Strategies to Make Development Easier
Use RAM-based Filesystems for Temporary Data
Because most popular space-efficient filesystems are read-only, use RAM disks or tmpfs filesystems to store temporary data. By doing so, you won’t need to reserve parts of flash memory for such data. Usually, RAM is much less expensive and less scarce on a device, so it makes sense to use it instead of flash.
Use chroot to Test the Filesystem
This method allows you to test much faster. You can boot your board over NFS with a known good RFS, and chroot from that point to the filesystem you are testing. While not exact, you can get an idea whether the system is working before going through the time-consuming process of uploading and burning your image onto the board’s flash.
Also refer to the webinar Making Linux Work in a Tiny Flash. For this and other webinars, browse Embedded Development Webinars.