第三章:Linux基础与Shell编程

说实话,很多刚入行的朋友问我:「做TBOX开发,Linux要学到什么程度?」我的回答很简单——你不需要成为内核专家,但命令行和脚本是吃饭的家伙。今天这一章,我就把最常用的东西拎出来,都是我在项目里反复用到的。

3.1 常用Linux命令

先说说几个高频命令。嗯,这些命令我每天至少用几十次。

3.1.1 ls - 列出文件

ls 是最基础的命令。但我建议你养成加参数的习惯:

ls -l     # 长格式显示,能看到权限、大小、时间
ls -a     # 显示隐藏文件(以.开头的文件)
ls -lh    # 人类可读的大小(K、M、G)
ls -lt    # 按时间排序,最新的在最上面

我的习惯: 我一般直接用 ll(其实是 ls -l 的别名)。在嵌入式开发中,经常用 ls -l /dev/ 查看设备节点,确认外设驱动是否加载成功。

3.1.2 cd - 切换目录

这个太基础了,但有几个技巧值得记住:

cd ~      # 回到家目录
cd -      # 回到上一个目录,超实用
cd ..     # 上一级目录
cd ../../ # 上两级

我在项目中经常用 cd - 在编译目录和源码目录之间快速切换。你想想看,省了多少打字时间。

3.1.3 ps - 查看进程

做TBOX开发,进程管理是家常便饭。我常用的组合:

ps aux           # 查看所有进程
ps aux | grep tbox  # 过滤出TBOX相关进程
ps -ef           # 另一种格式

注意: 我曾经在调试时发现TBOX进程莫名其妙消失了,用 ps aux 一看,原来是内存不足被OOM killer干掉了。所以,没事多看看进程状态。

3.1.4 top - 系统监控

top 是实时监控神器。按 1 可以看每个CPU核心的使用率,按 M 按内存排序,按 P 按CPU排序。

top -d 1    # 每秒刷新一次
top -p 1234 # 只监控PID为1234的进程

在TBOX上,我经常用 top 看哪个进程在偷吃CPU。有一次发现某个通信模块占用了80%的CPU,一查是死循环了。

3.1.5 netstat - 网络状态

TBOX离不开网络通信。netstat 是我排查网络问题的第一工具:

netstat -tlnp   # 查看TCP监听端口
netstat -ulnp   # 查看UDP监听端口
netstat -anp    # 查看所有连接

实战场景: 我记得有一次TBOX上报数据失败,用 netstat -tlnp 一看,发现MQTT客户端根本没连上服务器端口。原来是防火墙把端口封了。

3.2 Vim编辑器的使用

说到Vim,很多新手觉得难。但说实话,在嵌入式Linux上,你逃不掉的。没有图形界面,Vim就是你的IDE。

3.2.1 三种模式

Vim有三种模式,搞懂了就入门了:

模式作用如何进入
普通模式移动光标、复制粘贴默认模式,按 Esc 返回
插入模式输入文字i 进入
命令模式保存退出、搜索替换: 进入

3.2.2 我常用的操作

i        # 在光标前插入
dd       # 删除当前行
yy       # 复制当前行
p        # 粘贴
:wq      # 保存并退出
:q!      # 不保存强制退出
/xxx     # 搜索xxx
:%s/old/new/g  # 全局替换

我的建议: 刚开始别学太多,记住 i:wqddyyp 这五个就够了。其他的慢慢来。我曾经带过一个新人,第一天就背了50个Vim命令,结果一个都没记住。

3.3 Shell脚本基础

Shell脚本是自动化利器。在TBOX开发中,我经常用它来写启动脚本、日志清理、批量测试。

3.3.1 变量

Shell变量不需要声明类型,直接用:

#!/bin/bash
# 这是我的第一个脚本

NAME="TBOX_DEVICE"
VERSION="1.0.0"
echo "设备名称: $NAME"
echo "版本号: $VERSION"

# 命令替换
CURRENT_TIME=$(date)
echo "当前时间: $CURRENT_TIME"

