从零构建嵌入式Linux系统:S3C2440内核移植与YAFFS2根文件系统制作实战
1. 项目概述:从零构建一个可启动的嵌入式Linux系统
十年前,当我第一次拿到一块S3C2440开发板,看着它静静地躺在防静电袋里,我完全没意识到接下来的几个月会是一场怎样的“硬仗”。那时的我,刚从单片机转向嵌入式Linux,满脑子都是问号:Bootloader、内核、文件系统……这些词听起来就让人头大。市面上虽然有现成的系统镜像可以烧录,但那种“黑盒”操作总让我觉得不踏实——出了问题怎么办?想定制功能怎么办?于是,我决定亲手走一遍从内核移植到文件系统制作的全过程。
这份笔记,就是我当年“踩坑”无数后留下的实战记录。它不仅仅是一份操作手册,更是一个嵌入式Linux系统从无到有的构建逻辑拆解。无论你是刚接触ARM9的新手,还是想深入理解Linux启动流程的老鸟,我相信这里面的细节和经验都能帮到你。我们的目标很明确:在一块基于三星S3C2440处理器(ARM920T核心)的开发板上,移植一个定制的Linux-2.6.29.1内核,并为其制作一个基于YAFFS2的根文件系统,最终让系统从NAND Flash上顺利跑起来。
2. 核心思路与准备工作:为什么是这些选择?
在动手敲命令之前,理清思路比盲目操作重要十倍。嵌入式Linux系统的启动就像一场精心策划的接力赛,每一棒都必须完美交接。
2.1 启动流程全景图与组件选型逻辑
一个典型的嵌入式Linux系统,软件上可以分为四个层次,它们依次执行,缺一不可:
- Bootloader:系统上电后运行的第一段代码。它负责初始化最基础的硬件(如时钟、内存、串口),为内核准备好“舞台”,最后将内核映像从存储设备(如NAND Flash)加载到内存(SDRAM)中,并跳转到内核入口点。你可以把它理解为电脑的BIOS。
- Linux内核:系统的核心。它接管硬件管理、进程调度、内存管理、文件系统等所有核心任务。内核本身是一个高度可配置、可裁剪的软件。
- 根文件系统:内核启动后需要挂载的第一个文件系统。它包含了系统运行所必需的所有应用程序、库、配置文件和设备节点。没有根文件系统,内核在完成初始化后就会“恐慌”(Kernel Panic)并停止。
- 应用程序:运行在操作系统之上的具体软件,实现产品的最终功能。
基于这个流程,我们的选型就有了依据:
- 内核版本选择Linux-2.6.29.1:2.6内核系列在ARM9平台上非常成熟稳定,社区支持好,驱动丰富。2.6.29.1是一个长期支持版本,比更老的2.4系列支持更多新特性(如新的设备模型),又比3.x、4.x等新内核在资源有限的2440上更容易驾驭。
- 文件系统选择YAFFS2:这是为NAND Flash量身定做的日志型文件系统。相比传统的Ext2/Ext3,YAFFS2直接操作Flash物理特性,没有复杂的中间转换层,效率更高,特别适合存储容量不大、需要掉电安全的嵌入式设备。我们的开发板使用NAND Flash作为存储介质,YAFFS2是不二之选。
- Busybox版本选择1.13.3:Busybox被称为“嵌入式Linux的瑞士军刀”,它将上百个常用的Unix命令(如ls, cp, mkdir)集成到一个单一的可执行文件中,通过符号链接来调用,极大地节省了根文件系统的空间。1.13.3版本在功能、稳定性和体积上取得了很好的平衡。
- 交叉编译器选择arm-linux-gcc-4.3.2:编译器版本必须与内核和库文件匹配。4.3.2版本支持EABI(嵌入式应用二进制接口),能生成更高效、更兼容的代码,特别是对软浮点运算有更好的优化,这对于没有硬件浮点单元(FPU)的S3C2440至关重要。
2.2 工具与环境准备清单
工欲善其事,必先利其器。以下是我在RedHat 9.0虚拟机环境下准备的所有材料,你可以根据路径灵活调整。
| 组件 | 具体名称/版本 | 获取来源/备注 |
|---|---|---|
| 开发主机系统 | RedHat Enterprise Linux 9.0 (虚拟机) | 使用CentOS或Ubuntu亦可,但需注意工具链兼容性。 |
| 目标硬件平台 | 友善之臂Mini2440开发板 | 核心为S3C2440,64MB SDRAM,256MB NAND Flash。 |
| Linux内核源码 | linux-2.6.29.1.tar.bz2 | 从 kernel.org 或镜像站下载。 |
| 交叉编译工具链 | arm-linux-gcc-4.3.2 | 从友善之臂官网或第三方社区获取。解压后需将bin目录加入系统PATH环境变量。 |
| Busybox源码 | busybox-1.13.3.tar.bz2 | 从 busybox.net 下载。 |
| YAFFS2文件系统源码 | cvs-root.tar.gz (内含yaffs2) | 从 yaffs2官网 获取。用于给内核打补丁,使其支持YAFFS2。 |
| 根文件系统制作工具 | mkyaffs2image | 用于将制作好的根文件系统目录树打包成YAFFS2格式的镜像文件。通常由开发板厂商提供。 |
| 参考根文件系统 | root_qtopia.tgz | 友善之臂提供的示例根文件系统。我们主要借用其中已经编译好的、针对ARM的动态链接库(lib目录)。 |
实操心得一:环境隔离我强烈建议在
/opt或/home下创建一个独立的项目目录(例如/opt/studyarm),将所有源码和解压后的文件都放在这里。这样做的好处是路径清晰,避免污染系统目录,也方便后期清理和备份。我的所有操作都将基于/opt/studyarm这个工作目录。
3. Linux内核移植详解:让内核认识你的板子
内核移植的本质,是让一个通用的、最初为x86或其他平台编写的Linux内核,能够正确识别并驱动我们手上这块特定的S3C2440开发板上的所有硬件。这个过程主要分为配置、修改、编译三步。
3.1 源码准备与基础配置
首先,我们将内核源码解压到工作目录并进入:
cd /mnt/hgfs/share # 这是我的共享目录,你可能是其他路径 tar -jxvf linux-2.6.29.1.tar.bz2 -C /opt/studyarm cd /opt/studyarm/linux-2.6.29.1接下来是最关键的一步:指定架构和交叉编译器。编辑内核根目录下的Makefile文件,找到大约193行和194行:
ARCH ?= $(SUBARCH) CROSS_COMPILE ?=修改为:
ARCH ?= arm CROSS_COMPILE ?= arm-linux-这告诉编译系统,我们目标平台是ARM,使用的交叉编译器前缀是arm-linux-。
然后,导入一个最接近我们板子的默认配置。S3C2440和S3C2410在核心上高度兼容,所以我们可以先用2410的配置作为起点:
make s3c2410_defconfig这条命令会将arch/arm/configs/s3c2410_defconfig的配置复制到当前目录的.config隐藏文件中。
3.2 针对硬件的关键源码修改
默认配置是通用的,我们的板子有些特殊参数需要手动调整。这些修改集中在arch/arm/mach-s3c2440/目录下。
3.2.1 修正系统时钟开发板上的主晶振频率是12MHz。修改mach-smdk2440.c,找到smdk2440_map_io函数:
static void __init smdk2440_map_io(void) { s3c24xx_init_clocks(16934400); // 默认是16.9344MHz ... }改为:
s3c24xx_init_clocks(12000000); // 我们的板子外接12MHz晶振如果时钟设置错误,会导致串口波特率、定时器、所有总线时序全部错乱,系统根本无法启动。
3.2.2 调整NAND Flash分区与时序Flash分区决定了Bootloader、内核和根文件系统在存储芯片上的物理布局,必须与Bootloader(如VIVI、U-Boot)中的定义严格一致。修改arch/arm/plat-s3c24xx/common-smdk.c中的分区表:
static struct mtd_partition smdk_default_nand_part[] = { [0] = { .name = "bootloader", .size = 0x00030000, // 192KB,存放Bootloader .offset = 0x00000000, }, [1] = { .name = "kernel", .size = 0x00200000, // 2MB,存放内核映像 .offset = 0x00050000, // 从192KB后开始,留一些空隙 }, [2] = { .name = "root", .size = MTDPART_SIZ_FULL, // 剩余所有空间,约62MB,给根文件系统 .offset = 0x00250000, // 紧接着内核之后 } };同时,需要根据你的NAND Flash芯片手册调整访问时序参数tacls,twrph0,twrph1。这些值单位是HCLK周期。对于Mini2440常用的K9F1208U0B,经验值如下:
struct s3c2410_platform_nand smdk_nand_info = { ... .tacls = 0, // 建立时间 .twrph0 = 30, // 写脉冲宽度 .twrph1 = 0, // 读脉冲宽度 ... };时序设置过紧会导致读写不稳定,过松则影响性能。最稳妥的方法是参考你的Bootloader里成功的配置。
3.2.3 启用LCD背光很多LCD屏的背光由一个GPIO控制。对于Mini2440的3.5寸屏,背光由GPG4控制。在mach-smdk2440.c的smdk2440_machine_init函数末尾添加:
s3c2410_gpio_cfgpin(S3C2410_GPG4, S3C2410_GPIO_OUTPUT); s3c2410_gpio_setpin(S3C2410_GPG4, 1); // 输出高电平,点亮背光3.2.4 为内核添加YAFFS2文件系统支持Linux内核默认不支持YAFFS2,需要手动打补丁。
# 1. 解压yaffs2源码 cd /mnt/hgfs/share tar -zxvf cvs-root.tar.gz -C /opt/studyarm # 2. 进入yaffs2源码目录,执行补丁脚本 cd /opt/studyarm/cvs/yaffs2 ./patch-ker.sh c /opt/studyarm/linux-2.6.29.1/这个脚本会自动做三件事:1) 在fs/Kconfig中添加对YAFFS2的配置项;2) 在fs/Makefile中添加编译条目;3) 在fs/目录下创建yaffs2子目录,并将源码复制进去。
3.3 内核的图形化配置与选项解析
执行make menuconfig,会进入一个基于ncurses的文本图形配置界面。以下是针对S3C2440开发板的关键配置项,你需要逐级进入子菜单进行设置:
System Type --->
S3C2410 Machines下,只选中SMDK2410/A9M2410。虽然我们是2440,但2410的机器描述是基础。S3C2440 Machines下,选中SMDK2440和SMDK2440 with S3C2440 CPU module。这是我们的核心板类型。- 务必取消其他所有不相关的机器类型(如S3C2400, S3C2412, S3C2443等),避免编译进无关代码。
Kernel Features --->
- 选中
Use the ARM EABI to compile the kernel。这是因为我们使用的arm-linux-gcc-4.3.2是EABI规范的编译器。EABI在函数调用约定、浮点处理等方面有优化,能生成更高效的代码。对于没有硬件浮点的S3C2440,使用EABI配合内核的软浮点模拟,能提升浮点运算性能。
- 选中
Boot options --->
- 在
Default kernel command string中填入:noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0noinitrd:不使用初始内存盘。root=/dev/mtdblock2:指定根文件系统位于第2个MTD块设备上(对应我们之前分区表中的root分区)。init=/linuxrc:指定内核启动后执行的第一个用户空间程序为/linuxrc(通常由Busybox生成)。console=ttySAC0:指定控制台为串口0。S3C2440的串口设备名是ttySACx,而非传统的ttySx。
- 在
File systems ---> Miscellaneous filesystems --->
- 选中
<*> YAFFS2 file system support。这就是我们刚才打补丁加入的文件系统。 - 在
Native language support子菜单下,选中常用的编码,如Codepage 437,Simplified Chinese charset (GB2312),NLS UTF-8等,以支持中文文件名显示。
- 选中
Device Drivers ---> Graphics support --->
- 选中
Support for frame buffer devices和<*> S3C2410 LCD framebuffer support。这样内核才能驱动LCD显示屏。 - 在
Console display driver support下选中Framebuffer Console support,才能在LCD上显示控制台。 - 在
Bootup logo下可以选择你喜欢的启动Logo(如224-color Linux logo),它会在内核启动初期显示在LCD上。
- 选中
实操心得二:配置的保存与复用完成复杂配置后,一定要记得保存。在
make menuconfig中,选择Save,配置文件会默认保存为.config。你可以将这个文件备份(例如cp .config config_s3c2440_mini2440),下次直接复制回来作为配置起点,无需再次手动勾选。
3.4 内核的编译与产物
配置完成后,依次执行以下命令进行编译:
make clean # 清除之前的编译中间文件,确保全新编译 make zImage # 编译生成压缩的内核映像make zImage会生成一个压缩过的内核映像,它比未压缩的Image体积小,更适合存储空间有限的嵌入式设备。虽然启动时需要额外时间解压,但节省的Flash空间更为宝贵。
编译成功后,最终的内核映像文件位于:
arch/arm/boot/zImage这个大约1.5MB到2.5MB的zImage文件,就是我们即将要烧写到开发板kernel分区的核心。
注意事项:编译错误排查如果编译过程中报错,最常见的原因有:1) 交叉编译器路径未正确设置,检查
CROSS_COMPILE;2) 源码修改有语法错误,仔细检查修改过的.c和.h文件;3) 依赖缺失,尝试make distclean后重新配置和编译。错误信息通常会明确指出文件和行号,这是最好的调试线索。
4. 构建YAFFS2根文件系统:打造系统的“家”
内核启动后,需要一个地方存放系统命令、配置文件、应用程序和它们的运行环境,这个地方就是根文件系统。我们将使用Busybox作为命令集,并构建一个符合FHS(文件系统层次标准)的目录树。
4.1 创建标准的根文件系统目录结构
首先,创建一个脚本create_rootfs_bash来批量生成目录:
#!/bin/bash echo "------Create rootfs directories start...--------" mkdir -p rootfs cd rootfs mkdir -p bin dev etc boot tmp var sys proc lib mnt home usr/sbin usr/bin usr/lib usr/modules mkdir -p etc/init.d etc/rc.d etc/sysconfig mkdir -p mnt/etc mnt/jffs2 mnt/yaffs mnt/data mnt/temp mkdir -p var/lib var/lock var/run var/tmp # 创建设备节点(这是Linux中访问硬件的方式) sudo mknod -m 600 dev/console c 5 1 # 控制台设备 sudo mknod -m 666 dev/null c 1 3 # 空设备,丢弃所有写入的数据 # 设置目录权限 chmod 1777 tmp # 设置sticky位,防止用户删除他人的临时文件 chmod 1777 var/tmp echo "-------make directories done---------"运行这个脚本,一个基本的目录骨架就搭建好了。dev/console和dev/null是两个至关重要的设备文件,没有它们,系统将无法进行输入输出。
4.2 集成Busybox:系统命令的集散地
Busybox的编译和安装是构建根文件系统的核心步骤。
cd /opt/studyarm tar -jxvf /mnt/hgfs/share/busybox-1.13.3.tar.bz2 cd busybox-1.13.3修改Makefile,指定交叉编译器:
CROSS_COMPILE ?= arm-linux- ARCH ?= arm然后进行配置:
make menuconfig关键配置项如下:
- Busybox Settings ---> Build Options --->
- 选中
Build BusyBox as a static binary (no shared libs)。这是一个重要选择:静态链接会将所有库函数编译进Busybox二进制文件,使其体积变大但独立性强,不依赖外部库。动态链接则体积小,但需要配套的动态库。对于初学,我推荐先使用静态编译,可以避免很多库路径问题。
- 选中
- Busybox Settings ---> Installation Options --->
BusyBox installation prefix设置为/opt/studyarm/rootfs。这会让make install直接把编译好的文件安装到我们的根文件系统目录中。
- Linux System Utilities --->
- 确保选中
mdev。mdev是Busybox提供的精简版udev,用于在系统启动或热插拔时自动创建设备节点,这对/dev目录的动态管理至关重要。
- 确保选中
配置完成后,编译并安装:
make make CONFIG_PREFIX=/opt/studyarm/rootfs install安装完成后,查看/opt/studyarm/rootfs目录,会发现生成了bin,sbin,usr目录以及最重要的linuxrc文件(一个指向Busybox的软链接)。linuxrc就是内核指定的第一个用户空间程序init。
4.3 填充库文件与配置文件
动态链接库:即使Busybox静态编译,一些其他动态编译的程序(或你以后添加的程序)仍然需要动态库。最简单的方法是直接从现成的、可用的根文件系统中拷贝。我们使用友善之臂提供的root_qtopia中的库。
tar -zxvf /mnt/hgfs/share/root_qtopia.tgz -C /opt/studyarm cp -rf /opt/studyarm/root_qtopia/lib/* /opt/studyarm/rootfs/lib/关键配置文件:这些文件决定了系统的启动和行为。
etc/inittab:初始化进程init的配置文件,定义了系统启动级别和要运行的进程。# /etc/inittab ::sysinit:/etc/init.d/rcS # 系统启动时执行rcS脚本 ::askfirst:-/bin/sh # 启动一个登录shell,并在控制台提示“Please press Enter” ::ctrlaltdel:/sbin/reboot # 响应Ctrl+Alt+Del ::shutdown:/bin/umount -a -r # 关机时尝试卸载所有文件系统etc/init.d/rcS:系统启动脚本,这是你定制启动流程的地方。
记得给这个脚本加上可执行权限:#!/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin runlevel=S prevlevel=N umask 022 export PATH runlevel prevlevel mount -a # 挂载/etc/fstab中定义的所有文件系统 echo /sbin/mdev > /proc/sys/kernel/hotplug # 设置热插拔事件处理程序为mdev mdev -s # 在/dev目录下扫描并创建设备节点 # 以下是一些自定义信息,启动时会显示 echo "***********************************************" echo "****************Studying ARM*********************" echo "Kernel version:linux-2.6.29.1" echo "Student:Feng dong rui" echo "Date:2009.07.15" echo "***********************************************" /bin/hostname -F /etc/sysconfig/HOSTNAME # 设置主机名chmod +x etc/init.d/rcS。etc/fstab:文件系统挂载表。
这里将# device mount-point type options dump fsck proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 tmpfs /dev tmpfs defaults 0 0proc(进程信息虚拟文件系统)、sysfs(设备模型文件系统)和/dev(使用tmpfs在内存中创建,由mdev动态管理)挂载到相应目录。etc/profile:Shell环境配置文件。# Ash profile USER="`id -un`" LOGNAME=$USER PS1='[\u@\h \W]\$ ' # 设置命令行提示符,显示用户、主机名和当前目录 PATH=$PATH export USER LOGNAME PS1 PATH
4.4 生成YAFFS2镜像文件
首先安装镜像制作工具:
cd /mnt/hgfs/share tar -zxvf mkyaffs2image.tgz -C / # 通常会将工具安装到/usr/sbin/然后,在我们根文件系统的父目录下,运行制作命令:
cd /opt/studyarm mkyaffs2image rootfs rootfs.img成功执行后,会生成一个名为rootfs.img的文件,这就是最终的根文件系统镜像,大小通常在几MB到几十MB不等,取决于你添加的内容。
实操心得三:文件系统镜像的验证在烧录前,可以先用
file命令检查一下生成的镜像:file rootfs.img。它应该显示为VMS Alpha executable或类似的描述,表明这是一个YAFFS2文件系统镜像。你也可以尝试用mount命令挂载(需要内核支持YAFFS2),或者使用unsquashfs(针对其他格式)之类的工具来查看内部文件,确保关键目录和文件都存在。
5. 系统启动、问题排查与经验总结
5.1 烧录与启动
将编译好的zImage和rootfs.img通过开发板厂商提供的工具(如DNW、tftp或SD卡)分别烧写到NAND Flash的kernel分区和root分区。确保Bootloader(如VIVI或U-Boot)的参数设置正确,特别是启动命令bootargs,必须包含root=/dev/mtdblock2 console=ttySAC0,115200等内容,与内核配置匹配。
上电后,串口控制台(如SecureCRT或minicom)会输出大量启动信息。一个成功的启动日志,其关键节点如下:
- Bootloader信息:显示VIVI或U-Boot版本,初始化内存、时钟,加载内核。
- 内核解压:显示
Uncompressing Linux... done, booting the kernel.。 - 内核启动:显示Linux版本、CPU识别(
CPU: ARM920T)、机器类型(Machine: Study-S3C2440)、内核命令行参数(Kernel command line:)。 - 设备驱动初始化:依次初始化DMA、NAND Flash(显示ID和分区)、FrameBuffer、串口、USB等。
- 挂载根文件系统:出现
yaffs: dev is 32505858 name is "mtdblock2"和VFS: Mounted root (yaffs filesystem) on device 31:2.,这是成功的标志! - 执行初始化脚本:运行
/etc/init.d/rcS,显示你自定义的启动信息。 - 启动Shell:最后出现
Please press Enter to activate this console.,按下回车,出现熟悉的[root@MrFeng /]#提示符。运行ls等命令测试,系统构建完成。
5.2 常见启动失败问题与排查实录
移植过程极少一帆风顺,以下是几个我踩过的“坑”及其解决方案:
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
| 内核启动后卡住,无任何输出 | 1. 串口波特率不对。 2. 内核未正确配置串口驱动。 3. 内核启动参数 console设置错误。 | 1. 确认Bootloader和终端软件的波特率均为115200。 2. 在内核配置中确认 Device Drivers -> Character devices -> Serial drivers -> S3C2410 serial port support已选中。3. 检查Bootloader传给内核的 bootargs,确保console=ttySAC0,115200(注意是ttySAC0不是ttyS0)。 |
内核panic:无法挂载根文件系统VFS: Cannot open root device "mtdblock2" or unknown-block(31,2) | 1. 内核不支持MTD块设备或YAFFS2。 2. 内核命令行 root=参数错误。3. Flash分区号不对。 4. 根文件系统镜像损坏或格式不对。 | 1. 检查内核配置:Device Drivers -> Memory Technology Device (MTD) support及其子项,以及File systems -> Miscellaneous -> YAFFS2。2. 核对 root=/dev/mtdblockX,X是你的root分区序号(从0开始)。3. 确认 mtdblock2对应NAND Flash的第三个分区(即我们定义的root分区)。4. 重新制作 rootfs.img,并检查文件是否完整。 |
内核启动后提示/linuxrc不存在或无法执行 | 1. 根文件系统中没有linuxrc文件。2. linuxrc没有可执行权限。3. linuxrc是动态链接的,但缺少动态库。 | 1. 确认Busybox已成功安装,且/linuxrc软链接存在。2. 在根文件系统目录中执行 ls -l linuxrc查看权限,应为lrwxrwxrwx指向bin/busybox。3. 如果Busybox是动态链接,确保 /lib目录下有正确的ld-linux.so和libc.so等库文件。强烈建议初次尝试时使用静态编译的Busybox。 |
启动后/dev目录下设备节点为空 | mdev没有正确运行。 | 1. 检查/etc/init.d/rcS脚本中是否包含mdev -s命令。2. 检查内核是否配置了 sysfs和tmpfs支持,并且/etc/fstab中正确挂载了sysfs和tmpfs到/sys和/dev。3. 检查 /proc/sys/kernel/hotplug是否被正确设置为/sbin/mdev。 |
| LCD白屏或显示异常 | 1. LCD背光未开启。 2. LCD时序参数配置错误。 3. FrameBuffer驱动未编译进内核或配置错误。 | 1. 确认源码中已添加背光GPIO控制代码。 2. 根据你的LCD型号数据手册,仔细核对 mach-smdk2440.c中的smdk2440_lcd_cfg结构体参数(如left_margin,right_margin,hsync_len等)。3. 确认内核配置中 Device Drivers -> Graphics support -> Support for frame buffer devices -> S3C2410 LCD framebuffer support已选中。 |
5.3 项目回顾与进阶思考
回顾整个移植过程,其核心逻辑可以概括为“匹配”二字:Bootloader与内核的匹配(机器ID、参数传递)、内核与硬件的匹配(驱动、参数)、内核与文件系统的匹配(格式、分区)。任何一个环节不匹配,都会导致启动失败。
这次成功的移植,不仅仅是一次技术实践,更是一次对嵌入式Linux系统骨架的深刻理解。它让我明白了,一个能跑起来的系统只是起点。在此基础上,你可以:
- 裁剪内核:使用
make menuconfig深入内核子菜单,去掉不需要的驱动、文件系统、网络协议,让内核体积更小,启动更快。 - 优化启动速度:分析
bootgraph.pl生成的启动时间图,优化驱动初始化顺序,甚至使用initramfs将部分根文件系统集成到内核中。 - 添加自己的驱动:学习Linux设备驱动模型,为你的特定外设(如传感器、扩展接口)编写内核模块。
- 构建更复杂的应用:在根文件系统中加入图形界面(如Qt/E)、网络服务(如boa web服务器)、数据库等,打造真正的产品原型。
嵌入式开发就是这样,从点亮一个LED,到让整个Linux系统运行起来,每一步都充满挑战,也每一步都收获巨大。这份笔记记录了我最初的探索,希望它也能成为你嵌入式Linux之旅的一块坚实垫脚石。当你第一次在自制系统的命令行提示符下输入ls并看到输出时,那种成就感,就是驱动我们工程师不断前行的最好燃料。
