OpenWrt 架构与原理
整体架构
OpenWrt 在标准 Linux 内核之上构建了一套专为嵌入式网络设备设计的用户空间框架。
┌─────────────────────────────────────────────────────┐
│ 用户应用层 │
│ LuCI │ mosquitto │ node-red │ 自定义脚本 │
├─────────────────────────────────────────────────────┤
│ 服务管理层 │
│ procd(进程守护)│ ubus(IPC 总线) │
├──────────────┬──────────────────────────────────────┤
│ 配置层 │ 网络层 │
│ UCI │ netifd │ hostapd │ dnsmasq │ nftables│
├──────────────┴──────────────────────────────────────┤
│ Linux 内核 │
│ netfilter │ mac80211 │ kmod-* │ SquashFS/JFFS2 │
├─────────────────────────────────────────────────────┤
│ 硬件抽象层 │
│ SoC BSP │ 网卡驱动 │ USB │ UART │ SPI/I2C │
└─────────────────────────────────────────────────────┘UCI 配置系统
UCI(Unified Configuration Interface)是 OpenWrt 的核心配置框架,所有系统配置统一存储在 /etc/config/ 目录下的文本文件中。
配置文件格式
# /etc/config/network
config interface 'loopback'
option ifname 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'
config interface 'lan'
option type 'bridge'
option ifname 'eth0'
option proto 'static'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
config interface 'wan'
option ifname 'eth1'
option proto 'dhcp'UCI 命令行操作
bash
# 读取配置
uci get network.lan.ipaddr # 192.168.1.1
uci show network.lan # 显示 lan 接口所有配置
# 修改配置
uci set network.lan.ipaddr='10.0.0.1'
uci set network.lan.netmask='255.255.0.0'
# 添加列表项
uci add_list network.lan.dns='8.8.8.8'
uci add_list network.lan.dns='8.8.4.4'
# 提交并生效
uci commit network
/etc/init.d/network restart
# 批量操作(推荐用于脚本)
uci batch <<EOF
set network.wan.proto=pppoe
set network.wan.username=user@isp.com
set network.wan.password=secret
commit network
EOFUCI 配置文件对应关系
| 文件 | 管理的服务 |
|---|---|
/etc/config/network | 网络接口、路由、VLAN |
/etc/config/wireless | WiFi 配置 |
/etc/config/firewall | 防火墙规则 |
/etc/config/dhcp | DHCP/DNS(dnsmasq) |
/etc/config/system | 主机名、时区、日志 |
/etc/config/rpcd | RPC 访问控制 |
procd 进程管理
procd 是 OpenWrt 的 init 系统和进程守护程序,替代了传统的 sysvinit。
服务脚本结构
bash
#!/bin/sh /etc/rc.common
# /etc/init.d/myservice
USE_PROCD=1
START=95
STOP=05
start_service() {
procd_open_instance
procd_set_param command /usr/bin/myapp --config /etc/myapp.conf
procd_set_param respawn 3600 5 0 # 崩溃后重启:间隔/超时/最大次数(0=无限)
procd_set_param stdout 1 # 重定向 stdout 到 syslog
procd_set_param stderr 1
procd_set_param pidfile /var/run/myapp.pid
procd_set_param env MY_VAR=value # 环境变量
procd_close_instance
}
reload_service() {
stop
start
}
service_triggers() {
procd_add_reload_trigger "network" # 网络变化时自动 reload
}bash
# 服务管理命令
/etc/init.d/myservice start
/etc/init.d/myservice stop
/etc/init.d/myservice restart
/etc/init.d/myservice reload
/etc/init.d/myservice enable # 开机自启
/etc/init.d/myservice disableubus IPC 总线
ubus 是 OpenWrt 的进程间通信机制,类似于 D-Bus,用于服务发现和方法调用。
bash
# 列出所有注册的对象
ubus list
# 查看对象方法
ubus -v list network.interface
# 调用方法
ubus call network.interface.lan status
ubus call system board
ubus call network reload
# 监听事件
ubus monitor
ubus listen network.interfaceubus 编程示例(C)
c
#include <libubus.h>
static int hello_handler(struct ubus_context *ctx,
struct ubus_object *obj,
struct ubus_request_data *req,
const char *method,
struct blob_attr *msg)
{
struct blob_buf b = {};
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "message", "Hello from ubus!");
ubus_send_reply(ctx, req, b.head);
blob_buf_free(&b);
return 0;
}netifd 网络管理
netifd 是 OpenWrt 的网络接口守护进程,负责管理所有网络接口的生命周期。
netifd 架构
├── 接口(interface):逻辑网络,如 lan/wan
├── 设备(device):物理设备,如 eth0/br-lan
├── 协议处理器(proto handler):dhcp/static/pppoe/...
└── 热插拔脚本:/etc/hotplug.d/iface/自定义协议处理器
bash
# /lib/netifd/proto/myproto.sh
#!/bin/sh
. /lib/functions.sh
. /lib/netifd/netifd-proto.sh
init_proto() {
proto_config_add_string "server"
proto_config_add_string "username"
proto_config_add_string "password"
}
proto_myproto_setup() {
local config="$1"
local iface="$2"
local server username password
json_get_vars server username password
# 建立连接逻辑...
proto_init_update "$iface" 1
proto_add_ipv4_address "10.0.0.2" "255.255.255.0"
proto_add_ipv4_route "0.0.0.0" 0 "10.0.0.1"
proto_send_update "$config"
}文件系统结构
OpenWrt 使用 OverlayFS 实现可写文件系统:
Flash 存储
├── kernel 分区(只读)
│ └── Linux 内核镜像
└── rootfs 分区
├── SquashFS(只读基础系统)
└── JFFS2/F2FS(可写 overlay)
└── /overlay/upper/ ← 用户修改存储在此bash
# 查看文件系统使用情况
df -h
# overlay 是可写层,空间有限!
# 查看 overlay 内容
ls /overlay/upper/
# 恢复出厂设置(清空 overlay)
firstboot && reboot
# 或
jffs2reset -y && reboot构建系统
OpenWrt 构建系统基于 GNU Make,支持交叉编译。
bash
# 克隆源码
git clone https://git.openwrt.org/openwrt/openwrt.git
cd openwrt
git checkout v23.05.3
# 更新 feeds(软件包源)
./scripts/feeds update -a
./scripts/feeds install -a
# 配置目标平台和软件包
make menuconfig
# Target System → x86
# Target Profile → Generic x86/64
# 选择需要的软件包...
# 编译(-j 并行)
make -j$(nproc) V=s 2>&1 | tee build.log
# 输出目录
ls bin/targets/x86/64/自定义软件包 Makefile
makefile
# package/myapp/Makefile
include $(TOPDIR)/rules.mk
PKG_NAME:=myapp
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://example.com/releases/
PKG_HASH:=abc123...
include $(INCLUDE_DIR)/package.mk
define Package/myapp
SECTION:=utils
CATEGORY:=Utilities
TITLE:=My IoT Application
DEPENDS:=+libmosquitto +libmodbus
endef
define Package/myapp/description
Industrial IoT data collection application.
endef
define Build/Configure
$(call Build/Configure/Default,--enable-mqtt --enable-modbus)
endef
define Package/myapp/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/myapp $(1)/usr/bin/
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/myapp.init $(1)/etc/init.d/myapp
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/myapp.config $(1)/etc/config/myapp
endef
$(eval $(call BuildPackage,myapp))内存与存储优化
嵌入式设备资源有限,需要特别注意:
bash
# 查看内存使用
free -m
cat /proc/meminfo
# 查看进程内存
top
ps aux --sort=-%mem | head -20
# 减少日志占用(tmpfs)
uci set system.@system[0].log_size='64' # 64KB 循环日志
uci commit system
# 使用 USB/SD 扩展存储(extroot)
# 1. 格式化 USB
mkfs.ext4 /dev/sda1
# 2. 挂载并复制 overlay
mount /dev/sda1 /mnt
tar -C /overlay -cvf - . | tar -C /mnt -xf -
# 3. 配置 fstab
uci set fstab.@mount[0].target='/overlay'
uci set fstab.@mount[0].device='/dev/sda1'
uci set fstab.@mount[0].fstype='ext4'
uci set fstab.@mount[0].enabled='1'
uci commit fstab
reboot