ARTICLE DETAIL

资讯详情

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

嵌入式Linux入门:从裸机到命令行,开发者必须掌握的实用命令与调试技巧

嵌入式Linux入门:从裸机到命令行,开发者必须掌握的实用命令与调试技巧 从单片机裸机开发转向嵌入式Linux第一道坎往往不是C语言也不是中断、寄存器这些老熟人而是那个黑乎乎的终端界面。串口工具连上开发板光标停在#符号前面你突然发现自己连“看看目录里有什么”都做不到更别说调试驱动、挂载文件系统了。这篇东西就围绕嵌入式Linux学习中最该先打牢的命令基础展开讲清楚哪些命令必须烂熟于心、哪些坑是新手必踩、以及怎么把这些命令和实际的嵌入式开发场景串联起来。1. 先搞清楚嵌入式Linux的命令和普通Linux有什么区别很多新手有个误区觉得嵌入式Linux命令和桌面Linux命令是两套东西。实际上命令本身完全一样区别在于你使用它们的场景和目的。桌面Linux上你敲ls可能只是为了找文件但在嵌入式开发板上敲ls更常见的目的是确认某个驱动节点有没有生成、某个应用有没有被正确拷贝到rootfs里、某个交叉编译出来的库文件权限对不对。命令还是那条命令但你的关注点完全变了。另外嵌入式的硬件资源很有限很多在桌面上“理所当然”的东西在板子上会让你很痛苦。比如桌面Linux有完整的man手册板子上往往只有精简过的帮助信息桌面Linux可以apt install装东西板子上通常没有包管理器一切都要自己交叉编译然后拷贝过去。这就意味着你不能只靠“用啥查啥”的学习方式得把高频命令练成肌肉记忆。我把嵌入式开发中真正高频的命令分成几类文件与目录操作、进程与系统状态、网络与文件传输、文本处理、权限与链接。后面每一类我都会结合实际开发场景展开。2. 文件与目录操作这块基石打不牢后面全是坑2.1 ls、cd、pwd的组合是你在板子上的“眼睛”ls是你进入一个陌生系统后第一个该敲的命令。嵌入式开发里我几乎每天都会用它确认几件事驱动加载后/dev下有没有生成对应的设备节点交叉编译好的可执行文件有没有被正确拷贝到文件系统的目标目录rootfs目录结构是否完整尤其是/lib、/usr/lib这些运行库目录有个很容易被忽略的细节ls -l输出的第一列第一个字符是d代表目录-代表普通文件l代表软链接。嵌入式开发里你会经常看到/dev下的设备节点显示为c或b分别代表字符设备和块设备。刚接触时不用死记但要形成条件反射——看到c知道这是字符设备看到b知道这是块设备看到s也别慌那是socket文件。cd和pwd没什么好讲的但有一个嵌入式场景要提一下很多开发板的默认shell提示符会显示当前路径比如rootboard:/home#。如果你的板子改过PS1环境变量提示符不显示路径了记得用pwd确认自己在哪里。我有一次就是在交叉编译环境的目录里用相对路径执行了一个脚本结果日志全写到错误的地方排查了半天才发现是工作目录不对。2.2 删除和移动文件嵌入式环境里这些操作要格外小心rm和mv是危险系数很高的命令。嵌入式板子存储空间有限又没有回收站删错了只能重新烧录系统或者重新拷贝。新手最容易犯的错误是在根文件系统目录下用了带通配符的删除命令把不该删的库文件删了系统直接启动到一半就挂掉。比如你本来想删掉/tmp下的临时文件敲成了rm -rf /tmp /usr/lib中间多了一个空格结果整个/usr/lib目录被清空。这种事故我在刚入行的时候差点犯过一次从那以后定了两条规矩删除之前永远先ls确认一下通配符匹配到了哪些文件杜绝在root权限下使用rm -rf配合变量或通配符的组合除非你明确知道自己在干什么mv看起来比rm安全但嵌入式环境里有一个坑当你把一个文件从普通分区移动到另一个分区比如从/tmptmpfs移动到/dataflash分区mv实际上会先拷贝再删除源文件。如果两个分区空间都不宽裕拷贝过程可能失败导致源文件还在但目标文件不完整。跨文件系统移动文件我建议直接用cprm分两步做至少能看清是哪一步出了问题。2.3 打包与解压嵌入式开发中的日常操作嵌入式开发里tar是你和rootfs、驱动模块、应用源码打交道时最常用的命令之一。交叉编译完一个应用要拷贝到板子上通常的做法是把整个目录打包传输而不是散着传几十个文件。基本用法不多说重点讲两个实际场景。第一个是解压到指定目录开发中你的压缩包可能在/home下但你希望解压到/opt/app这时候tar -xvf xxx.tar.gz -C /opt/app是标准操作别先解压再移动多此一举还容易出错。第二个场景是排除某些文件不打包。比如你要把整个应用目录打包拷去板子但.git目录和build目录都不要可以用tar -czvf app.tar.gz --exclude.git --excludebuild app/。这条命令我今天还在用比先删再打包安全得多。还有一点要记住解压.tar.gz用zxf参数解压.tar.bz2用jxf参数。现在很多新工具链和rootfs包都是.tar.xz格式对应的是Jxf。参数记错了不会报错但会提示“无法识别文件格式”白白浪费时间。3. 权限与软链接嵌入式系统启动和运行的隐形命脉3.1 chmod不只是数字权限还要注意执行权限的缺失嵌入式板子上最经典的启动问题是“我明明把app拷贝到rootfs了为什么开机不执行”十有八九是执行权限丢失了。从Windows共享目录或者某些FTP工具拷贝文件到Linux系统文件权限经常会被重置为-rw-r--r--也就是没有x执行权限。这时候你得手动补上chmod x app。新手常问我知道chmod 777但为什么不行不是不行而是低权限原则问题。嵌入式系统里跑的应用能用chmod 755所有者可读写执行组和其他用户只读执行就不要用chmod 777。尤其是一些以root身份运行的系统服务权限过宽在出安全问题的时候会放大风险。虽然嵌入式设备不像服务器那样暴露在公网但现在很多物联网设备都被扫过权限习惯从入门就养好后面做产品化会省心很多。3.2 chown与软链接驱动开发中的权限和链接问题chown在嵌入式调试中也是高频命令。比如某个设备节点被创建为root:root但你的应用是以普通用户身份跑的访问/dev/xxx时就会 Permission denied。这时候你会用到chown或chmod具体用哪个取决于设备节点的要求。软链接在嵌入式系统里更是无处不在。/dev下很多设备节点就是软链接比如/dev/sd类设备在不同内核版本下命名会变化用软链接固定到一个稳定路径是常见的做法。还有/lib下的.so库文件经常有libxxx.so - libxxx.so.1.2这样的软链接链这个设计是有讲究的程序编译时链接的是libxxx.so运行时加载器会顺着软链接找到真实版本文件。如果你在拷贝库文件时漏掉了软链接本身比如用了不带d参数的cp程序启动时会报找不到libxxx.so这种情况在交叉编译开发和板子部署之间来回折腾时特别常见。我建议在板子上排查这类问题时养成先看ls -l确认链接关系的习惯别一看到“找不到库”就着急重编程序。4. 进程与系统状态查看让板子告诉你它到底怎么了4.1 ps和top别把桌面思维带到嵌入式环境嵌入式设备上ps和top是排查“为什么系统卡死”“这个进程到底起没起来”的第一工具。但要注意嵌入式BusyBox提供的ps和完整版Linux的ps参数不同。很多从桌面Linux转过来的新手习惯敲ps aux在BusyBox上也能用但输出的内容字段会少一些。BusyBox的ps默认输出PID、USER、VSZ、STAT、COMMAND够排查基本问题了。如果你想看线程信息ps -T可以列出线程列表这在定位多线程程序死锁时很有用。top在嵌入式板子上要小心使用因为板子本身性能弱top又是个动态刷新的命令比较费CPU。我一般用top -b -n 1来获取一次性静态快照相当于“拍一张照片”而不是让界面持续刷新。这在远程调试时也更好用输出不会乱跳。还有一点很多新手看top只关注CPU和内存使用率忽略了load average这个负载均值。嵌入式设备如果load average长期大于1说明系统一直处于高负载状态这可能是驱动死循环、应用疯狂刷日志或者内核线程异常导致的。4.2 dmesg和日志查看内核打印是你的第一调试信息来源dmesg是嵌入式开发里我敲得最多的命令之一没有悬念。驱动加载成功或失败、设备树解析错误、内存分配失败、文件系统挂载错误这些信息都会打印在内核环形缓冲区里。板子上电后dmesg | grep是定位问题的标准动作。新手最容易犯的错误是程序跑起来后崩溃了然后在整个应用代码里翻逻辑错误却不知道内核可能早就把“segment fault”的上下文打印在dmesg里了。尤其是驱动的printk信息默认输出级别不一样有些需要调整内核打印级别才能看到。BusyBox环境下你可以用dmesg -n 8把打印级别临时调到最高但要注意它只影响当前运行时的printk级别不会持久化。日志文件方面嵌入式系统通常没有完整syslog服务很多日志直接输出到/var/log/messages或者被init系统接管。如果你用了systemd现在很多新平台确实在用那journalctl也可能派上用场。但很多轻量级嵌入式平台还是用BusyBox init日志管理方式各不相同所以我建议到了一个新板子先花十分钟摸清它的日志机制再开始调程序能省半天时间。4.3 kill和后台运行进程管理的日常操作嵌入式开发里终止一个进程最常用的当然是kill [PID]。有两个细节值得注意kill -9是最后手段不是默认手段。kill -9直接让内核杀掉进程进程完全没有机会做清理工作可能留下设备节点被占用、文件锁未释放等问题。我见过多次因为滥用kill -9导致重启后设备异常、需要手动清理残留状态的案例。killall [进程名]比kill更方便尤其当你有多个相同名字的进程实例时。但BusyBox的killall是精简版不支持-u这类参数用之前killall --help确认一下就好。后台运行命令和nohup也是基本功。嵌入式设备上调试时如果你通过串口或SSH启动一个程序然后断开连接程序往往会被挂断信号杀掉。用nohup app 可以免疫挂断信号这在远程调试时特别重要。还有一个更进阶的用法是setsid app来完全脱离会话这个在把程序放养成后台守护进程时更规范但我个人在开发调试阶段用nohup ... 就足够了。5. 网络与文件传输开发板和主机之间的生命线5.1 网络排查三板斧ifconfig、ping、route嵌入式开发板连不上主机、连不上路由器这是每个嵌入式工程师都会遇到的问题。排查顺序很重要我的习惯如下ifconfig看网卡是否起来了有没有拿到IP地址如果网卡没起来检查驱动和设备树ping 网关IP确认二层和三层通路ping 主机IP确认和目标主机的连通性route -n查路由表是否正确有一个很隐蔽的问题板子和主机在同一网段但板子没有设置默认网关导致板子能ping通主机却访问不了主机所在的其他网段服务。比如板子通过NFS挂载主机文件系统NFS服务在另一个VLAN上这时候路由表重要性就体现出来了。另外嵌入式板子经常出现DNS配置问题。很多应用要解析域名比如云平台连接但板子上/etc/resolv.conf为空或指向了无效DNS应用就会一直超时。排查时cat /etc/resolv.conf应该是顺手动作。5.2 NFS挂载根文件系统或应用目录NFS网络文件系统是嵌入式开发中最常用的工具之一。它让你不用每次编完程序都手动拷贝到板子上而是直接在主机上用make编译板子上通过NFS挂载主机目录实现“编辑即运行”的效果。这也是很多新手第一次接触“挂载”概念的场景而“挂载”就是嵌入式文件系统里的核心操作。板子端挂载命令很简单比如mount -t nfs -o nolock 192.168.1.100:/home/user/boardfs /mnt/nfs注意nolock参数。嵌入式板子上的NFS客户端通常没有完整的lockd支持不加这个参数挂载时会报错或卡住。网上的很多教程会写-o nolock,tcp实际使用中tcp参数按需加有些老内核还不支持。挂载失败的时候先看dmesg有没有NFS相关错误再确认主机端的NFS服务有没有开、导出的路径对不对、权限够不够。我曾经纠结了半天发现主机端NFS导出目录的配置写错了路径这类低级错误用showmount -e 主机IP一秒就能查出来但新手往往不知道这个命令。NFS还可以挂载整棵根文件系统也就是网上常说的“NFS根文件系统启动”。这个方案在调试阶段非常实用因为你可以直接在主机目录里改rootfs板子重启后自动加载最新内容不用反复烧写flash。不过配置过程涉及bootargs传递U-Boot环境变量里的root/dev/nfs参数要配套设置这部分等学到系统移植阶段再去深入也不迟。5.3 串口、scp与TFTP三种传输方式的适用场景嵌入式开发里传输文件有几种常用方式各有利弊。我把它们的典型使用场景整理一下传输方式适用场景注意事项串口ZModem协议文件不大、环境最简单速度慢用rz/sz或lrz/lszTFTPU-Boot阶段加载镜像无认证仅限调试网络scp基于SSH适合板子已有Linux系统依赖SSH服务端镜像要带dropbear或openssh很多老工程师还是习惯串口传文件因为串口是嵌入式开发中最稳定的通道网络没配好时也能用。在U-Boot引导阶段TFTP几乎是标准操作tftp 0x20000000 uImage这类命令每个做启动移植的人都熟悉但注意TFTP是基于UDP的跨网段传输容易丢包最好在局域网内用。板子已经有系统后我更喜欢scp。开SSH服务、配置好IPscp app user板子IP:/opt/app就能传文件还能免密配置公钥。但嵌入式Linux的镜像通常会做精简很多出厂系统不会带SSH服务端你需要确认有没有没有就换其他方式。6. vim与基本文本处理在板子上改配置的必备技能6.1 vim的最小可用配置和常用命令很多新手问“有必要在嵌入式环境中专门学vim吗”我的回答是有必要但不需要学到精通。嵌入式开发中你需要在命令行改文件的地方非常多比如/etc/fstab加一行挂载、/etc/network/interfaces改IP、/etc/init.d脚本调启动顺序在终端里改这些文件vim几乎不可避免。不清楚vim的三种模式就会卡在做最基本的事情上不知道按i才能输入文字、不知道按Esc才能退出编辑、不知道:wq保存退出。这个入门成本并不高花一个小时练一下就能形成肌肉记忆。我建议新手的vim学习路线是先能改文件保存退出再学文本搜索/关键词、行跳转:行号、复制粘贴yy、p然后再去了解块操作、宏这些高级功能。有一个特别实用的小技巧是vim的set nu显示行号。排查脚本语法错误时报错信息会给你行号没有行号显示你会非常痛苦。让我给新手指一个方向把下面这几行加到~/.vimrc里就能舒服很多set nu set tabstop4 set shiftwidth4 syntax on嵌入式环境里还有一个特殊情况BusyBox自带的是vi而不是完整vim两者核心操作一致但有些增强功能没有。如果你在某个板子上发现vim命令报错检查一下是不是用vi替代就好了。6.2 grep、awk、sed三板斧在日志分析中的实战用法嵌入式开发中日志分析是重头戏。程序刷出几千行日志你要快速找出关键信息grep就是那个“从大海里捞针”的工具。grep -i忽略大小写、grep -n显示行号、grep -r递归搜索目录这三个参数是最常用的。调试时我经常用dmesg | grep -i error来过滤内核错误但注意error可能匹配到类似error_message这样的字符串必要时用grep -w error做整词匹配。awk和sed在嵌入式里用到的频次不如grep但有几个场景很值钱。比如从top输出里提取某个进程的CPU占用率用top -b -n 1 | grep app | awk {print $9}能直接拿到数字批量替换配置文件里的IP地址用sed -i s/192.168.1.100/192.168.1.101/g /etc/xxx.conf搞定。学awk和sed不要试图全盘掌握先把“提取第几列”和“文本替换”练熟后面用到再查。还有一个很多新手容易忽略的组合技巧管道|和重定向。日志太多不想刷屏时command 21 | tee /tmp/log.txt能把屏幕输出和文件保存同时做到只要错误输出的话command 2/tmp/error.txt只想丢弃不需要的输出时command /dev/null 21。理解这三个表达你分析问题的效率会有一个明显的提升。7. shell脚本基础把重复劳动交给机器7.1 一个嵌入式场景驱动的脚本骨架嵌入式开发中每天重复做的事情太多了交叉编译、打包rootfs、把固件传到开发板、启动测试程序。与其每次都手敲一串命令不如写一个脚本把流程固定下来。我给新人推荐的是一个“骨架级”的入门模板#!/bin/sh # 简易编译部署脚本 APPhello_app TARGET_IP192.168.1.100 ROOTFS_DIR/opt/rootfs echo 编译 arm-linux-gnueabihf-gcc -o $APP main.c || exit 1 echo 拷贝到rootfs cp $APP $ROOTFS_DIR/rootfs/usr/bin/ || exit 1 echo 通过NFS挂载或scp同步 scp $APP root$TARGET_IP:/usr/bin/ || exit 1 echo 完成这里体现的几个原则值得讲一讲脚本开头用#!/bin/sh而不是#!/bin/bash。虽然很多板子的shell是bash或者ash但用sh最保险兼容性最好。每条关键命令后加|| exit 1如果上一步失败了就直接退出避免“编译失败还继续拷贝旧文件”这种隐蔽问题。带过的一个新人就是栽在这个细节上调试时改了一行代码编译报错没注意结果板子上还在跑旧版本排查了好久。变量统一用小写或大写别混用写脚本跟写代码一个道理可读性优先。7.2 实际场景按键非阻塞扫描中的shell思路顺着开发场景多说一点。嵌入式开发中“按键非阻塞扫描”通常在应用层实现但有经验的工程师也会用shell或者系统层面的思维来辅助。比如通过读取/dev/input/eventX设备节点判断按键事件直接在命令行试一下cat /dev/input/event0按一下键看有没有数据输出这个动作可以快速确认驱动有没有正常工作。这种“先用命令行验证驱动再写应用逻辑”的工作顺序是嵌入式开发里效率最高的调试方式之一。很多新人拿到新板子驱动教程还没看完就想着写复杂应用结果跑不起来也不知道是驱动、设备树还是应用本身的问题。先用最基础的命令验证底层通路再用脚本或者小应用验证业务逻辑由浅入深才是合理的做法。另外脚本里如果要做循环监控某个进程是否退出可以用while true; do pidof app || break sleep 1 donepidof app取进程PID进程不在时返回非零值配合|| break跳出循环。如果你想写“等待某个进程退出后再执行下一步”这种写法很常见。注意BusyBox的pidof和完整版的输出格式可能略有不同多数情况下直接用没问题。7.3 开机启动脚本的常见写法嵌入式系统的开机启动是另一个绕不开的场景。不同init系统的写法不同但我给你一个通用的基本认知init.d脚本的思路都是“一个脚本内部通过case判断参数是start、stop还是restart”。一个典型的模板长这样#!/bin/sh case $1 in start) echo starting app... /usr/bin/hello_app ;; stop) killall hello_app ;; restart) $0 stop $0 start ;; *) echo Usage: $0 {start|stop|restart} exit 1 ;; esac这是嵌入式Linux里最常见的init脚本样式无论你用BusyBox init还是sysvinit风格理解了start|stop|restart这个模式后面遇到具体平台的启动细节再查就行。新版系统用systemd的话写法完全不同unit文件但你要是一上来没接触systemd先掌握传统的init脚本模式即可因为大量设备和教程还是旧式风格。8. 从入门到实战给嵌入式新手的命令学习路线与避坑清单8.1 一条可行的学习路线关于嵌入式Linux的整体学习路线我的看法是不要一上来就学“Linux命令大全”那是运维工程师的需求嵌入式工程师需要的是“带着开发任务学命令”。你要做的不是在桌面上玩转所有命令而是带着鲁莽一点的任务去驱动学习。给你一条实际参考过的路线按顺序走搭好环境Ubuntu虚拟机或Windows WSL、安装交叉编译工具链、装串口终端minicom/picocom/screen/mobaXterm用QEMU或者其他模拟器跑通一个小型Linux系统熟悉登录、文件操作、进程查看在开发板上烧录出厂系统用串口登录反复练习文件操作、网络配置、NFS挂载交叉编译一个hello world手动拷贝到板子上运行写一个控制LED的驱动模块用insmod动态加载用dmesg查看驱动打印用NFS挂载方式做应用迭代开发形成“主机编译、板子运行”的工作流遇到问题先看日志和状态命令dmesg、ps、top、cat /proc/xxx养成调试思维这套路线的核心逻辑是先用模拟器/开发板把命令基础打牢再用实际开发任务把命令技能转化成开发能力。命令本身是工具解决问题才是目的。8.2 高频面试命令题与考察点嵌入式Linux岗位面试命令题出现概率极高而且往往不是单纯考命令拼写而是考你“在什么场景下用什么命令”。我把高频考察点列一下ls -l输出的第一列每个字符含义文件类型、rwx权限chmod的数字表示法4读、2写、1执行以及chmod 755与chmod 754的区别如何查看系统当前运行的进程、如何强制终止一个进程dmesg在什么时候用、内核日志怎么看如何排查端口被占用netstat -anp或ss -tlnp如何查找一个命令对应的可执行文件路径which、whereisShell脚本里$0、$?、$#的含义NFS挂载的常用选项尤其nolock软链接与硬链接的区别以及ln -s的用法有几个问题值得单独展开说。比如“软链接和硬链接的区别”很多新手背了答案却不理解。软链接相当于Windows的快捷方式删掉原文件链接就失效硬链接是同一个文件的多个目录项删除其中一个其他入口依然有效。嵌入式里接触最多的是软链接因为文件系统类型和空间限制硬链接在一些文件系统上根本不被支持你在FAT格式的SD卡分区里就别想创建硬链接了。另一个高频考察点是“如何查看端口占用”。板子上排查某个服务起不来或者被其他进程抢占端口时netstat -anp | grep 8080和ss -tlnp都常见但BusyBox里的netstat参数有限可能没有-p选项需要结合lsof如果镜像里有或者fuser来判断。知道不同环境下的差异面试时反而能体现你的实战经验。8.3 嵌入式开发中常见的“命令事故”复盘接触过的很多新手几乎都会在下面这些场景里栽跟头我列出来希望你能绕开忘记先mount就操作另一个分区结果提示“Read-only file system”。很多出厂系统把根文件系统挂为只读或者某些分区默认没有挂载。先mount、再看/etc/fstab别一上来就mkdir。在开发板里没有/lib/ld-linux.so.3就去跑一个动态链接的arm应用提示“No such file or directory”。有些新手看到这个报错以为是文件不存在其实是动态链接器缺失或路径不对。用交叉编译工具链里的arm-linux-readelf -l app可以看到interpreter指向哪个链接器和板子上的路径对一下就能定位。交叉编译出来的库文件不匹配导致的version GLIBC_x.x not found。嵌入式板子上的libc版本通常比较老主机上编译时用了高版本glibc拷贝到板子上运行就会报这种错。解决办法是编译时用板子配套的工具链而不是随便在宿主机上交叉编译。在/tmp下写了一个大文件导致tmpfs内存占满系统直接卡死。tmpfs本质是占内存的文件系统你写一个几百MB的文件到/tmp内存就被吃掉了。开发板上df -h和free配合看产生大文件时注意确认挂载类型。这些事故都有一个共同特点不是命令本身用错了而是对系统机制理解不够。这也就回到文章开头说的问题——学命令学的从来不只是命令本身。8.4 最后一点建议把命令基础当作“习惯养成”而不是“背单词”学Linux命令和学一门自然语言很像。你在书上看一百遍ls -l怎么用不如在板子上亲手敲一遍。条件允许的话给自己准备一块开发板或者至少用QEMU模拟环境每天花半小时执行几个和开发场景相关的命令坚持两三周效果比一次性背完命令大全好得多。我自己带新人时的习惯是让他们把调试过程完全用命令行完成不依赖任何图形工具。刚开始会觉得低效但一两个项目下来命令行操作速度和问题定位能力都会有明显提升。嵌入式Linux开发的复杂度决定了你必须熟练和系统对话而命令行就是那个对话入口先把门敲开后面的路走起来才顺畅。
返回列表