The Major Components of an Embedded Linux System
This article provides an overview of the major components of a Linux system and describes the interactions between these components. It will explain terms and describe details that may seem very basic, as it doesn’t assume a lot of prior expertise.
Every Linux system has a number of major components. One of these components, the bootloader, is technically outside of Linux and often isn’t talked about. The rest of the components are all software elements that together create the full Linux system. These components are:
- Root filesystem
When the computer is powered on, after performing some initial setup, it will load a bootloader into memory and run that code. (The term “boot” comes from the phrase “pick yourself up by your bootstraps.” It’s kind of a joke because you, of course, can’t pick yourself up by the bootstraps). The bootloader’s main job is to find the operating system’s binary program, load that binary into memory, and run the operating system. In our case, this is the Linux kernel.
The bootloader is done at this point, and all of its code and data in RAM are usually overwritten by the operating system. The bootloader won’t run again until the computer is reset or power cycled again.
The bootloader in embedded systems is different from a typical laptop, desktop or server computer. A typical PC usually boots into what we call the BIOS first and then runs Grub as the bootloader. Embedded Linux systems boot using Das-UBoot or U-Boot for short as the bootloader.
Once the bootloader loads the Linux kernel into memory and runs it, the Kernel will begin running it’s startup code. This startup code will initialize the hardware, initialize system critical data structures, initialize the scheduler, initialize all the hardware drivers, initialize filesystem drivers, mount the first filesystem, and launch the first program, among other things.
The Linux kernel’s main job is to start applications and provide coordination among these applications (or programs, as they’re usually called in Linux). The Linux kernel doesn’t know about all programs that are supposed to run. So the Linux kernel starts only one program and lets that program launch all the other programs that are needed. This very first program is called the init program, or sometimes just “init” for short. Note that this first program doesn’t need to be in a file called “init”, but often it is.
If the kernel can’t find the init program, the kernel’s purpose is gone and the kernel crashes.
The main difference in the Linux kernel for embedded systems is that it is built to run on a different CPU architecture. Otherwise, the way the kernel operates is consistent with a typical PC, which is one of its strengths.
In Linux, the kernel loads programs into memory separately, and the kernel expects these programs to be stored on some medium organized into files and directories. This organization of files and directories is called a filesystem. As is true of many operating systems, Linux has filesystems on media — the data actually stored on a storage medium, and filesystem drivers — the code that knows how to interpret and update the filesystem data on the medium.
In Linux, this medium is often a hard disk. However, embedded systems often don’t have a hard drive, so the medium can be other hardware devices like SD cards, flash memory, or even RAM to name a few.
Unlike Windows, Linux filesystems get associated with a directory rather than a drive letter. Filesystems can be associated with any directory, even one that is several layers down in a path. This associating a filesystem with a directory is called “mounting.” Linux first starts with an empty directory called
/ (slash). During Linux startup, the top most filesystem gets associated with (or mounted to) this directory, and all the contents of that filesystem appear under
/. This topmost filesystem is called the root filesystem.
Linux systems expect the root filesystem to be laid out a certain way. So this filesystem is special and can’t just be some random set of directories and files. This is where directories like
etc, and more come from.
We’ll cover filesystems in more detail in a future article. The main point here is that Linux looks for this first program, this init program, to reside in the filesystem. The root filesystem needs to be created in advance and be mounted to ‘/’ before the kernel can launch the init program.
Because embedded systems have different hardware constraints, often Linux embedded systems use special filesystem formats rather than the typical EXT3, EXT4, btrfs, or xfs used on desktop or laptop computers.
When the kernel finds, loads and runs the init program, that program then is responsible for bringing up the rest of the system. At this point, the kernel is no longer actively running and remains to coordinate the sharing of hardware among all of the running programs.
A number of different init programs are available. Regardless of which init program is chosen, this program will launch all of the necessary services and applications that are needed for the system to be useful. This set of services includes setting up networking, mounting additional filesystems, setting up a graphical environment, and more.
Under Linux, services are just programs that run in the background. Linux folks traditionally call these services daemons or daemon programs, though I see this terminology less frequently these days.
The init program is also responsible for starting regular programs. These programs do have user interaction. Embedded systems often have just a few user programs, sometimes just one. In an embedded system, this set of programs make the device do what it’s supposed to do, for example, display maps and waypoints, listen to microphones, or display a list of recorded tv shows. The possibilities are endless.
So in summary, in broad strokes, when an embedded computer starts, the Linux system will perform these steps:
- jump into the bootloader
- jump into the kernel
- mount the root filesystem
- load and run init
- load and run background services (or daemons)
- load and run applications
Each of these steps invokes a component that is needed in the system.