ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从零构建嵌入式Linux根文件系统:BusyBox原理与实战

从零构建嵌入式Linux根文件系统:BusyBox原理与实战 做嵌入式Linux开发的人早晚都要跟BusyBox打交道。不管是搞驱动、搞应用还是只想把一块开发板跑起来最后几乎都要落到“怎么做一个能用的根文件系统”这个问题上。而一提到根文件系统BusyBox就是绕不开的那个名字甚至可以说它就是嵌入式Linux的“瑞士军刀”——一个二进制文件集成了上百个常用命令ls、cp、mv、init、sh、mount、vi、tar全都有体积还小得惊人。这篇东西我想从一个老嵌入式工程师的角度把BusyBox的原理、根文件系统的设计思路以及从零构建一个可运行根文件系统的完整过程都捋一遍。包括怎么编译、怎么搭目录、怎么写init、怎么用NFS挂载调试、怎么集成dropbear做SSH远程登录以及那些网上很少说清楚的坑。适合刚开始接触嵌入式Linux的初学者也适合那些已经能跑通demo但想弄清底层机制的兄弟们。1. BusyBox到底是什么一个“躺平”的产物1.1 从“多个命令”到“一个二进制”先问你一个问题你在PC的Linux上敲ls系统到底执行了什么shell会去PATH路径下找有个叫ls的可执行文件比如/usr/bin/ls然后加载它运行。这是正常的Linux命令模型每个命令都是一个独立的二进制程序。但BusyBox不是这么玩的。你安装它之后在/bin目录下会看到一排软链接比如/bin/ls - busybox、/bin/cp - busybox、/usr/bin/tar - busybox。这些软链接全部指向同一个可执行文件/bin/busybox。当你执行ls时内核加载的其实是busybox这个程序唯一的区别是传给程序的argv[0]是“ls”。BusyBox拿到argv[0]之后会在内部一张applet表里查找匹配的名字然后调用对应的函数去干活。这设计是不是很“躺平”它把原来一堆独立的工具全部揉进了一个ELF文件里。好处显而易见整个根文件系统只需要一个C库、一个BusyBox、几个配置文件几十MB的发行版能压到几MB甚至几百KB用静态编译精简配置时。代价是每个命令的功能都比GNU coreutils弱一些参数也没那么全这在嵌入式场景里完全够用。1.2 为什么嵌入式Linux离不开它嵌入式设备存储空间小Flash有时候就几十MBRAM更是紧张。如果每个命令都单独编译光库和程序的开销就很可观。BusyBox一出来等于把几百个命令的代码共享了一份符号重用的价值极大。更关键的是BusyBox不只是“命令集合”它还实现了很多系统启动必需的核心组件/sbin/initBusyBox自带init程序读取/etc/inittab启动shell、挂载文件系统、执行rcS脚本。mdev轻量级设备管理程序配合/sys和/etc/mdev.conf可以动态创建设备节点替代udev。sh一个可用的shellash虽然不如bash功能全但写init脚本足够了。mount、umount、switch_root、pivot_root配合initramfs做系统切换。telnetd、dropbear需要额外集成、httpd等网络服务。可以说BusyBox把“让一个嵌入式设备启动并进入可用状态”所需的用户态工具都打包好了。内核起来之后第一个用户程序就是init而这个init十有八九就是BusyBox提供的。我在实际项目里见过不少板子整个用户态加起来就一个busybox二进制、两个so库、几个脚本照样跑得飞起。注意BusyBox本身不包含C库它必须链接到某个C库上运行。常见选择是uclibc和musl体积小非常适合嵌入式而glibc功能全但体积大。编译时选对库后面能少掉一大堆麻烦。2. 动手前先搞懂几个底层机制2.1 init进程、/sbin/init与内核的约定不搞清楚init和内核的关系后面折腾根文件系统会像没头苍蝇一样。Linux内核在完成自解压、初始化硬件、挂载根文件系统之后会尝试执行第一个用户态程序。这个路径在编译内核时用CONFIG_DEFAULT_INIT指定也可以在启动参数里通过init覆盖。如果都没设置内核按顺序寻找这几个路径/sbin/init、/etc/init、/bin/init、/bin/sh。如果这四个都没找到内核就会报出著名的Kernel panic - not syncing: No init found. Try passing init option to kernel.。这个错误我见得太多了后面专门讲排查。简单说根文件系统里只要存在一个/sbin/init内核就能把它拉起来。而BusyBox装完之后它的/sbin/init就是指向busybox的软链接。之后内核会以PID 1运行sbin/init并为其提供根目录、标准输入输出等基本环境。init进程负责用户态的一切后续初始化挂载proc、tmpfs、sysfs配置网络启动各类服务。BusyBox的init会按顺序做这几件事读取/etc/inittab如果没有使用默认配置。执行系统初始化脚本通常是/etc/init.d/rcS可在inittab里指定。根据inittab中的条目启动各个终端上的getty或shell。对孤儿进程进行收养和回收。这玩意儿很简单但足够稳定。很多国产开发板教程里的/etc/inittab都是BusyBox格式学一次以后看谁的板子都能看懂。2.2 根文件系统的基本骨架/dev /proc /sys /etc /bin /sbin /usr要做一个能启动的根文件系统不需要拷贝整个发行版也不需要成千上万个文件。只需要保证下面这些目录存在内容可以很精简/bin存放用户基本命令。BusyBox安装时通常会把sh、ls、cp、mount等放到这里。/sbin系统管理命令。init、mdev、ifconfig等会放在这里。/usr按习惯放用户程序和库。嵌入式系统里/usr/bin和/usr/sbin也常用。/etc配置文件目录inittab、fstab、profile、init.d/rcS都在这。/dev设备节点目录。用mdev时先放一个console节点和一个null节点即可。/proc内核虚拟文件系统挂载点。/syssysfs挂载点mdev通过它来动态生成设备节点。/lib动态库目录。如果是动态编译的busybox必须把C库so文件放这里。/tmp临时文件目录通常用tmpfs。/mnt挂载点比如光驱、U盘、网络文件系统挂这里。/rootroot用户的家目录。实际构建时我习惯先用mkdir把所有目录建好再安装BusyBox。这个顺序看起来啰嗦但能避免后面补目录时权限错乱。提示根文件系统里可以没有/usr但很多工具和库默认路径基于/usr所以还是建议把标准目录结构搭完整。对你后面移植其他软件会省很多事。2.3 BusyBox的编译配置与选项编译BusyBox不是直接./configure make它使用的是类Kconfig的开关系统。源码根目录有个Makefile配置命令是make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- menuconfig如果你不想用菜单可以直接拿默认配置改make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- defconfig默认配置会启用大部分功能对初学者友好。真正做量产固件时我习惯把不需要的命令裁掉。menuconfig的界面跟Linux内核一模一样能勾选各个applet。需要特别关注的选项有Busybox Settings - Build Options - Build BusyBox as a static binary (no shared libs)如果选Y出来的busybox不依赖动态库拷贝到哪个板子都能跑体积稍大。对于早期调试静态编译能省掉“lib缺失”的一堆破事。Busybox Settings - Installation Options - Destination path for make install设置安装路径默认是_install目录。Busybox Settings - Busybox Library Tuning这里可以调一些行为细节比如终端宽度、用户名长度等。Coreutils、Shells、Init Utilities等大类下按需勾选具体命令。网上一些教程建议直接静态编译因为简单。但要注意静态编译出的busybox对DNS解析、用户管理这些依赖NSSName Service Switch的功能可能会有兼容问题。比如静态编译的ping无法解析域名因为getaddrinfo内部不再加载libnss_dns.so。我建议主程序静态编译但如果你要做完整系统且依赖DNS就最好是动态编译把库文件放进根文件系统。我自己的习惯是调试阶段先静态编译让系统先跑起来验证完所有功能后再改回动态编译做瘦身。这样分两个阶段排查问题容易得多。3. 构建一个能跑的最小根文件系统3.1 准备交叉编译环境与下载BusyBox源码目标板架构不同需要用的交叉编译链也不同。比如树莓派用arm-linux-gnueabihfAArch64用aarch64-linux-gnu某些开发板用arm-buildroot-linux-gnueabihf。关键要和你的内核、BootLoader匹配。如果不知道用什么最稳妥的办法是直接用Buildroot它会把交叉编译链和BusyBox一起打包生成根文件系统镜像还带编译缓存很适合不想造轮子的场景。如果想手动编译先下载BusyBox源码。老项目里经常看到busybox 1.22.1 (kylin1:1.22.0)这种版本号那是发行版打过补丁的。我自己更喜欢在busybox.net下载官方release版本1.30到1.36都比较新配置项更完善。不过1.22也足够跑基础系统版本差异主要体现在新命令支持和bug修复上。对你来说选一个稳定的版本比追新更重要。假设交叉编译链已经安装好并且arm-linux-gnueabihf-gcc在PATH里。接下来解压源码tar xjf busybox-1.36.1.tar.bz2 cd busybox-1.36.1然后执行配置。注意必须指定ARCH和CROSS_COMPILE否则会用x86的gcc编译板子上跑不了。make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- defconfig make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- menuconfig在menuconfig里我把静态编译打开Settings - Build Options - Build static binary其他暂时不动。你如果想自己定义配置记得保存为.config文件方便以后用脚本重复构建。3.2 配置、编译、安装三步走配置好之后编译安装一气呵成make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- -j4 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- install CONFIG_PREFIX/path/to/rootfs注意第二个命令里我指定了CONFIG_PREFIX也可以在前面menuconfig里设置安装路径。BusyBox安装时做的事情很直接把所有启用的applet的软链接或硬链接创建到目标目录下同时把busybox这个二进制本身拷贝到bin/busybox。安装完成后去目标目录里看看ls -l bin/你会看到一堆指向busybox的软链接。如果用了硬链接那就是所有文件都指向同一个inode也差不多。跑到这里BusyBox部分已经完成接下来是纯手工搭建环境。注意如果CONFIG_PREFIX指定的目录不存在BusyBox不会自动创建先用mkdir -p建好。另外安装完之后别手动删掉busybox否则所有软链接都失效。3.3 手工搭建目录结构和设备节点我习惯先把一套最小目录建出来ROOTFS/tmp/rootfs mkdir -p $ROOTFS/{bin,sbin,usr,lib,etc/init.d,dev,proc,sys,tmp,mnt,root} chmod 1777 $ROOTFS/tmpbin/sbin/usr放命令的地方lib放动态库etc放配置。tmp目录必须给1777权限让任何用户都能创建临时文件否则很多程序运行时会报错。接着创建必要的设备节点。这里有个先有鸡还是先有蛋的问题根文件系统刚挂载时内核需要至少一个/dev/console才能把控制台交给init。所以不管你有没有mdev这两个节点必须提前用mknod建好sudo mknod -m 600 $ROOTFS/dev/console c 5 1 sudo mknod -m 666 $ROOTFS/dev/null c 1 3console是内核控制台设备主设备号5次设备号1权限600。null是黑洞设备主设备号1次设备号3权限666。其他设备节点不用手动建等内核起来后mdev会扫描sysfs动态创建。如果你用的是devtmpfs甚至不需要mdev但很多嵌入式内核没开CONFIG_DEVTMPFS所以还是老老实实用mdev吧。然后安装库。如果BusyBox是动态编译用file bin/busybox看一下它依赖哪些so文件$ file bin/busybox bin/busybox: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3把交叉编译链里的对应库复制到$ROOTFS/libSYSROOT$(arm-linux-gnueabihf-gcc -print-sysroot) cp -a $SYSROOT/lib/*.so* $ROOTFS/lib/不要复制所有库到根目录但为了省事我一般先把lib/arm-linux-gnueabihf下所有so文件拷过去后面再按体积裁减。3.4 写一个可用的init和配置文件根文件系统能启动核心是配置文件要写对。第一个文件是/etc/inittab。BusyBox init的默认格式如下::sysinit:/etc/init.d/rcS ::respawn:/sbin/getty -L ttyS0 115200 vt100 ::ctrlaltdel:/sbin/reboot ::shutdown:/bin/umount -a -r这里每一行有4个字段用冒号分隔id:runlevel:action:process。BusyBox对id和runlevel处理得比较随意但action字段很关键sysinit系统初始化时只执行一次等待结束后才继续。respawn进程退出后自动重启适用于getty或shell。askfirst类似respawn但运行前会在控制台上提示“Please press Enter to activate this console”。ctrlaltdel按CtrlAltDel时执行的命令。shutdown系统关机时执行。串口名要根据你开发板实际类型写。很多ARM板用的是ttyS0或ttyAMA0还有一些模拟器用ttySAC0。如果你用的是USB转串口可能对应ttyUSB0但这是驱动注册的不一定会出现在文件系统里。具体的要查你的内核串口驱动。然后写/etc/fstabproc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 tmpfs /tmp tmpfs defaults 0 0 devpts /dev/pts devpts defaults 0 0有了fstab启动脚本里就可以用mount -a一次挂全部。接着写初始化脚本/etc/init.d/rcS#!/bin/sh mount -a mkdir -p /dev/pts mount -t devpts devpts /dev/pts echo /sbin/mdev /proc/sys/kernel/hotplug mdev -s ifconfig lo 127.0.0.1 up ifconfig eth0 192.168.1.100 netmask 255.255.255.0 up /etc/init.d/rc.local这个脚本算是传统写法。mount -a读取fstab挂载proc/sys/tmpfs。mdev -s扫描/dev目录对应的sysfs信息自动生成设备节点。ifconfig配置网络按你自己局域网调整。最后如果有/etc/init.d/rc.local调用它来放用户自启动命令。别忘了给rcS加执行权限chmod x $ROOTFS/etc/init.d/rcS再写一个/etc/profile这样登录进去的shell会有基本环境变量export PATH/bin:/sbin:/usr/bin:/usr/sbin export HOSTNAMEembedded export PS1[\u\h:\w]\$ alias llls -l最后用/bin/sh启动shell时如果根目录下没有/etc/passwd和/etc/groupBusyBox会给出大量警告。简单创建两个最小文件root:x:0:0:root:/root:/bin/sh这样init起来后就能稳定工作。到这里一个基础根文件系统的雏形已经出来了。3.5 用NFS挂载根文件系统快速验证调试阶段我最推荐用NFS挂载根文件系统而不是每次把SD卡拔下来拷贝。原理是在开发板上通过U-Boot设置内核启动参数让内核从网络挂载NFS目录作为根。这样你在电脑上改了文件系统板子重启就生效开发效率能提升一个量级。首先在开发机上配置NFS服务。以Ubuntu为例安装nfs-kernel-server然后编辑/etc/exports加上/tmp/rootfs *(rw,sync,no_root_squash,no_subtree_check)no_root_squash必须加否则NFS客户端以root身份操作时会被映射成nobody权限乱掉。然后sudo exportfs -ra接着给U-Boot设置启动参数。假设你的板子IP是192.168.1.100开发机IP是192.168.1.10setenv bootargs consolettyS0,115200 root/dev/nfs nfsroot192.168.1.10:/tmp/rootfs,v3 ip192.168.1.100:192.168.1.10:192.168.1.1:255.255.255.0::eth0:off rw init/sbin/init saveenv boot内核里的NFS相关配置要打开比如CONFIG_ROOT_NFS、CONFIG_NFS_V3。启动过程中内核会先通过DHCP或静态IP配置网络再挂载NFS目录。当你在串口看到[ 0.000000] VFS: Mounted root (nfs filesystem) on device 0:17时说明NFS挂载成功了。这时候root权限是NFS客户端文件读写会经过vfs和网络协议栈。最容易出的问题是开发板NFS权限不够表现为Permission denied。九成原因是开发机/etc/exports没加no_root_squash或者目录权限不是777。还有就是NFS版本不匹配某些老内核只支持NFSv3开发机默认只开NFSv4那就在nfsroot后面明确加,v3。3.6 打包成镜像文件刷入目标板NFS调试通过后最终要往板子的Flash或SD卡里写镜像。根文件系统常用格式是ext4、jffs2、ubifs或squashfs。我拿SD卡启动的板子举例做ext4根文件系统镜像dd if/dev/zero ofrootfs.ext4 bs1M count64 mkfs.ext4 -F rootfs.ext4 mkdir /mnt/rootfs sudo mount -o loop rootfs.ext4 /mnt/rootfs sudo cp -a $ROOTFS/* /mnt/rootfs/ sudo umount /mnt/rootfs这样生成一个64MB的ext4镜像烧写时用dd写到SD卡分区或者用fastboot刷到flash分区。要注意的是如果你用了动态BusyBox库检查尤其重要可以先chroot到挂载目录里测试一下sudo chroot /mnt/rootfs /bin/busybox --listchroot能模拟目标环境但不能模拟设备节点所以dev下没有console可能会报错。如果busybox能列出applet列表说明动态库找得到。提示squashfs是只读文件系统适合根文件系统可以压缩体积。但如果你需要运行时修改某些文件需要用tmpfs覆盖可写目录。这就是为什么很多系统/etc用了overlay或tmpfs。4. 集成Dropbear SSHD让板子能连上去调试4.1 为什么要SSH而不是telnet板子起来之后调试方式通常还是串口。但串口有诸多不便距离受限、多用户没法同时操作、传输文件麻烦。telnet虽然简单但密码和传输数据全是明文在实验网里还好到了项目环境很容易被诟病。所以很多嵌入式Linux项目会集成dropbear——一个轻量级SSH服务器。它比OpenSSH体积小得多功能却足够日常使用。Dropbear和BusyBox是绝配BusyBox提供基础命令和shellDropbear提供加密的远程登录通道。实际项目中集成dropbear最简单的方式是用Buildroot它会自动交叉编译并部署dropbear。这里我讲一下手动集成的过程让你知道背后发生了什么。4.2 Dropbear的交叉编译与部署从dropbear官网下载源码解压后交叉编译tar xzf dropbear-2022.83.tar.gz cd dropbear-2022.83 ./configure --hostarm-linux-gnueabihf --disable-zlib CCarm-linux-gnueabihf-gcc make PROGRAMSdropbear dropbearkey dropbearconvert scp--disable-zlib是为了省掉libz依赖自己少带一个库。make PROGRAMS...只编译服务端和密钥工具再加一个scp客户端。编译产物里dropbear是SSH服务端dropbearkey用来生成主机密钥dropbearconvert用来转换key格式。然后把两个二进制拷贝到根文件系统cp dropbear $ROOTFS/usr/sbin/ cp dropbearkey $ROOTFS/usr/bin/如果动态链接还需要检查依赖的so文件是否在rootfs里arm-linux-gnueabihf-readelf -d dropbear | grep NEEDED通常需要libc、libutil。把缺失的so库补到/lib里。4.3 自动生成主机密钥与开机启动配置SSH服务端工作前必须有主机密钥否则拒绝连接。密钥可以预先生成后放进根文件系统但更稳妥的做法是在开机脚本里检查密钥是否存在不存在就生成。这样同一个根文件系统可以刷到不同板子上不用为每块板子单独定制。在/etc/init.d/rcS或者在独立的rc.local里加一段if [ ! -f /etc/dropbear/dropbear_rsa_host_key ]; then mkdir -p /etc/dropbear /usr/bin/dropbearkey -t rsa -s 2048 -f /etc/dropbear/dropbear_rsa_host_key fi if [ ! -f /etc/dropbear/dropbear_ed25519_host_key ]; then /usr/bin/dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key fi /usr/sbin/dropbear -p 22注意密钥文件目录权限要设置为只有root可读写否则dropbear会警告并拒绝使用。我一般mkdir -p $ROOTFS/etc/dropbear chmod 700 $ROOTFS/etc/dropbear如果板子flash空间有限不想存大量密钥每次开机重新生成也行。但会导致每次连接时主机密钥都变客户端的known_hosts会报警。我建议还是存下来一个2048位的RSA密钥就几KB占不了多少空间。注意Dropbear默认只监听0.0.0.0如果板子有外网接口安全隐患很大。我一般在dropbear启动参数上加-B禁止空密码登录再在防火墙层限制22端口来源。5. 常见问题排查与避坑技巧实录5.1 Kernel panic - not syncing: No init found这个报错是每个做根文件系统的人必须跨过的坎。出现它说明内核找不到可执行的init。原因通常有这几种根文件系统根本没挂载成功。比如ext4格式不对、分区表不对内核报的是VFS: Cannot open root device你需要检查bootargs里的root和rootfstype。根文件系统挂上了但/sbin/init不存在。检查你是否真的安装了BusyBox以及/sbin/init是不是软链接。init文件存在但它需要的动态库缺失。比如你动态编译了busybox但没把/lib/ld-linux-armhf.so.3放进去。这种情况下内核会报Kernel panic - not syncing: No init found或Requesting system reboot后面跟着一条找不到解释器或共享库的错误。init文件没有执行权限。检查一下ls -l /sbin/init权限至少要755。排查时最简单的是先用静态编译的busybox把系统拉起来排除库依赖问题。然后再换成动态编译的把库补齐。这样能快速定位是“文件系统本身坏了”还是“库少了”。5.2 /bin/sh: not found或cant access tty; job control turned off启动过程中如果出现sh: cant access tty; job control turned off这通常不是致命错误但说明shell启动后无法获得一个真正的控制终端。最可能的原因是你的/etc/inittab里用了::respawn:/bin/sh但没有指定正确的tty设备。在BusyBox里inittab的id字段可以指定tty。比如ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100如果你用-L参数getty会打开指定串口。如果串口名写错了或者启动参数里console和inittab不一致shell就找不到tty。一个取巧的办法是改用::askfirst:/bin/shBusyBox会自己选择当前控制台并且等你按回车。这样能保证shell起来虽然日志里可能还是会有tty警告。还有一种情况console指定的是ttyS0但内核编译时对应的串口驱动模块没加载或者编译成了模块而根文件系统里没有。这种情况下你先看串口能不能看到内核日志能看到就说明console参数至少生效了。5.3 设备节点都是问号或 /dev/mtdblock不存在挂载了根文件系统后ls -l /dev里看到一堆问号或者某些节点消失说明mdev没有正常工作。mdev依赖sysfs你得保证/sys挂载成功。如果rcS里没有执行mount -a那/sys是空的mdev扫不到任何设备。另外内核必须启用CONFIG_DEVTMPFS或者CONFIG_HOTPLUG。用mdev时还需要设置热插拔事件代理echo /sbin/mdev /proc/sys/kernel/hotplug mdev -s如果你坚持不使用mdev那得手动mknod但这在设备数目多的板子上不现实。所以还是老老实实把/sys挂好把hotplug设好。5.4 动态链接的busybox在目标板上找不到库这个问题我在前面提过但值得单独再说一次。动态编译的busybox依赖C库有些C库还有多层依赖。比如glibc的libnss_*DNS解析时会动态加载。如果只看ldd可能不是全部。最保险的办法是先在开发机上把busybox拷贝到rootfs里然后chroot到rootfs里执行sudo chroot $ROOTFS /bin/busybox如果chroot环境下能运行并输出usage说明库找得到。注意chroot之前先复制/etc/resolv.conf可能也没用但至少能测出基础库缺失。如果还缺但你又不想一个个试可以用这份分析命令arm-linux-gnueabihf-readelf -d busybox | grep NEEDED把列出的每个Shared library在交叉编译链的sysroot里找到拷贝对应so文件到$ROOTFS/lib。记得检查so本身是不是符号链接如果是连目标文件一起拷用cp -a。5.5 文件系统只读、sync丢数据与VFS的关系嵌入式设备突然断电后根文件系统里数据丢失或损坏太常见了。这背后是Linux的VFS虚拟文件系统缓存机制。你在应用程序里write()数据数据是先进页缓存的由内核的pdflush现代内核是writeback线程在某个时机再写回物理设备。如果断电发生在写回之前缓存里的数据就没了。sync命令和sync()系统调用就是强制把缓存数据刷到存储介质保证断电前数据落盘。很多根文件系统本身也是日志文件系统比如ext4、jffs2、ubifs它们有自己的断电恢复机制。但如果你用的是squashfs这种只读文件系统SD卡里的分区只读数据则放在另一个可写分区那就要在应用层做好“写完数据后sync”的习惯。特别是调试阶段经常发现程序printf的数据没保存重启后老日志还在就是因为没有sync。VFS还影响NFS根文件系统的性能。sync选项会导致每次write()都同步到NFS服务器速度很慢async则先把数据发到本地缓存再由内核写回。但对于调试sync更安全反正速度快慢无所谓。如果挂载时用了async每次改完文件系统后记得sync否则丢数据。5.6 集成dropbear常见坑密钥权限和主机密钥格式dropbear在嵌入式设备上跑起来通常会碰几个问题启动后连接被拒绝日志显示Bad owner or permissions on /etc/dropbear。这就是权限问题。检查目录权限是不是小于等于700文件权限是不是600。日志显示Failed to load key。可能是dropbearkey生成的主机密钥格式不对或者路径写错。生成RSA时用dropbearkey -t rsa -s 2048生成后文件就是dropbear格式不要用OpenSSH的key直接放进去。如果你确实有OpenSSH格式的key可以用dropbearconvert转换。连接时提示Host key verification failed。这是客户端缓存了你板子之前的主机密钥但板子密钥变化了。在OpenSSH客户端执行ssh-keygen -R 板子IP清除旧记录就好。板子没有/dev/urandom导致dropbear启动卡住或者密钥生成慢。Dropbear生成随机数依赖/dev/urandom如果没提前创建这个设备节点或者内核没启用相关驱动dropbear会长时间阻塞。解决方案是确保根文件系统里/dev/urandom和/dev/random节点存在主设备号1次设备号8和9。用mdev自动创建就行。最后再分享一个实用小技巧我每次在Module上做根文件系统都会在/etc/init.d/rcS的最后加上这样一段echo rootfs boot ok cat /proc/cmdline这样一旦系统启动到用户态串口会立即打印出当前内核的启动参数方便确认bootargs有没有生效。千万别小看这个习惯很多问题排查到后面就是因为bootargs里多了个奇怪的参数或者root路径写错了。调试阶段所有信息都摆在眼前比猜快得多。BusyBox这门手艺说到底是“把复杂系统压缩到极简”的功夫。把原理吃透把根文件系统结构记熟后面不管是调驱动、优化启动时间还是移植新软件都会顺手很多。希望这篇东西能帮你少踩几个坑顺利把板子跑起来。
返回列表