We have now discussed the basic configuration, compiling, installation, and usage of BusyBox on virtual Android devices and demonstrated how to build a bootable embedded system with BusyBox from scratch and enhanced the system through integrating additional utilities, including lightweight applets, standalone tools, and even programming language environments. In short, the functionality implementation of a scalable embedded system with BusyBox has been discussed in depth in previous recipes.
This recipe onwards, we will learn how to enhance user experience—it's time to talk about system optimization. Size optimization will be discussed in this recipe.
For most embedded systems (particularly consumer electronics like smartphone systems), due to the limitation of product cost, size of the hardware model, and rich-function requirements, system image size should be limited to fit the size of the disk and memory and reserve more free storage for end users.
The software of an embedded Linux system mainly includes bootloader, kernel, ramdisk, system, and applications. Use the embedded filesystem we just built with BusyBox in the Building BusyBox-based embedded system (Intermediate) recipe as an example. To reduce its size, we should:
Demand determine features:
Remove the functions that are not required by the target system by disabling those features, via the configuration utility.
For example, use
defconfigas a configuration base and enable the necessary features and disable the rest.
Share common features in libraries:
When adding new features to BusyBox, try to use the library functionality already present in BusyBox and remove the code from the utility that duplicates that functionality.
For example, for the
sstripprogram we just integrated, if some of its functions already exist in the common libraries of BusyBox, they should be removed.
Static linking versus dynamic linking:
If possible, link BusyBox with a lightweight standard library such as uClibc (http://www.uclibc.org/; it is optimized for an embedded system and is smaller than Glibc), not Glibc.
If extra tools are required and support dynamic linking to share the same library, don't link BusyBox statically. But if you only need BusyBox, link statically to avoid the need of a dynamic linker and loader.
Utilize compiler and linker support:
-Osoption of GCC by disabling the
CONFIG_DEBUG_PESSIMIZEconfiguration option of BusyBox and enable
--gc-sectionsusing not Glibc (see the comments in
scripts/trylink), and with a new enough
Strip everything that is not required at runtime:
stripto discard symbols from object files, and use
sstripto remove the section header table at the end of the executable.
It should be noted that an executable file that has no section header table cannot be used with gdb or objdump for debugging. You can learn more from the comments of
Compression is a technique with external perspective:
Instead of optimizing the size of the filesystem, compressing the whole filesystem image with a suitable algorithm is a technique with external perspective. The algorithms include a general
lzmaformat with higher compression rate, and LZO or LZ4 with lower compression rate but faster decompression speed.
For example, the embedded filesystem we created in the Building BusyBox-based embedded system (Intermediate) recipe is packaged in cpio format and finally compressed with gzip.
To use such algorithms, corresponding decompression support is required in bootloader, or else the Linux kernel for the filesystem will be unpacked into memory at boot time.
Measure before starting optimization:
Find the objects consuming large storage by measuring the size of each object or executable file and then optimize them one by one. The tools include
For example, the
sizeutility can be used to output the size of different segments of an executable as follows:
$ size busybox text data bss dec hex filename 824447 4154 9520 838121 cc9e9 busybox
The main storage consumers of an embedded Linux system are executables. A standard Linux executable is in an ELF format. Besides an ELF header, a program header table, and a section header table, it consists of the text segment, data segment (initialized), and BSS segment (uninitialized).
To optimize size means to remove unnecessary executables or to reduce the size of the previously mentioned segments of the executables by sharing common functions or data, or even via sharing a single ELF header.
BusyBox is highly modular and configurable, and therefore allows to choose only the necessary applets based on the real system requirements. And as a single binary, all of the built-in applets share one ELF header and share all of the common functions. Besides those, compiler size optimization options like
--gc-sections are also well supported.
Also, the last binary can be stripped with
sstrip, and the generated filesystem image can be compressed.
We have discussed size optimization of ramdisk built with BusyBox. For the other parts of an embedded Linux system and the other methods, http://www.elinux.org/System_Size is a very good reference.
For the kernel parts, we can get more useful information from a project proposed by one of the authors of this book, at http://elinux.org/Work_on_Tiny_Linux_Kernel.