At runtime, an embedded Linux system contains the following software components:
• Boot loader: What gets the operating system loaded and running on the board.
• Kernel: The software that manages the hardware and the processes.
• Root file system: Everything under the / directory, containing the programs run by the kernel. Every Linux system has a root file system. Embedded systems have a great amount of flexibility in this respect: the root file system can reside in flash, can be bundled with the kernel, or can reside on another computer on the network.
• Application: The program that runs on the board. The application can be a single file or a collection of hundreds of executables.
All these components are interrelated and thus depend on each other to create a running system. Working on an embedded Linux system requires interaction with all of these, even if your focus is only on the application. If you’re new to Linux but have used other commercial embedded solutions, the notion of a distinct kernel and root file system can be disorienting. With a traditional embedded solution, the application code is linked into a binary image with the rest of the embedded OS. After initialization, the operating system calls a function that is the entry point into your code and starts running. Next, I define these components so you understand what they do and how they work together.
Boot loaders can be laden with features, but their primary responsibility is to get the processor initialized and ready to run the operating system. In most modern embedded Linux systems, the kernel is stored in a partition in flash memory. The boot loader copies that flash partition into a certain location in RAM, sets the instruction pointer to that memory location, and tells the processor to start executing at the instruction pointer’s current location. After that, the program that’s running unceremoniously writes over the boot loader. The important thing to note is that the boot loader is agnostic with respect to what is being loaded and run. It can be a Linux kernel or another operating system or a program written to run without an operating system. The boot loader doesn’t care; it performs the same basic actions in all these use scenarios. As boot loaders have matured, they’ve become more like operating systems with network, video, and increasing support for flash storage devices. I look at the popular boot loaders you may encounter when working with Linux. One more important note: boot loaders are now ubiquitous. Rarely as an embedded Linux developer do you need to port a boot loader for your board. You may want to recompile the boot loader (I’ll cover that, too) to remove functionality to conserve space and increase boot time, but the low-level engineering is done by the board vendor. Users of Intel-based systems that use the Phoenix BIOS boot loader have no opportunity to change this code, because it’s baked into the board design.
As discussed earlier, the Linux kernel was created by a Finnish computer science student as a hobby project and was first released in August 1991. The operating system originally ran only on x86 hosts and was modeled on a teaching aid operating system, MINIX. The Linux kernel was first ported to the Motorola 68KB processor, a painful process resulting in Linus Torvalds designing the kernel for portability. By doing the right thing, he laid the groundwork for Linux being ported to nearly every major processor over the following decade. Due to the maturity and wide device support of Linux, engineers spend less time doing kernel development work such as creating device drivers (for example, to drive an LCD) and more time and effort creating applications the user values (like displaying the current weather conditions). Some effort may go into customizing the kernel by removing unneeded components or making other tweaks to increase booting time, but generally you don’t need to do the low-level programming necessary to get the Linux kernel running in the first place. Although it’s an essential and vital component, the kernel has a symbiotic3 relationship with the software it runs. The point isn’t to give the Linux kernel short shrift or minimize its importance! The point is to make clear how the kernel fits into the overall functioning of a Linux system. Without something to run, the kernel stops executing and panics. That’s where the root file system and your application come into play.
A file system is a way of representing a hierarchical collection of directories, where each directory can contain either more directories or files. For computer science types, this hierarchy is a tree structure in which the files are always leaf nodes and directories are internal nodes when they contain something and leaf nodes otherwise. The point of making this trip down data-structure memory lane is that the top node in a tree structure is the root node and that, in Linux, the file system mounted at the top node is aptly called the root file system. On your desktop Linux system, you can see what’s mounted as the root file system by doing the following:
gene@imac-2:~$ mount | head -1 /dev/hda3 on / type ext3 (rw,errors=remount-ro)
Just typing mount shows all the file systems mounted. Most Linux systems have several file systems mounted, but all the file systems are mounted relative to the root file system. When the Linux kernel boots, it must be able to mount a root file system. During the boot process, the root file system can be replaced with another, but only one root file system can be mounted at a time. Failure to mount a root file system means that the system can’t find something to run, because a file system is a container for your program and the kernel panics and halts. Depending on the board’s hardware and application requirements, you’re free to select any number of root file system types. A completed device contains a single file system mounted at root but likely uses several different file systems mounted at other directories within the root file system. Only one file system can be mounted at the root (/ directory), but Linux allows for an arbitrary number of file systems to be mounted at other locations in the root file system. For example, a system that uses flash memory for storage mounts a RAM-based file system for temporary storage because it’s faster, and flash memory has a much smaller duty cycle than RAM.
After the boot loader loads the kernel and the kernel mounts the root file system, it’s time for something to start running that you and your boss view as useful. When Linux starts, it looks for a program to execute by default, or you can supply it with the name of something to run. This program runs as the first process and must continue to run. When this process stops, the kernel, and thus the entire system, stops running. On your desktop Linux system, this program is likely init. You can find out by doing the following:
gene@imac-2:~$ ps aux | head -2 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.2 1692 516 ? S Dec07 0:01 init 
It’s important to note that your application can be anything that can be executed by Linux: a shell script, a C program, a Perl script, or even an assembly written in C# (the Mono project is a robust, open source C# platform)—you name it. As long as it can be executed by Linux, it’s fair game to be the application. This is obvious to those who have already worked with Linux; and it’s liberating almost to the point of disbelief for embedded engineers new to Linux. In addition, you can implement a solution using a few different languages if necessary. All the shared libraries and other supporting files for the application must be present in the root file system deployed on the board.
So far, I’ve discussed the software components that are on the board. A cross-compiler is part of the development environment and, in the most basic terms, produces code that runs on a different processor or operating system than where the compiler ran. For example, a compiler that runs on a Linux x86 host that produces code to execute on an ARM9 target is a cross-compiler. Another example is a compiler running on Windows that produces code that runs on a x86 Linux host. In both cases, the compiler doesn’t produce binaries that can be executed on the machine where the compiler ran. In Linux, the cross-compiler is frequently referred to as a tool chain because it’s a confederation of tools that work together to produce an executable: the compiler, assembler, and linker. The debugger is a separate software component. The article later describes how to obtain or create a tool chain for your target processor based on the GNU Compiler Collection (GCC) project.
Linux is GNU licensed software, and subsequently users who receive a Linux kernel must have the ability to get the source code for the binaries they receive. Having the source code is just one part of what’s necessary to rebuild the software for the board. Without the cross-compiler, you can’t transform that source into something that can run on the remote target. The de facto compiler for Linux is GCC, but this need not always be the case. Several chip vendors market compilers that produce highly optimized code for their processors. The Linux operating system, although written in C and assembler, requires GCC for compilation. However, you can compile programs to be run on the system with a different compiler. For a certain segment of embedded boards, a cross-compiler isn’t necessary. Many PowerPC boards are as powerful as your desktop system; and some embedded systems are, for all intents and purposes, PCs in a different case. In these cases, development can happen right on the board. The compiler runs on the board, which produces code that runs in the same environment; the development cycle is much like that of a regular software project.
Embedded development can be done with a simple collection of tools. This article covers the most frequently used tools so you can understand what they do and how they fit into an embedded system. This article covers each of these in great detail, to provide terra firma on which to stand if you’re new to the world of embedded development. One of the most confusing aspects of embedded Linux is that there are many interrelated tools, it’s difficult to talk about any one in isolation. This is just a subset of the tools used during any development project, but it represents the bare minimum subset. Most projects of any consequence use a variety of tools in addition to the ones mentioned.
Our website is not responsible for the information contained by this article. Articleinput.com is a free articles resource thus practically any visitor can submit an article. However if you notice any copyrighted material, please contact us and we will remove the article(s) in discussion right away.
Note: This article was sent to us by: James D. Smith at 01212010
1. Break ins
© 2009 ArticleInput.com.