The "Holy Bible" for embedded engineers
Customized Linux for Embedded Systems
Understanding Buildroot, Yocto, and custom distributions for resource-constrained devices
Embedded Linux refers to Linux distributions specifically designed for embedded systems—devices with limited resources, specific hardware requirements, and specialized functionality. Unlike desktop Linux, embedded Linux focuses on efficiency, customization, and reliability.
Embedded Linux Characteristics:
Desktop Linux:
Embedded Linux:
┌─────────────────────────────────────┐
│ Embedded Linux Stack │
├─────────────────────────────────────┤
│ Application Layer │
│ (Custom applications) │
├─────────────────────────────────────┤
│ System Services │
│ (Init system, networking) │
├─────────────────────────────────────┤
│ Linux Kernel │
│ (Optimized for target) │
├─────────────────────────────────────┤
│ Bootloader │
│ (U-Boot, GRUB, etc.) │
├─────────────────────────────────────┤
│ Hardware Layer │
│ (Target-specific) │
└─────────────────────────────────────┘
Embedded Linux follows the purpose-built principle—create Linux distributions that are specifically designed for the target application, hardware, and operational requirements.
Embedded Linux Design Goals:
Build systems for embedded Linux automate the process of creating custom distributions. They handle dependency resolution, cross-compilation, and system integration to produce bootable images.
Build systems follow the automation and reproducibility principle—automate the complex process of building embedded Linux systems while ensuring reproducible results across different environments.
Build System Goals:
Core Components:
Build Process Flow:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Source Code │───▶│ Build System │───▶│ System Image │
│ & Patches │ │ Processing │ │ Generation │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Dependencies │ │ Cross-compile │ │ Bootable │
│ Resolution │ │ Toolchain │ │ Image │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Buildroot is a lightweight build system that creates embedded Linux systems from source code. It’s designed for simplicity and efficiency, making it ideal for smaller projects and rapid prototyping.
Buildroot follows the simplicity and efficiency principle—provide a straightforward build system that produces minimal, efficient embedded Linux systems with minimal overhead.
Buildroot Design Goals:
Core Components:
┌─────────────────────────────────────┐
│ Buildroot Structure │
├─────────────────────────────────────┤
│ Config.in │
│ (Package selection) │
├─────────────────────────────────────┤
│ Rules.mak │
│ (Build rules) │
├─────────────────────────────────────┤
│ Package/ │
│ (Package definitions) │
├─────────────────────────────────────┤
│ Board/ │
│ (Board configurations) │
├─────────────────────────────────────┤
│ Configs/ │
│ (Default configurations) │
└─────────────────────────────────────┘
Buildroot Configuration:
# Basic Buildroot setup
git clone https://github.com/buildroot/buildroot.git
cd buildroot
# Configure for target
make defconfig
make menuconfig
# Build system
make
# Clean build
make clean
make distclean
Package Definition Example:
# Package definition for custom application
################################################################################
#
# myapp
#
################################################################################
MYAPP_VERSION = 1.0.0
MYAPP_SITE = $(TOPDIR)/package/myapp/src
MYAPP_SITE_METHOD = local
MYAPP_LICENSE = GPL-2.0
MYAPP_LICENSE_FILES = LICENSE
# Build dependencies
MYAPP_DEPENDENCIES = host-pkgconf
# Build commands
define MYAPP_BUILD_CMDS
$(MAKE) CC=$(TARGET_CC) LD=$(TARGET_LD) -C $(@D)
endef
# Install commands
define MYAPP_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 0755 $(@D)/myapp $(TARGET_DIR)/usr/bin/myapp
$(INSTALL) -D -m 0644 $(@D)/myapp.conf $(TARGET_DIR)/etc/myapp.conf
endef
$(eval $(generic-package))
Custom Package Integration:
# Create package directory
mkdir -p package/myapp
cp myapp.mk package/myapp/
# Add to package selection
echo "source package/myapp/Config.in" >> package/Config.in
# Create Config.in
cat > package/myapp/Config.in << EOF
config BR2_PACKAGE_MYAPP
bool "myapp"
help
Custom application for embedded system.
http://example.com/myapp
EOF
The Yocto Project is a comprehensive build system designed for industrial embedded Linux development. It provides advanced features, extensive package support, and enterprise-grade reliability.
Yocto follows the industrial strength principle—provide a robust, scalable build system that meets the requirements of enterprise embedded Linux development with comprehensive tooling and support.
Yocto Design Goals:
Core Components:
┌─────────────────────────────────────┐
│ Yocto Architecture │
├─────────────────────────────────────┤
│ BitBake │
│ (Build engine) │
├─────────────────────────────────────┤
│ OpenEmbedded Core │
│ (Build system) │
├─────────────────────────────────────┤
│ Meta Layers │
│ (Configuration & packages) │
├─────────────────────────────────────┤
│ Build Output │
│ (Images, packages, SDK) │
└─────────────────────────────────────┘
Yocto Setup:
# Clone Yocto Project
git clone -b kirkstone git://git.yoctoproject.org/poky.git
cd poky
# Source environment
source oe-init-build-env
# Configure for target
bitbake-layers add-layer meta-raspberrypi
bitbake-layers add-layer meta-openembedded/meta-oe
# Build core image
bitbake core-image-minimal
# Build custom image
bitbake my-custom-image
Basic Recipe Example:
# Recipe for custom application
DESCRIPTION = "Custom embedded application"
HOMEPAGE = "http://example.com"
LICENSE = "GPL-2.0-only"
LIC_FILES_CHKSUM = "file://LICENSE;md5=1234567890abcdef"
SRC_URI = "file://myapp-${PV}.tar.gz"
SRC_URI[sha256sum] = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
S = "${WORKDIR}/myapp-${PV}"
inherit autotools pkgconfig
# Build dependencies
DEPENDS += "pkgconfig-native"
# Runtime dependencies
RDEPENDS:${PN} += "libc"
# Install additional files
do_install:append() {
install -d ${D}${sysconfdir}/myapp
install -m 0644 ${S}/config/myapp.conf ${D}${sysconfdir}/myapp/
install -d ${D}${systemd_system_unitdir}
install -m 0644 ${S}/systemd/myapp.service ${D}${systemd_system_unitdir}/
}
# Enable systemd integration
inherit systemd
SYSTEMD_AUTO_ENABLE = "enable"
SYSTEMD_SERVICE:${PN} = "myapp.service"
Custom Image Recipe:
# Custom image recipe
DESCRIPTION = "Custom embedded Linux image"
LICENSE = "MIT"
inherit core-image
# Include additional packages
IMAGE_INSTALL += " \
myapp \
openssh \
iperf3 \
htop \
"
# Include development tools (debug builds)
IMAGE_INSTALL:append = " \
packagegroup-core-buildessential \
gdb \
strace \
"
# Configure image features
IMAGE_FEATURES += " \
ssh-server-openssh \
tools-debug \
tools-profile \
"
# Set root password
ROOTFS_POSTPROCESS_COMMAND += "set_root_passwd;"
set_root_passwd() {
sed -i 's/^root:[^:]*:/root:\$6\$rounds=5000\$salt\$hash:/' ${IMAGE_ROOTFS}/etc/shadow
}
Custom distribution development involves creating Linux systems specifically designed for target applications. This requires understanding system requirements, component selection, and integration.
Custom distributions follow the application-specific principle—design Linux systems that perfectly match the target application’s requirements, constraints, and operational environment.
Custom Distribution Goals:
Essential Components:
# Minimal system components
- Linux kernel (optimized for target)
- Init system (systemd, busybox, or custom)
- Core libraries (glibc, uclibc, or musl)
- Basic utilities (coreutils, busybox)
- Device management (udev, mdev)
- Network stack (if required)
- Application-specific packages
Component Selection Criteria:
# Size considerations
- Memory footprint
- Storage requirements
- Boot time impact
# Functionality requirements
- Application dependencies
- Hardware support needs
- Network requirements
# Maintenance considerations
- Update mechanisms
- Security updates
- Long-term support
Systemd Configuration:
# /etc/systemd/system/myapp.service
[Unit]
Description=Custom Application
After=network.target
Wants=network.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/myapp
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Busybox Init Configuration:
# /etc/inittab
::sysinit:/etc/init.d/rcS
::respawn:/sbin/getty -L ttyS0 115200 vt100
::respawn:/opt/myapp/myapp
::shutdown:/etc/init.d/rcK
System optimization involves reducing resource usage, improving performance, and ensuring reliable operation within the constraints of embedded hardware.
System optimization follows the efficiency and reliability principle—maximize system performance and reliability while minimizing resource usage and power consumption.
Optimization Goals:
Kernel Configuration:
# Essential kernel options
CONFIG_EMBEDDED=y
CONFIG_EXPERT=y
CONFIG_SLOB=y # Simple memory allocator
CONFIG_BLK_DEV_INITRD=y # Initial RAM disk
CONFIG_DEVTMPFS=y # Device filesystem
CONFIG_DEVTMPFS_MOUNT=y # Auto-mount devtmpfs
# Disable unnecessary features
# CONFIG_DEBUG_FS is not set
# CONFIG_DEBUG_KERNEL is not set
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_MEMORY_INIT is not set
# Enable only required drivers
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK=y
Kernel Size Reduction:
# Remove unused drivers
# CONFIG_SOUND is not set
# CONFIG_VIDEO_DEV is not set
# CONFIG_INPUT is not set
# Optimize for size
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_KERNEL_GZIP=y
CONFIG_MODULES=n
Filesystem Selection:
# Lightweight filesystems
- squashfs: Read-only, compressed
- ext4: Journaling, good performance
- f2fs: Flash-optimized
- jffs2: Flash filesystem
- ubifs: Advanced flash filesystem
Library Optimization:
# Use lightweight alternatives
- musl libc instead of glibc
- busybox instead of coreutils
- dropbear instead of openssh
- mdev instead of udev
Package Optimization:
# Remove unnecessary packages
- Development tools
- Documentation
- Locale data (keep only required)
- Debug symbols
- Unused libraries
Deployment and maintenance involve creating reliable update mechanisms, monitoring system health, and ensuring long-term operation in the field.
Deployment follows the reliability and maintainability principle—ensure systems can be deployed reliably and maintained effectively throughout their operational lifetime.
Deployment Goals:
OTA (Over-The-Air) Updates:
# Update script example
#!/bin/sh
# Download update
wget -O /tmp/update.tar.gz http://update.server/update.tar.gz
# Verify checksum
echo "expected_checksum /tmp/update.tar.gz" | sha256sum -c
# Extract update
tar -xzf /tmp/update.tar.gz -C /tmp/update
# Backup current system
cp -r /opt/myapp /opt/myapp.backup
# Install update
cp -r /tmp/update/* /opt/myapp/
# Restart application
systemctl restart myapp
# Cleanup
rm -rf /tmp/update*
Dual Partition Updates:
# Dual partition update script
#!/bin/sh
# Determine current partition
CURRENT_PART=$(cat /proc/cmdline | grep -o "root=/dev/mmcblk0p[12]")
if echo $CURRENT_PART | grep -q "p1"; then
UPDATE_PART="/dev/mmcblk0p2"
NEXT_PART="p2"
else
UPDATE_PART="/dev/mmcblk0p1"
NEXT_PART="p1"
fi
# Format update partition
mkfs.ext4 $UPDATE_PART
# Mount and copy files
mount $UPDATE_PART /mnt/update
cp -r /opt/myapp/* /mnt/update/
# Update bootloader to boot from new partition
fw_setenv bootpart $NEXT_PART
# Reboot to new partition
reboot
Health Monitoring:
#!/bin/sh
# System health check script
while true; do
# Check memory usage
MEM_USAGE=$(free | grep Mem | awk '{print $3/$2 * 100.0}')
if [ $(echo "$MEM_USAGE > 90" | bc) -eq 1 ]; then
logger "WARNING: High memory usage: ${MEM_USAGE}%"
fi
# Check disk usage
DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 90 ]; then
logger "WARNING: High disk usage: ${DISK_USAGE}%"
fi
# Check application status
if ! pgrep myapp > /dev/null; then
logger "ERROR: Application not running, restarting"
systemctl restart myapp
fi
sleep 60
done
Log Management:
# Log rotation configuration
cat > /etc/logrotate.d/myapp << EOF
/var/log/myapp/*.log {
daily
missingok
rotate 7
compress
notifempty
create 644 myapp myapp
postrotate
systemctl reload myapp
endscript
}
EOF
Embedded Linux provides powerful capabilities for creating customized, efficient Linux systems for embedded applications. Understanding Buildroot, Yocto, and custom distribution development is essential for building reliable embedded systems.
Key Takeaways:
The Path Forward:
As embedded systems become more complex and connected, the importance of embedded Linux skills will only increase. Modern systems continue to evolve, providing new optimization techniques and deployment strategies.
Remember: Embedded Linux is not just about running Linux on embedded devices—it’s about creating purpose-built systems that perfectly match your application requirements while efficiently using available resources. The skills you develop here will enable you to create robust, efficient, and maintainable embedded Linux systems.