注意: 变量赋值时等号两边不能有空格!我刚开始写脚本时,经常写成 NAME = "xxx",结果报错半天找不到原因。

3.3.2 循环

循环在批量处理时特别好用:

#!/bin/bash

# for循环 - 遍历文件
for file in /var/log/*.log; do
    echo "处理日志: $file"
done

# while循环 - 读取文件
while read line; do
    echo "读取到: $line"
done < /etc/config/tbox.conf

3.3.3 条件判断

条件判断让脚本有了「脑子」:

#!/bin/bash

# 判断文件是否存在
if [ -f "/etc/tbox.conf" ]; then
    echo "配置文件存在"
else
    echo "配置文件不存在,创建默认配置"
    touch /etc/tbox.conf
fi

# 判断进程是否运行
if pgrep -x "tbox_main" > /dev/null; then
    echo "TBOX主进程正在运行"
else
    echo "TBOX主进程未运行,启动中..."
    /usr/bin/tbox_main &
fi

实战经验: 我曾经写过一个监控脚本,每隔5分钟检查TBOX的4G模块是否在线。如果掉线就自动重启模块。这个脚本帮我省了不少半夜爬起来处理故障的麻烦。

3.4 Makefile的编写入门

Makefile是C/C++项目的构建工具。说白了,就是告诉编译器怎么编译你的代码。

3.4.1 最简单的Makefile

# 目标:依赖
# 命令(前面必须是Tab)

tbox_app: main.o network.o mqtt.o
    gcc -o tbox_app main.o network.o mqtt.o -lpthread -lmqtt

main.o: main.c
    gcc -c main.c

network.o: network.c
    gcc -c network.c

mqtt.o: mqtt.c
    gcc -c mqtt.c

clean:
    rm -f *.o tbox_app

3.4.2 使用变量和模式规则

上面的写法太啰嗦了。实际项目中我这样写:

CC = arm-linux-gnueabihf-gcc
CFLAGS = -Wall -O2 -I./include
LDFLAGS = -lpthread -lmqtt
TARGET = tbox_app
SRCS = main.c network.c mqtt.c
OBJS = $(SRCS:.c=.o)

$(TARGET): $(OBJS)
    $(CC) -o $@ $^ $(LDFLAGS)

%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -f $(OBJS) $(TARGET)

.PHONY: clean

解释一下: $@ 表示目标文件,$^ 表示所有依赖,$< 表示第一个依赖。这些自动化变量能省不少事。我刚开始学Makefile时,就是被这些符号搞晕的,但用多了就习惯了。

3.4.3 交叉编译的Makefile

做TBOX开发,代码是在PC上编译,在ARM板子上运行。所以Makefile要指定交叉编译器:

# 交叉编译工具链
CROSS_COMPILE = arm-linux-gnueabihf-
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
STRIP = $(CROSS_COMPILE)strip

CFLAGS = -Wall -O2 -march=armv7-a
LDFLAGS = -lpthread -lrt

TARGET = tbox_app
SRCS = main.c network.c mqtt.c
OBJS = $(SRCS:.c=.o)

all: $(TARGET)
    $(STRIP) $(TARGET)

$(TARGET): $(OBJS)
    $(CC) -o $@ $^ $(LDFLAGS)

%.o: %.c
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    rm -f $(OBJS) $(TARGET)

install:
    cp $(TARGET) /tftpboot/

踩坑提醒: 我曾经犯过一个低级错误——在PC上编译通过的程序,拷到ARM板子上跑不起来。后来发现是忘了用交叉编译器。记住:PC的gcc编译出来的程序,ARM板子跑不了!

小结

这一章的内容,说白了就是TBOX开发的「基本功」。Linux命令让你能操作设备,Vim让你能编辑代码,Shell脚本让你能自动化任务,Makefile让你能构建项目。这四个技能,缺一不可。

我个人建议,别急着一次性全记住。先上手用,遇到问题再回来查。我在项目里也是这么过来的——用多了自然就熟了。

下一章,我们会进入真正的TBOX应用开发。到时候,这些基础都会派上用场。