Guide to Building Embedded Linux Root File System

Click on the “Embedded Application Research Institute” above, and select “Top/Star Official Account

Useful Resources Delivered First Hand!

Source | CSDN-Stop at Perfection 656

Compiled & Formatted | Embedded Application Research Institute

1. Root File System Layout

The layout of the embedded Linux root file system is recommended to follow the FHS standard. In fact, most embedded Linux systems do this. However, embedded systems may not require all directories of a large desktop/server system, and the system can be streamlined as appropriate to simplify the use of Linux. For instance, the embedded Linux file system typically does not include kernel source code, so the /usr/src directory for storing source code is unnecessary, and even header files in the /usr/include directory are not needed; however, the directories /bin, /dev, /etc, /lib, /proc, /sbin, /usr are essential.

Therefore, it is permissible for embedded Linux to simplify the system directory structure to meet specific application needs. A typical embedded Linux root file system directory is shown below:

Guide to Building Embedded Linux Root File System

To build a usable Linux root file system, many binaries and libraries are needed. Starting from scratch is impractical; it is recommended to refer to other existing usable file systems and modify them as needed; or use file system creation tools like BusyBox to generate the file system.

2. Using BusyBox to Generate Binary Tools

2.1. Obtaining BusyBox Source Code

The official source code download path for BusyBox is: https://busybox.net/downloads/. Here, we take downloading busybox-1.29.3.tar.bz2 as an example.

2.2. Configuring BusyBox

Unzip the source code and enter the root directory:

$ tar jxvf busybox-1.29.3.tar.bz2
$ cd busybox-1.29.3/

First, execute:

$ make menuconfig

Enter the graphical configuration interface:

Guide to Building Embedded Linux Root File System

2.2.1. Select Static Library Compilation

In Settings —> use the space key to select to compile static libraries

--- Build Options                                              
[*] Build static binary (no shared libs)  

As shown:

Guide to Building Embedded Linux Root File System

2.2.2. Select Cross-Compilation Toolchain

In Settings —> under settings, fill in the prefix for the cross-compilation toolchain

Guide to Building Embedded Linux Root File System

2.2.3. Select Installation Directory

In Settings —> under settings, find

--- Installation Options ("make install" behavior) 
    What kind of applet links to install (as soft-links)  --->  
(./_install) Destination path for 'make install' (NEW)  

The default is the current directory; here I use the default _install directory:

Guide to Building Embedded Linux Root File System

2.2.4. Compile and Install

After exiting and saving, execute make to compile, which will finish in a few minutes, and then execute make install, which will complete installation quickly:

Guide to Building Embedded Linux Root File System

Enter the _install directory and check the generated files

Guide to Building Embedded Linux Root File System

Create a new directory to store the generated root file system, which can be named rootfs. Copy all files and directories from the _install directory to the rootfs directory.

3. Building the Root File System

After compiling with BusyBox, there are only three directories: bin, sbin, and usr, along with the soft link linuxrc. The directory contains binary command tools, which are insufficient to form a usable root file system. Additional work must be done to complete a usable root file system.

3.1. Improve Directory Structure

According to the typical embedded Linux root file system directory, create other directories in the rootfs directory:

$ mkdir dev etc lib proc sys tmp var
3.2. Add C Runtime Library Files

Library files can be obtained directly from the cross-toolchain, usually in the libc/lib/ directory of the toolchain. Here, I installed the Linaro cross-toolchain on ubuntu:

Guide to Building Embedded Linux Root File System

The library files are in the /usr/arm-linux-gnueabihf/lib/ directory. Copy the dynamic link library files (.so files) to the new root file system’s root directory /lib:

