Customize Linux from the Bottom—Building Your Own Linux Base System
As far as we know, it is hard to find a document that tell us in detail how to put images, executables, binaries and scripts together; in other words, to package things together, to assemble a system. Different systems may take different approaches to packaging, although components can be created in the same way. The easiest and most popular way is packaging on floppies. The general steps of packaging a bootable system on a single floppy, that is a boot/root floppy, can be summarized in the following few steps:
1. Create individual items needed for the system, such as the kernel image, libraries, executables, scripts and configuration etc.2. Create the directory structure of the initial root file system for the base system.3. Move things to the root file system and create items like device nodes.4. Create the compressed root file system.5. Tell the kernel where to find the initial root file system image by setting some flags in the kernel image.6. Write the kernel and the compressed root image to a floppy and make it bootable. To show more details, we wrote a set of makefiles. They are actually instructions to implement the above steps and to create a small base system from freely available packages. We run a simple application called a netperf sever which tests TCP/IP stack performance and is also freely available from the Web. We provide these instructions in Resources. It may not be simple to run, but curious readers can dig into the lines and find how to build a base system from scratch.
An application can be started in different ways depending on how the base system is configured. In most of cases, a Linux kernel is configured to run a startup script or a binary executable, called init or linuxrc, in the initial root file system after the kernel is up. This init program usually does things like remount the root file system to allow read/write permissions, mount other file systems like proc, and initialize other parts of the system, such as starting a shell interface or running the application immediately. The SysVInit program is very popular in most Linux distributions for this purpose.
For our base system, we don't need a complex init sequence to demonstrate. So, we simply write a shell script like the following. Anyone is free to change and add more commands to it:
mount -n -o remount,rw / mount /proc /proc -t proc echo MyCompanyName, Version X.Y. Built Z, August 2000 exec /bin/sh
As an exercise, it might look better if you have the above echo line in your application, and start the application at the end of the script instead of running the standard shell. An example in C++:
cout << COMPANY << VERSION_NO << BUILD_NO << __DATE__ << __TIME__;In our case, the system prompts after it is up.
pipe-elinux> MyCompanyName, Version X.Y, Build Z, August 2000 pipe-elinux>
After the base system is up, you might think it is not much use without any interesting applications. But it is a base from which you could start your big project. One by one, you can gradually add things into this base system, making it more and more attractive. The following examples might be worth considering:
a init program : SysVInit is a good choice, but it seems too big for simple applications
a security facility: add login support
an editor : vi or emacs
more networking services: telnet or ftp dæmons
a GUI : X is a choice
non-volatile storage: flash memory and hard disk support.
add more loadable modules
use rpm to manage packages
We don't really need to recompile every package we choose, because we can easily find a binary already compiled for a processor. Like our system, the host and the target machines are the same type, so, we can just use most of the binaries found on the host machine. For example, for the utility top, we just copy the binary into our base system; and then it runs. Things are not always that simple, however. Because most often we have to sort out the dependencies for an executable, that is, the shared libraries it needs and configuration files it reads, usually we don't know until we run it. However, some tools can help us with this. ldd and strace can tell us these dependencies. For example, once I tried successfully to run Emacs on the base system by simply copying the executable (emacs-nox), a few shared libs and configuration files to the base system. This usually helps us a lot during the development and saves a lot of time.
You might not be satisfied with booting from floppies. Instead, you can implement booting from EPROM or others. To do this, you have to redesign your packaging approach, but the components are mostly unchanged. What's specific here is the kernel image loader. Booting can be implemented like:
boot from EPROM
boot from Networks
boot from devices like flash, hard disk partition, CD-ROM and ZIP disks; that is, any devices other than floppies
boot from other OS
If you like and want to spend more time, you can make your system as fancy as many other successful systems already in the field.
- High-Availability Storage with HA-LVM
- Localhost DNS Cache
- DNSMasq, the Pint-Sized Super Dæmon!
- Days Between Dates: the Counting
- Real-Time Rogue Wireless Access Point Detection with the Raspberry Pi
- You're the Boss with UBOS
- The Usability of GNOME
- Multitenant Sites
- Linux for Astronomers
- PostgreSQL, the NoSQL Database