$ cp -a  /usr/arm-linux-gnueabihf/lib/*so* ./lib/

Guide to Building Embedded Linux Root File System

This only copies dynamic link libraries. Generally, programs using dynamic compilation need support from dynamic libraries on the board to run, hence the dynamic libraries are copied. Static libraries are usually used during static compilation, and since the cross-compilation work is done on the PC, there is no need for static libraries on the board, so they are not copied, which also reduces the size of the root file system.

Executable files, object files, and dynamic libraries compiled with gcc usually contain debugging and symbol information, which are useful during debugging but increase file size. Typically, debugging is done on the PC, or libraries with debugging and symbol information are used during debugging; after the program is released, libraries without this information can significantly reduce the size of the root file system. Here, we remove this information using the strip tool:

$ arm-linux-gnueabihf-strip ./*
3.3. Add Initialization Configuration Script

The initialization configuration script is placed in the /etc directory and is used for the initialization configuration script required for system startup. BusyBox provides some example initialization scripts in the examples/bootfloppy/etc/ directory. Copy these configuration files to the newly created root file system’s etc directory:

cp -a ../busybox/busybox-1.29.3/examples/bootfloppy/etc/* etc/

After adding, it appears as shown:

Guide to Building Embedded Linux Root File System

3.3.1. Modify /etc/inittab File

The /etc/inittab file is the configuration file parsed by the init process, which determines which process to execute and when to execute it. Modify the file to:

# System startup
::sysinit:/etc/init.d/rcS

# Press Enter key during system startup
::askfirst:-/bin/sh

# Press Ctrl+Alt+Del key
::ctrlaltdel:/sbin/reboot

# System shutdown
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

# System restart
::restart:/sbin/init

The above content defines the processes to be executed during system startup, shutdown, restart, and when pressing the Ctrl+Alt+Del key.

3.3.2. Modify /etc/init.d/rcS File
#! /bin/sh

# Mount all file systems defined in /etc/fstab
/bin/mount -a

# Mount virtual devpts file system for pseudo-terminal devices
/bin/mkdir -p /dev/pts
/bin/mount -t devpts devpts /dev/pts

# Use mdev to dynamically manage hot-plug devices like USB drives and mice
/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug

# Scan and create nodes
/sbin/mdev -s
3.3.3. Modify /etc/fstab File

The /etc/fstab file stores the file system information. After the system starts, when the /etc/init.d/rcS file executes the /bin/mount -a command, these file systems are automatically mounted. The content is as follows:

# <file system>    <mount point>    <type>    <options>    <dump>    <pass>     
proc                  /proc          proc     defaults       0         0
sysfs                 /sys           sysfs    defaults       0         0
tmpfs                 /tmp           tmpfs    defaults       0         0
tmpfs                 /dev           tmpfs    defaults       0         0

Note: The file systems we are mounting here are proc, sysfs, and tmpfs. Both proc and sysfs are supported by default in the kernel, while tmpfs is not supported, so we need to add support for tmpfs.

3.3.4. Modify /etc/profile File

The /etc/profile file is used to set environment variables and is executed for each user upon login. Modify the file content to:

# Hostname
export HOSTNAME=zyz

# Username
export USER=root

# User directory
export HOME=/root

# Default terminal prompt
export PS1="[$USER@$HOSTNAME:\$PWD]# "    

# Environment variables
export PATH=/bin:/sbin:/usr/bin:/usr/sbin

# Dynamic library path
export LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH

Since the root user’s home directory is specified as /root, this directory needs to be created; otherwise, executing cd ~ will fail:

$ mkdir root

The effect after logging into the system is:

...
Please press Enter to activate this console.
[root@zyz:/]#
[root@zyz:/]# cd ~
[root@zyz:/root]#
[root@zyz:/root]# echo $PATH
/bin:/sbin:/usr/bin:/usr/sbin

At this point, the root file system is basically constructed.

4. Create Root File System Image

4.1. Root File System Types

If the file system layout is complete, it can be sent to the target. Typically, an image is created and then solidified into the target system in some way. The specific method for release needs to be decided based on resource conditions, kernel status, and system requirements:

(1) From a hardware perspective, at least consider the type and size of the main storage medium, such as whether Flash is NOR Flash or NAND Flash, the size of RAM, etc.

(2) From a kernel perspective, consider which file systems are supported after the cut and which are most suitable to meet requirements for compatibility, speed, etc.

(3) From a system requirements perspective, consider factors like running speed and whether it can be written or compressed. Common types of root file systems that can be used include ramdisk, cramfs, jffs2, yaffs/yaffs2, and ubifs, with their respective characteristics listed in the table.

Guide to Building Embedded Linux Root File System

Although the file system firmware is released as an image of a certain file system, the entire file system actually coexists with multiple logical file systems. For example, a system’s root file system may be mounted as ubifs, but the /dev directory is mounted as tmpfs, and the /sys directory is mounted as sysfs. Currently, ubifs seems to be a trend.

4.2. Create UBIFS Root File System Image

There are two commands to create UBIFS under Linux: mkfs.ubifs and ubinize. mkfs.ubifs creates a UBIFS file system from a directory. Usage example:

$ mkfs.ubifs -m 2048 -e 128KiB -c 4096 -r ./rootfs -o rootfs.ubifs

Where:

-r, -d, --root=DIR       build file system from directory DIR(directory)
-m, --min-io-size=SIZE   minimum I/O unit size(minimum input/output unit size)
-e, --leb-size=SIZE      logical erase block size(logical erase block size)
-c, --max-leb-cnt=COUNT  maximum logical erase block count(maximum logical erase block number)
-o, --output=FILE        output to FILE(output file)

Thus, to create a ubifs image file, three key parameters must be known: the minimum input/output unit size, the logical erase block size, and the maximum logical erase block count, where the maximum logical erase block count can be calculated from the Flash partition size and the logical erase block size. This information can be viewed through u-boot commands:

=> mtdparts default
=> ubi part rootfs

ubinize creates a UBIFS file system image with a volume label that can be directly burned onto Flash from the image created by mkfs.ubifs. Usage example:

$ ubinize -m 2048 -p 128KiB ubinize.cfg -o rootfs_ubifs.img

Where:

-o, --output=<file name>     output file name(output file)
-p, --peb-size=<bytes>       size of the physical eraseblock of the flash(physical erase block size)
                             this UBI image is created for in bytes,
                             kilobytes (KiB), or megabytes (MiB)
                             (mandatory parameter)
-m, --min-io-size=<bytes>    minimum input/output unit size of the flash
                             in bytes

Here, two parameters are needed: the physical erase block size and the minimum input/output unit size. The ubinize.cfg is the configuration file, with the following content:

[ubifs]
mode=ubi
image=rootfs.ubifs
vol_id=0
vol_size=1024MiB
vol_type=dynamic
vol_name=rootfs
vol_flags=autoresize

Explanation:

[ubifs]
mode=ubi
image=rootfs.ubifs       # Source image generated by mkfs.ubi 
vol_id=0                 # Volume number
vol_size=1024MiB         # Volume size, generally set larger than the partition to prevent bad blocks
vol_type=dynamic         # Volume type, dynamic volume
vol_name=rootfs          # Volume name, rootfs
vol_flags=autoresize     # Auto size

Previous Highlights

Today, I discussed work-related issues with ChatGPT

Layered Design of Embedded Software Architecture

Comprehensive Performance Evaluation of Embedded Linux

Open Source Project: Creating a Series of Common Linux Application API Wheels

Make Your Code More Stylish: Summary of 23 Design Patterns

Porting Ubuntu Core 16.04 (ubuntu-base) to i.MX6ULL Development Board

If you find this article helpful, feel free to click <span>[Looking]</span> and share it, which is also a support for me.

Leave a Comment