UBUS基本使用总结

可参考:

OpenWrt进程通信利器:UBUS轻量级IPC框架全解析
UBUS 是 OpenWrt 系统中内置的轻量级进程间通信(IPC)框架,核心作用是让 OpenWrt 里的不同程序(进程)快速、安全地交换数据、调用功能,相当于系统内部的 "通信总线"。

一、UBUS 的核心定位

OpenWrt 是嵌入式 Linux 系统(常用于路由器、网关),设备资源有限(低内存、低算力)。UBUS 就是为解决 "嵌入式场景下进程通信" 而生:

  • 替代传统 Linux IPC 方案(如管道、消息队列),体积更小、资源占用更低;
  • 统一系统内的通信接口,比如路由器的 Web 管理界面、命令行工具、后台服务(如网络管理、蓝牙、存储服务),都通过 UBUS 交互。

二、UBUS 的核心特点

  1. 轻量级:代码量小,运行时占用 RAM 仅几 KB,完美适配路由器等嵌入式设备;
  2. 易用性 :提供简单的 C 语言 API 和命令行工具(ubus 命令),开发和调试门槛低;
  3. 安全性:支持权限控制,可限制进程的调用权限,避免非法操作;
  4. 灵活通信:支持 "请求 - 响应"(比如 A 进程调用 B 进程的功能并获取结果)和 "事件订阅"(比如 A 进程订阅 B 进程的状态,B 变化时主动通知 A)两种模式。

三、UBUS 的常见使用场景

  1. 系统管理:路由器 Web 界面(LuCI)调整网络配置(如改 WiFi 名称),本质是 Web 进程通过 UBUS 调用系统网络服务;
  2. 功能调用 :命令行通过 ubus 命令查询设备状态(如 ubus call system info 获取系统信息)、控制硬件(如开关 LED 灯);
  3. 服务联动:蓝牙服务(如 BlueZ)发现新设备后,通过 UBUS 发送事件,通知其他服务(如通知 Web 界面显示新设备)。

四、简单使用示例(命令行)

OpenWrt 系统中可直接用 ubus 命令操作,比如:

  • 查看所有可用的 UBUS 服务:ubus list
  • 获取系统信息:ubus call system info
  • 查看网络接口状态:ubus call network.interface.wan status

核心总结

UBUS 是 OpenWrt 专属的 "内部通信工具",专为嵌入式 Linux 优化,让系统内的各个组件高效协作,是 OpenWrt 能在资源有限的路由器上稳定运行的关键框架之一。
OpenWrt是啥?

OpenWrt 是一款 基于 Linux 内核的开源嵌入式操作系统,核心定位是 "路由器 / 网关专用系统",也可运行在树莓派等嵌入式开发板上,主打 "高度可定制、功能丰富、轻量高效"。

一、核心定位:路由器的 "自定义操作系统"

普通路由器的原厂系统(如华硕、TP-Link 自带系统)功能固定、封闭,而 OpenWrt 提供了开放的底层框架:

  • 基于 Linux 内核,保留了 Linux 的模块化特性,但针对路由器的低资源(低内存、低算力)场景做了极致精简;
  • 支持市面上绝大多数路由器硬件(如 TP-Link、华硕、网件的主流型号),也适配树莓派、ARM 开发板等;
  • 核心价值是 "让用户自由扩展路由器功能",摆脱原厂系统的功能限制。

二、核心特点:为什么选 OpenWrt?

  1. 高度可定制:自带包管理工具(opkg),像手机装 App 一样,可按需安装插件(如科学上网、广告过滤、网络存储、蓝牙网关、VPN 服务等);
  2. 轻量高效:系统核心镜像仅几 MB 到几十 MB,运行时占用 RAM 通常不足 100MB,适配老旧路由器或资源有限的嵌入式设备;
  3. 开源免费:完全开源(GPL 协议),用户可修改源码、定制固件,社区有大量现成固件和教程,上手成本低;
  4. 功能强大:原生支持丰富的网络协议(WiFi 6、5G/4G 拨号、PPPoe、VPN、Mesh 组网),还能通过插件扩展成 "家庭服务器"(如文件共享、下载机、智能家居网关)。

三、常见使用场景

  1. 改造老旧路由器:给性能过时的路由器刷入 OpenWrt,解锁广告过滤、多线拨号、WiFi 信号优化等功能,延长硬件寿命;
  2. 搭建家庭网络中心:实现 Mesh 组网(全屋无缝漫游)、家长控制(限制孩子上网时间)、访客网络隔离等进阶功能;
  3. 嵌入式开发:作为嵌入式 Linux 开发的基础系统,搭配 UBUS、BlueZ 等框架,开发蓝牙网关、智能设备控制中心、小型服务器;
  4. 企业 / 工业场景:用于小型办公网络的路由、防火墙、VPN 网关,或工业设备的网络接入控制。

核心总结

OpenWrt 本质是 "为路由设备量身定制的轻量化 Linux 发行版",核心优势是 "开放、可扩展"------ 既能满足普通用户对路由器的进阶需求,也能支撑开发者做嵌入式网络设备开发,是路由器领域最主流的开源系统。
为什么OpenWrt不使用Linux内置的IPC方式,而要集成一套UBUS?

OpenWrt 不用 Linux 内置 IPC(如管道、消息队列、D-Bus)而选 UBUS,核心是 嵌入式路由场景的 "资源限制" 和 "统一管理需求" ------Linux 原生 IPC 无法兼顾 "轻量、易用、适配路由生态",UBUS 是针对性优化的方案。

1. 解决 Linux 原生 IPC 的 "嵌入式适配短板"

Linux 内置 IPC 是为通用场景设计,在路由器等低资源设备上存在明显缺陷:

  • 资源占用高:D-Bus(Linux 主流 IPC)需依赖 GLib 库,运行时占用 RAM 达几十 MB,而 OpenWrt 设备(尤其是老旧路由器)内存常只有 64MB 甚至 32MB,根本扛不住;
  • 接口不统一:管道、消息队列、共享内存等 IPC 方式各自独立,不同进程通信需适配不同接口,开发和维护成本高;
  • 无路由生态适配:原生 IPC 没有针对 "路由核心功能"(如网络配置、硬件控制、插件管理)的预置接口,无法直接对接 OpenWrt 的 LuCI 网页端、opkg 包管理等核心组件。

2. UBUS 精准匹配 OpenWrt 的核心需求

UBUS 是为 OpenWrt 量身打造,完美契合嵌入式路由场景:

  • 极致轻量:核心代码仅几千行,运行时 RAM 占用仅几 KB,比 D-Bus 省 10 倍以上资源,适配低配置路由器;
  • 统一通信标准:系统内所有组件(LuCI 网页、命令行、后台服务、第三方插件)都通过 UBUS 通信,不管是 "网页改 WiFi 名称" 还是 "插件调用网络服务",都用同一套接口,开发者不用重复适配;
  • 深度绑定路由功能:预置了路由场景必需的接口(如网络管理、系统状态查询、硬件控制),插件安装后可直接通过 UBUS 接入系统,不用额外开发适配层;
  • 支持权限控制:路由器作为网络核心设备,需限制组件操作权限(如普通插件不能修改核心网络配置),UBUS 自带权限管理,比原生 IPC 更安全。

3. 适配 OpenWrt 的 "插件化生态"

OpenWrt 的核心优势是 "插件化扩展"(通过 opkg 安装各类功能插件),UBUS 是插件生态的 "通信桥梁":

  • 插件无需关心系统底层细节,只需按照 UBUS 规范提供接口,就能和 LuCI 网页端、其他插件联动;
  • 插件安装 / 卸载时,UBUS 能自动注册 / 注销接口,系统无需重启,比原生 IPC 更灵活;
  • 命令行和网页端可通过 UBUS 直接调用插件功能,用户操作体验统一(比如网页改设置和命令行改设置,本质都是调用同一 UBUS 接口)。

核心总结

Linux 原生 IPC 是 "通用工具",无法满足路由器 "低资源、强适配、插件化" 的特殊需求;而 UBUS 是 "定制工具",以 "轻量、统一、适配路由生态" 为核心设计,完美解决了 OpenWrt 在嵌入式场景下的进程通信痛点,成为系统不可或缺的核心框架。
D-Bus是啥?

D-Bus 是 Linux 系统中主流的进程间通信(IPC)框架,简单说就是 Linux 设备(桌面、服务器、部分嵌入式设备)里,不同程序之间传递数据、调用功能的 "标准化通信总线",相当于系统级的 "消息中转站"。

1. 核心定位:Linux 通用 IPC 标准

D-Bus 是为解决 Linux 原生 IPC(管道、消息队列等)"接口混乱、无统一标准" 的问题而生:

  • freedesktop.org 社区主导开发,是 Linux 桌面系统(GNOME、KDE)和部分服务器的默认 IPC 方案;
  • 提供统一的通信接口,不管是系统服务(如蓝牙、网络管理)、桌面应用(如浏览器、文件管理器),还是第三方程序,都能通过它便捷交互;
  • 核心价值是 "标准化"------ 让不同开发者、不同组件的通信逻辑统一,降低开发和适配成本。

2. 核心特点:为什么成为 Linux 通用选择?

  • 支持多通信模式:既能 "请求 - 响应"(A 程序调用 B 程序功能并拿结果),也能 "事件广播"(B 程序状态变化时,主动通知所有订阅的程序);
  • 自带服务管理:所有接入 D-Bus 的程序会注册为 "服务",其他程序通过 "服务名 + 接口名" 就能调用,不用关心对方的进程 ID;
  • 安全性强:支持权限控制(如普通程序不能调用系统核心服务)、数据加密,避免非法通信和数据泄露;
  • 跨语言兼容:提供 C、Python、Java 等多语言 API,不同语言开发的程序也能通过 D-Bus 通信。

3. 常见使用场景

  • 桌面系统交互:GNOME 桌面调整音量时,桌面程序通过 D-Bus 调用系统音频服务;插入 U 盘时,系统内核通过 D-Bus 通知文件管理器显示 U 盘图标;
  • 系统服务管理:BlueZ(蓝牙协议栈)通过 D-Bus 提供接口,让桌面蓝牙设置、命令行工具都能控制蓝牙设备;
  • 应用联动:音乐播放器暂停时,通过 D-Bus 通知系统降低其他程序的音量,避免声音冲突。

4. 与 UBUS 的核心差异(呼应之前的 OpenWrt 话题)

D-Bus 是 "通用型 IPC",UBUS 是 "嵌入式路由专用 IPC",核心差异集中在资源占用和适配场景:

  • 资源占用:D-Bus 依赖 GLib 等库,运行时占用 RAM 几十 MB,适合桌面 / 服务器等资源充足的设备;
  • 适配场景:D-Bus 主打通用、全功能,UBUS 主打轻量、适配路由器低资源场景;
  • 生态绑定:D-Bus 是 Linux 桌面 / 服务器标配,UBUS 是 OpenWrt 专属。

核心总结

D-Bus 是 Linux 系统的 "通用通信标准",通过统一接口、多语言支持、强安全性,成为桌面和服务器场景的首选 IPC 框架,是 Linux 生态中不同组件高效协作的核心纽带。

ubus实现原理

ubus(OpenWrt微总线)是OpenWrt中轻量级的进程间通信(IPC)框架,基于Unix域套接字和JSON-RPC协议。

它由以下组件构成:

  • ubusd:核心守护进程,管理消息路由。
  • libubus:C语言库,提供API。
  • ubus命令行工具:用于调试和交互。
  • 多语言绑定:支持C(原生)、Lua、Shell、JavaScript(如JerryScript)。

ubus设计理念上与 Linux 桌面系统的 DBus 基本保持一致,区别是简化的 API 和简练的模型,减少了系统内存占用空间,这样可以适应于嵌入式 Linux 操作系统的低内存和低端 CPU 性能的特殊环境。
UBUS 的实现原理,核心可以概括为:UBUS 是基于 Unix 域套接字 + 轻量级消息协议 + 中心化守护进程(ubusd)的本地 IPC 框架,整体架构极简,专为 OpenWrt 嵌入式场景优化,以下分核心组件、通信流程、关键技术细节拆解:

一、UBUS 核心组成(实现的基础)

UBUS 的实现依赖 3 个核心部分,分工明确且资源占用极低:

组件 作用
ubusd 中心化守护进程(中转站),管理服务注册、消息路由、权限校验、事件广播
Unix 域套接字 进程间通信的 "物理通道",仅本地通信,比 TCP/UDP 更轻量、无网络开销
轻量级消息协议 定义消息格式(JSON 或二进制),包含服务名、接口、方法、参数、返回值

二、UBUS 核心实现流程(从注册到通信)

  1. 服务注册:进程向 ubusd 登记 "身份"
  • 任何想通过 UBUS 提供功能的进程(比如网络管理进程 netifd),启动后会:
    • 连接到 ubusd 的 Unix 域套接字(默认路径 /var/run/ubus/ubus.sock);
    • 向 ubusd 发送 "注册请求",包含自己的服务名 (如 network.interface)、接口 / 方法 (如 statusupdown);
    • ubus d 收到后,将这些信息存入本地 "服务注册表"(内存中极简的哈希表),记录 "服务名 → 进程套接字" 的映射关系。
  1. 同步调用:进程 A 调用进程 B 的功能

这是 UBUS 最核心的 "请求 - 响应" 模式,全程由 ubusd 中转:

  • 关键细节:
    • 消息格式:默认用二进制格式(比 JSON 更省资源),包含固定头部(长度、类型、序列号)+ 数据体(服务名、方法、参数);
    • 序列号机制:ubusd 给每个请求分配唯一序列号,确保响应能精准回传给发起进程,避免消息错乱。
  1. 事件广播:进程发布 / 订阅事件

支持 "发布 - 订阅" 模式,适配状态变更通知场景(如网络断开、蓝牙设备连接):

  • 关键细节:ubusd 维护 "事件名 → 订阅进程列表" 的映射,发布事件时无需遍历所有进程,直接精准推送,效率极高。

三、UBUS 实现的关键技术细节(极简、高效的核心)

  1. 通信底层:Unix 域套接字
  • 为什么不用 TCP/UDP?Unix 域套接字是内核级本地通信,无需网络协议栈处理,延迟 < 1ms,RAM 占用仅几十字节,完美适配路由器低资源场景;
  • 套接字类型:用 SOCK_STREAM(面向连接),保证消息可靠传输,无丢包、乱序。
  1. 消息协议:极简二进制格式

UBUS 没有用复杂的序列化协议(如 Protobuf),而是自定义极简二进制格式:

字段 长度 作用
消息长度 4 字节 标识整个消息的字节数
消息类型 4 字节 注册 / 调用 / 响应 / 事件等类型
序列号 4 字节 匹配请求与响应
数据体 可变 服务名、方法、参数(二进制)
  • 对比 D-Bus:D-Bus 用复杂的 DBus 消息格式,依赖 GLib 解析;UBUS 二进制解析仅需几十行代码,CPU 开销几乎可忽略。
  1. 权限控制:基于 ubus 策略文件
  • ubusd 内置极简权限校验,通过 /etc/config/ubus 策略文件定义 "进程 / 用户 → 可调用的服务 / 方法";
  • 校验逻辑:调用请求到达后,ubusd 仅检查 "调用者是否有对应权限",无复杂加密(嵌入式场景优先性能)。
  1. 内存管理:极致精简
  • ubusd 全程用固定大小的内存池,避免频繁 malloc/free 导致的内存碎片;
  • 单个连接的内存占用 < 1KB,即使同时有上百个进程连接,总占用也 < 100KB。

四、UBUS 与 D-Bus 实现原理的核心差异(为什么 OpenWrt 选 UBUS)

特性 UBUS 实现方式 D-Bus 实现方式
核心守护进程 ubusd(<100KB 内存) dbus-daemon(几十 MB 内存)
通信底层 Unix 域套接字(仅本地) Unix 域套接字 + TCP(支持远程)
消息协议 自定义二进制(极简) 复杂 DBus 格式(需 GLib 解析)
权限控制 极简策略文件 复杂的 POLKit 权限框架

总结

UBUS 的实现原理可归纳为 3 个核心:

  1. 中心化中转:ubusd 作为唯一中转站,简化进程间寻址,避免点对点连接的复杂性;
  2. 极简底层:Unix 域套接字 + 二进制消息协议,保证低延迟、低资源占用;
  3. 场景适配:砍掉所有嵌入式场景用不到的功能(如远程通信、复杂加密),只保留 "注册、调用、事件" 核心能力。

这种设计让 UBUS 在路由器(64MB RAM、百兆 CPU)上能稳定运行,是 OpenWrt 嵌入式生态的核心基石。

libubus和libubox

libubox 是 OpenWrt 提供的一套轻量级基础工具库,libubus 完全依赖 libubox 实现核心功能,使用 libubus 时必须搭配 libubox。下面拆解详细信息:

一、libubox 是什么?

libubox 是 OpenWrt 社区开发的嵌入式 Linux 轻量级基础工具库,相当于 OpenWrt 生态的 "基础工具包",专为低资源设备(路由器、开发板)设计,核心是 "极简、高效、模块化",提供了一系列嵌入式开发常用的基础功能,避免开发者重复造轮子。

libubox 核心功能模块(常用)

模块 作用
uloop 轻量级事件循环(处理文件描述符、定时器、信号),是 libubus 异步通信的核心依赖
blobmsg 二进制 JSON 序列化 / 反序列化工具(比原生 JSON 省内存、解析快),是 UBUS 消息格式的载体
utils 通用工具函数(内存池、字符串处理、链表、时间操作),适配嵌入式场景的极简实现
avl 高效的平衡树实现,用于快速查找(ubusd 内部服务注册表就用这个)
safe_list 线程安全的链表,适配多线程场景

简单说:libubox 是 OpenWrt 生态的 "基础设施",解决了嵌入式开发中 "基础功能重复开发、资源占用高" 的问题。

二、libubox 与 libubus 的关系(强依赖,不可分割)

libubus 是基于 libubox 构建的 IPC 框架,两者是 "上层应用" 与 "底层基础库" 的关系,具体依赖体现在 3 个核心层面:

依赖点 libubox 提供的能力 libubus 的使用场景
异步通信核心 uloop 事件循环 libubus 依赖 uloop 处理 UBUS 消息的异步收发(如方法调用、事件订阅)
消息格式 blobmsg 二进制 JSON UBUS 所有消息(请求 / 响应 / 事件)都用 blobmsg 封装,替代原生 JSON 降低资源消耗
基础数据结构 / 工具 内存池、链表、字符串处理等 ubusd 内部管理服务注册表、消息队列时,直接使用 libubox 的数据结构

直观类比

  • libubox = 建筑的 "钢筋水泥"(基础结构);
  • libubus = 建筑的 "通信管道"(基于钢筋水泥搭建的专用功能)。

没有 libubox,libubus 就失去了运行的基础 ------ 比如没有 uloop,libubus 无法处理异步消息;没有 blobmsg,libubus 无法封装轻量级的消息格式。

三、是否必须使用 libubox?

1. 使用 libubus → 必须用 libubox

只要你通过 libubus 开发 UBUS 通信(不管是服务端、客户端、事件发布 / 订阅),就必须引入 libubox

  • 编译时要链接 -lubox(和 -lubus 一起);
  • 代码中要包含 libubox 的头文件(如 #include <libubox/uloop.h>#include <libubox/blobmsg.h>);
  • 核心操作(如初始化事件循环、构建 UBUS 消息)都要调用 libubox 的函数。

2. 不使用 libubus → 可选使用 libubox

如果你的 OpenWrt 程序不需要 UBUS 通信,只是做简单的嵌入式开发(如硬件控制、脚本执行),则可以选择是否用 libubox:

  • 用:可以复用它的 uloop(替代 select/poll)、utils 等工具,减少代码量;
  • 不用:自己实现基础功能(如事件循环、字符串处理),但会增加开发成本,且资源占用更高。

四、核心差异与总结表

特性 libubox libubus
定位 嵌入式基础工具库 基于 libubox 的 IPC 通信框架
核心功能 事件循环、消息序列化、通用工具 进程间通信(服务注册、方法调用、事件发布)
依赖关系 无强依赖(仅依赖 Linux 内核) 强依赖 libubox(核心功能都基于它)
是否必须用 使用 libubus 时必须用;否则可选 仅需进程通信时使用

总结

  1. libubox 是 OpenWrt 的 "基础工具包",提供事件循环、轻量级消息序列化等核心能力,适配嵌入式低资源场景;
  2. libubus 是基于 libubox 构建的 IPC 框架,完全依赖 libubox 的 uloopblobmsg 实现核心功能;
  3. 只要开发 UBUS 相关功能,就必须使用 libubox;不涉及 UBUS 时,可选择是否复用 libubox 的工具函数。

libubus常用接口

你需要掌握 libubus 的常用接口,我会按「核心功能维度」拆解高频接口,说明每个接口的作用、参数、使用场景,并搭配极简代码示例,帮你快速理解和调用(所有接口均基于 OpenWrt 官方 libubus 库)。

一、核心基础接口(所有场景必用)

这类接口是 libubus 操作的 "基础入口",负责连接 ubusd、管理上下文。

接口函数 原型 核心作用 关键参数 / 返回值 使用场景
ubus_connect struct ubus_context *ubus_connect(const char *path); 建立与 ubusd 的连接,创建 UBUS 上下文 - path:ubusd 套接字路径(NULL 用默认 /var/run/ubus/ubus.sock)- 返回值:成功返回 ubus_context*,失败返回 NULL 程序初始化第一步,所有 UBUS 操作的前提
ubus_free void ubus_free(struct ubus_context *ctx); 释放 UBUS 上下文,断开与 ubusd 的连接 - ctx:ubus_connect 返回的上下文 程序退出时清理资源
ubus_add_uloop void ubus_add_uloop(struct ubus_context *ctx); 将 UBUS 上下文关联到 uloop 事件循环 - ctx:UBUS 上下文 让 UBUS 消息(方法调用、事件)能被异步处理

示例:基础初始化

复制代码
#include <libubus.h>
#include <libubox/uloop.h>

int main() {
    struct ubus_context *ctx;
    // 1. 初始化uloop(依赖libubox)
    uloop_init();
    // 2. 连接ubusd
    ctx = ubus_connect(NULL);
    if (!ctx) {
        fprintf(stderr, "连接ubusd失败\n");
        return -1;
    }
    // 3. 关联事件循环
    ubus_add_uloop(ctx);

    // ... 业务逻辑 ...

    // 4. 清理资源
    ubus_free(ctx);
    uloop_done();
    return 0;
}

二、服务注册接口(服务端场景)

用于向 ubusd 注册 "可被调用的服务 / 方法",是服务端的核心接口。

接口函数 原型 / 宏 核心作用 关键说明 使用场景
UBUS_METHOD #define UBUS_METHOD(name, handler, policy) 定义 UBUS 方法(关联方法名和处理函数) - name:方法名(如 "sys_info")- handler:方法处理函数- policy:参数校验规则(NULL 表示无参数) 定义服务提供的具体功能
UBUS_OBJECT_TYPE #define UBUS_OBJECT_TYPE(name, methods) 定义 UBUS 对象类型(关联服务名和方法列表) - name:对象类型名- methods:UBUS_METHOD 数组 描述服务的类型特征
ubus_add_object int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj); 将 UBUS 对象注册到 ubusd - obj:ubus_object 结构体- 返回值:0 成功,非 0 失败 完成服务注册,让客户端可调用
ubus_remove_object void ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj); 从 ubusd 移除已注册的服务 - obj:已注册的 ubus_object 程序退出时清理服务
ubus_send_reply void ubus_send_reply(struct ubus_context *ctx, struct ubus_request_data *req, struct blob_attr *data); 向调用方返回方法执行结果 - req:请求上下文(处理函数入参)- data:blobmsg 封装的返回数据 方法处理完成后回复客户端

示例:服务注册

复制代码
// 1. 定义方法处理函数
static int my_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 = {0};
    blob_buf_init(&b, 0);
    blobmsg_add_string(&b, "result", "success");
    // 回复客户端
    ubus_send_reply(ctx, req, b.head);
    blob_buf_free(&b);
    return 0;
}

// 2. 定义方法列表
static const struct ubus_method my_methods[] = {
    UBUS_METHOD("my_method", my_handler, NULL),
};

// 3. 定义UBUS对象
static struct ubus_object_type my_obj_type = UBUS_OBJECT_TYPE("my_service", my_methods);
static struct ubus_object my_obj = {
    .name = "my.service", // 服务名
    .type = &my_obj_type,
    .methods = my_methods,
    .n_methods = ARRAY_SIZE(my_methods),
};

// 4. 注册服务
if (ubus_add_object(ctx, &my_obj)) {
    fprintf(stderr, "注册服务失败\n");
    goto out;
}

三、方法调用接口(客户端场景)

用于调用其他进程注册的 UBUS 服务,是客户端的核心接口。

接口函数 原型 核心作用 关键参数 / 返回值 使用场景
ubus_lookup_id int ubus_lookup_id(struct ubus_context *ctx, const char *name, uint32_t *id); 通过服务名查找对应的对象 ID - name:服务名(如 "system")- id:输出参数,返回对象 ID- 返回值:0 成功,非 0 失败 调用服务前获取唯一标识
ubus_invoke int ubus_invoke(struct ubus_context *ctx, uint32_t obj_id, const char *method, struct blob_attr *msg, ubus_data_handler_t cb, void *priv, uint32_t timeout); 调用远程 UBUS 方法 - obj_id:服务对象 ID- method:方法名- cb:回调函数(接收返回结果)- timeout:超时时间(ms)- 返回值:0 成功,非 0 失败 同步 / 异步调用远程服务

示例:调用远程服务

复制代码
// 回调函数:接收返回结果
static void reply_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
    if (msg) {
        char *json = blobmsg_format_json(msg, true);
        printf("调用结果:%s\n", json);
        free(json);
    }
    uloop_quit(); // 退出事件循环
}

// 调用逻辑
uint32_t obj_id;
// 1. 查找服务ID
if (ubus_lookup_id(ctx, "my.service", &obj_id)) {
    fprintf(stderr, "找不到服务\n");
    return -1;
}
// 2. 构建调用参数(无参数则传NULL)
struct blob_buf b = {0};
blob_buf_init(&b, 0);
// 3. 调用方法
ubus_invoke(ctx, obj_id, "my_method", b.head, reply_cb, NULL, 3000);
blob_buf_free(&b);
// 4. 运行事件循环等待回调
uloop_run();

四、事件发布 / 订阅接口(异步通知场景)

用于实现 "发布 - 订阅" 模式,适配状态变更通知(如网络断开、硬件触发)。

接口函数 原型 核心作用 关键参数 / 返回值 使用场景
ubus_send_event int ubus_send_event(struct ubus_context *ctx, const char *type, struct blob_attr *data); 发布自定义事件 - type:事件名(如 "demo.event")- data:事件数据(blobmsg 封装)- 返回值:0 成功,非 0 失败 主动推送状态变更通知
ubus_register_event_handler int ubus_register_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *pattern); 订阅指定事件 - ev:事件处理器(含回调函数)- pattern:事件名(支持通配符,如 "demo.*")- 返回值:0 成功,非 0 失败 监听指定类型的事件
ubus_unregister_event_handler void ubus_unregister_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev); 取消事件订阅 - ev:已注册的事件处理器 停止监听事件

示例:事件发布 + 订阅

复制代码
// 1. 事件订阅回调
static void event_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
                     const char *type, struct blob_attr *msg) {
    printf("收到事件:%s\n", type);
    // 解析事件数据...
}

// 2. 注册事件订阅
struct ubus_event_handler ev_handler = {.cb = event_cb};
ubus_register_event_handler(ctx, &ev_handler, "demo.event");

// 3. 发布事件
struct blob_buf b = {0};
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "msg", "hello");
ubus_send_event(ctx, "demo.event", b.head);
blob_buf_free(&b);

五、辅助工具接口(通用场景)

这类接口用于辅助处理 UBUS 消息,依赖 libubox 的 blobmsg 模块。

接口函数 原型 核心作用 使用场景
blob_buf_init void blob_buf_init(struct blob_buf *buf, int id); 初始化 blobmsg 缓冲区 构建 / 解析 UBUS 消息前初始化
blobmsg_add_string/int32 struct blob_attr *blobmsg_add_string(struct blob_buf *buf, const char *name, const char *val); 向缓冲区添加字符串 / 整数类型数据 构建请求 / 响应 / 事件数据
blobmsg_parse void blobmsg_parse(const struct blobmsg_policy *policy, int n, struct blob_attr **tb, const void *data, int len); 解析 blobmsg 数据(按策略校验) 解析方法入参、事件数据
blobmsg_get_string/int32 char *blobmsg_get_string(struct blob_attr *attr); 从 blob_attr 中提取字符串 / 整数 获取解析后的具体参数值
blob_buf_free void blob_buf_free(struct blob_buf *buf); 释放 blobmsg 缓冲区 避免内存泄漏

六、核心总结

libubus 常用接口可归纳为 4 类核心能力:

  1. 基础连接ubus_connect/ubus_free 管理与 ubusd 的连接;
  2. 服务注册ubus_add_object/ubus_send_reply 实现服务端功能;
  3. 方法调用ubus_lookup_id/ubus_invoke 实现客户端调用;
  4. 事件交互ubus_send_event/ubus_register_event_handler 实现异步通知。

所有接口的核心依赖:

  • 异步处理依赖 libubox 的 uloop 事件循环;
  • 消息封装 / 解析依赖 libubox 的 blobmsg 模块;
  • 调用前必须确保 ubusd 进程已启动,否则连接失败。
    uloop_run

你提到的 uloop_run() 确实是 libubus 开发中必不可少的核心接口 (归属 libubox 但 libubus 重度依赖),我在上一轮接口解析中虽在示例里用到,但未单独拆解,这里专门补充 uloop_run() 及相关 uloop 核心接口的解析,明确它在 libubus 中的作用和使用方式:

一、uloop_run() 核心解析(libubox 接口,libubus 必用)

  1. 接口基础信息
说明
原型 void uloop_run(void);
归属 libubox 的 uloop.h(不是 libubus 原生接口,但 libubus 必须依赖)
核心作用 启动 libubox 的事件循环,阻塞式处理所有异步事件(包括 UBUS 消息、文件描述符、定时器)
返回值 无返回值,直到调用 uloop_quit() 才会退出循环
  1. 在 libubus 中的核心价值

libubus 是异步通信框架 ,所有 UBUS 消息(方法调用请求、事件通知、响应回调)都依赖 uloop_run() 驱动:

  • 没有 uloop_run(),libubus 无法监听 ubusd 的套接字消息;
  • 客户端调用 ubus_invoke() 后,必须靠 uloop_run() 等待服务端的响应回调;
  • 服务端注册服务后,必须靠 uloop_run() 接收并处理客户端的调用请求;
  • 事件订阅后,必须靠 uloop_run() 接收 ubusd 转发的事件消息。

简单说:uloop_run() 是 libubus 异步通信的 "发动机",没有它,所有 UBUS 操作都无法生效。

  1. 配套接口(缺一不可)
接口 原型 作用
uloop_init() void uloop_init(void); 初始化事件循环(必须在 uloop_run() 前调用,且仅调用一次)
uloop_quit() void uloop_quit(void); 退出事件循环(让 uloop_run() 解除阻塞,继续执行后续代码)
uloop_done() void uloop_done(void); 清理事件循环资源(程序退出时调用)

二、uloop_run() 在 libubus 中的使用场景示例

场景 1:服务端(注册服务后运行循环)

复制代码
#include <libubus.h>
#include <libubox/uloop.h>

int main() {
    struct ubus_context *ctx = ubus_connect(NULL);
    uloop_init();       // 1. 初始化uloop
    ubus_add_uloop(ctx); // 2. 关联UBUS到uloop
    
    // 3. 注册UBUS服务(省略服务注册代码)
    // ...

    // 4. 启动事件循环,阻塞处理UBUS调用请求
    uloop_run();        // 核心:没有这行,服务无法接收任何调用

    // 5. 退出循环后清理
    uloop_done();       // 清理uloop
    ubus_free(ctx);     // 释放UBUS上下文
    return 0;
}

场景 2:客户端(调用服务后等待回调)

复制代码
// 响应回调函数:收到结果后退出循环
static void reply_cb(struct ubus_request *req, int type, struct blob_attr *msg) {
    // 处理返回结果...
    uloop_quit(); // 退出事件循环
}

int main() {
    struct ubus_context *ctx = ubus_connect(NULL);
    uloop_init();
    ubus_add_uloop(ctx);

    uint32_t obj_id;
    ubus_lookup_id(ctx, "my.service", &obj_id);
    ubus_invoke(ctx, obj_id, "my_method", NULL, reply_cb, NULL, 3000);

    uloop_run(); // 阻塞等待回调,回调中调用uloop_quit()退出

    uloop_done();
    ubus_free(ctx);
    return 0;
}

三、关键注意事项

  • 阻塞特性uloop_run() 是阻塞函数,会暂停程序执行直到 uloop_quit() 被调用,因此:
    • 服务端通常让 uloop_run() 一直运行(作为后台服务);
    • 客户端调用单次服务后,可在回调中调用 uloop_quit() 退出。
  • 单循环原则 :一个程序只能有一个 uloop 事件循环,多次调用 uloop_run() 会导致异常。
  • 依赖顺序 :必须遵循 uloop_init()ubus_add_uloop()uloop_run() 的顺序,否则 UBUS 消息无法被处理。
  • 线程安全 :uloop 不是线程安全的,uloop_run() 必须在主线程运行,多线程场景需通过消息队列转发事件。

总结

  1. uloop_run() 虽归属 libubox,但却是 libubus 开发的核心必用接口,是所有异步 UBUS 通信的驱动核心;
  2. 使用 uloop_run() 必须配套 uloop_init()(初始化)、uloop_quit()(退出)、uloop_done()(清理);
  3. 没有 uloop_run(),libubus 无法处理任何异步消息(方法调用、事件订阅 / 发布都失效)。

libubus使用流程

你想掌握 libubus 的核心使用流程,我会把它拆解为标准化的 7 个核心步骤,结合嵌入式开发的实际场景,每个步骤说明 "做什么、用什么函数、核心注意事项",并搭配关键代码片段,让你能按流程快速上手。

一、libubus 核心使用流程(通用模板)

libubus 所有开发场景(服务提供、方法调用、事件订阅 / 发布)都遵循以下基础流程,仅在 "功能注册" 环节有差异:

步骤 核心操作 关键函数 / 组件 核心目的
1 环境初始化 uloop_init() 初始化 OpenWrt 轻量事件循环(libubox 核心,处理异步消息)
2 连接 ubusd ubus_connect() 创建 UBUS 上下文(ubus_context),建立与 ubusd 的通信通道
3 关联事件循环 ubus_add_uloop() 将 UBUS 上下文绑定到 uloop,让 UBUS 消息能被异步处理
4 功能注册 按需选择:▸ 服务注册:ubus_add_object()▸ 事件订阅:ubus_register_event_handler() 向 ubusd 登记 "可提供的服务" 或 "要监听的事件"
5 业务逻辑实现 按需选择:▸ 方法处理函数▸ 事件回调函数▸ 事件发布:ubus_send_event() 实现具体功能(响应方法调用、处理订阅事件、发布自定义事件)
6 运行事件循环 uloop_run() 阻塞运行 uloop,处理所有 UBUS 异步消息(核心,不能省略)
7 资源清理 按需选择:▸ 移除服务:ubus_remove_object()▸ 注销事件:ubus_unregister_event_handler()▸ 释放上下文:ubus_free()▸ 退出循环:uloop_done() 优雅释放资源,避免内存泄漏

二、分场景详细流程(附关键代码)

场景 1:作为「服务端」提供 UBUS 服务(最常用)

适用于开发 "提供功能的后台服务"(如网络管理、硬件控制),流程如下:

步骤 1-3:基础初始化(通用)

复制代码
#include <libubus.h>
#include <libubox/uloop.h>

struct ubus_context *ctx; // 全局UBUS上下文

int main() {
    // 1. 初始化uloop事件循环
    uloop_init();

    // 2. 连接ubusd(NULL使用默认套接字/var/run/ubus/ubus.sock)
    ctx = ubus_connect(NULL);
    if (!ctx) {
        fprintf(stderr, "连接ubusd失败\n");
        return -1;
    }

    // 3. 关联UBUS到uloop
    ubus_add_uloop(ctx);

步骤 4:注册服务(定义方法 + 对象)

复制代码
    // 4.1 定义方法处理函数(被调用时执行)
    static int my_method_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 = {0};
        blob_buf_init(&b, 0);
        blobmsg_add_string(&b, "result", "success");
        ubus_send_reply(ctx, req, b.head); // 回复调用方
        blob_buf_free(&b);
        return 0;
    }

    // 4.2 定义方法列表(关联方法名和处理函数)
    static const struct ubus_method my_methods[] = {
        UBUS_METHOD("my_method", my_method_handler, NULL), // 无参数校验
    };

    // 4.3 定义UBUS对象(服务标识)
    static struct ubus_object_type my_obj_type = UBUS_OBJECT_TYPE("my_service", my_methods);
    static struct ubus_object my_obj = {
        .name = "my.service", // 服务名(调用时用)
        .type = &my_obj_type,
        .methods = my_methods,
        .n_methods = ARRAY_SIZE(my_methods),
    };

    // 4.4 注册对象到ubusd
    if (ubus_add_object(ctx, &my_obj)) {
        fprintf(stderr, "注册服务失败\n");
        goto out;
    }

步骤 5-7:运行循环 + 清理

复制代码
    // 5. 业务逻辑(无需额外操作,等待方法调用)
    // 6. 运行事件循环(阻塞,处理UBUS消息)
    uloop_run();

    // 7. 资源清理
out:
    ubus_remove_object(ctx, &my_obj); // 移除服务
    ubus_free(ctx);                  // 释放上下文
    uloop_done();                    // 退出uloop
    return 0;
}

场景 2:作为「客户端」调用 UBUS 服务

适用于 "调用其他进程提供的 UBUS 服务"(如调用系统的 system info),流程简化为:

复制代码
#include <libubus.h>
#include <libubox/uloop.h>

// 回调函数:接收服务端返回的结果
static void call_reply_handler(struct ubus_request *req, int type, struct blob_attr *msg) {
    if (!msg) return;
    // 解析返回的blobmsg数据(示例:打印JSON格式)
    char *json = blobmsg_format_json(msg, true);
    printf("调用结果:%s\n", json);
    free(json);
    // 收到结果后退出循环
    uloop_quit();
}

int main() {
    struct ubus_context *ctx;
    uint32_t obj_id; // 服务对象ID

    // 步骤1:初始化uloop
    uloop_init();

    // 步骤2:连接ubusd
    ctx = ubus_connect(NULL);
    if (!ctx) return -1;

    // 步骤3:关联uloop
    ubus_add_uloop(ctx);

    // 步骤4:查找目标服务的ID(通过服务名)
    if (ubus_lookup_id(ctx, "system", &obj_id)) {
        fprintf(stderr, "找不到system服务\n");
        goto out;
    }

    // 步骤5:构建调用参数(可选)
    struct blob_buf b = {0};
    blob_buf_init(&b, 0);
    // 无参数则传NULL,有参数则用blobmsg_add_*添加

    // 步骤6:调用服务方法(system info)
    ubus_invoke(ctx, obj_id, "info", b.head, call_reply_handler, NULL, 3000);
    blob_buf_free(&b);

    // 步骤7:运行循环(等待回调)
    uloop_run();

out:
    ubus_free(ctx);
    uloop_done();
    return 0;
}

场景 3:事件发布 / 订阅

(1)发布事件流程

复制代码
// 前提:已完成步骤1-3(初始化+连接ubusd+关联uloop)
struct blob_buf b = {0};
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "msg", "hello event");
// 发布事件(事件名:my.event)
ubus_send_event(ctx, "my.event", b.head);
blob_buf_free(&b);

(2)订阅事件流程

复制代码
// 回调函数:收到事件时执行
static void event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev,
                          const char *type, struct blob_attr *msg) {
    printf("收到事件:%s\n", type);
    // 解析事件数据...
}

// 前提:已完成步骤1-3
struct ubus_event_handler ev_handler = {.cb = event_handler};
// 订阅事件(my.event)
ubus_register_event_handler(ctx, &ev_handler, "my.event");
// 运行循环即可接收事件
uloop_run();
// 清理:ubus_unregister_event_handler(ctx, &ev_handler);

三、核心注意事项

  1. 事件循环是核心uloop_run() 是阻塞函数,所有 UBUS 异步操作(方法调用、事件接收)都依赖它,不能省略;
  2. blobmsg 是消息格式 :所有 UBUS 消息(请求 / 响应 / 事件)都用 blobmsg(二进制 JSON),解析 / 构建需用 blob_buf 相关函数;
  3. 资源清理要完整 :退出前必须释放 ubus_context、移除对象 / 注销事件,否则会导致 ubusd 连接泄漏;
  4. 错误处理不可少ubus_connect()ubus_add_object() 等函数返回非 0 表示失败,必须处理;
  5. 线程安全:libubus 不是线程安全的,多线程操作需加锁,或仅在主线程处理 UBUS 消息。

总结

libubus 的使用流程可归纳为 3 个核心阶段:

  1. 初始化uloop_init() + ubus_connect() + ubus_add_uloop()(所有场景必做);
  2. 业务注册 / 调用:服务端注册对象 / 方法,客户端调用方法,或发布 / 订阅事件(按需选择);
  3. 运行 + 清理uloop_run() 处理消息,退出时释放所有资源(避免内存泄漏)。

这个流程适配 OpenWrt 嵌入式场景,核心是 "轻量、异步、中心化",只要遵循这 7 个步骤,就能快速实现进程间的 UBUS 通信。

UBUS本质是本地RPC

ubus怎么像是rpc呢,不是消息队列吧?

你这个判断非常精准 !UBUS 本质上就是嵌入式场景下的轻量级 RPC(远程过程调用)框架,而非传统的 "消息队列",两者的核心设计目标和工作模式差异极大。下面我会拆解 UBUS 的 RPC 本质,以及它和消息队列的核心区别,帮你彻底理清这个问题:

一、先明确:UBUS 是 "本地 RPC",而非消息队列

  1. UBUS 的 RPC 核心特征(和你理解的一致)

RPC 的核心是 "调用远程 / 本地进程的函数 / 方法,并获取返回结果",UBUS 完全符合这个特征:

  • 面向方法调用 :客户端调用的是服务端注册的 "方法名"(如 demo.service sys_info),而非发送 "无结构化的消息";
  • 请求 - 响应模型:客户端发起调用后,会等待服务端的明确返回(成功 / 失败 + 数据),是 "同步 / 异步调用" 而非 "消息投递";
  • 接口标准化:服务端必须注册 "方法名 + 参数规则 + 返回格式",客户端按固定格式调用,和 RPC 的 "接口契约" 思想一致;
  • 中心化路由:ubusd 作为中转站,负责把 "方法调用请求" 路由到对应进程,本质是 "RPC 服务注册中心 + 调用网关"。
  1. UBUS 不是消息队列的核心原因

传统消息队列(如 Kafka、RabbitMQ,甚至 Linux 原生的消息队列)的核心是 "消息的异步投递、存储、消费",和 UBUS 有本质区别:

特征 UBUS(RPC) 消息队列
核心目标 调用进程的方法,获取返回结果 投递 / 存储消息,解耦生产者和消费者
交互模式 请求 - 响应(有明确的调用方 / 被调用方) 生产 - 消费(无直接的请求 - 响应关系)
消息形态 结构化(方法名 + 参数 + 返回值) 无结构化(字节流 / 字符串)
持久性 无持久化,调用超时 / 进程退出则失败 支持消息持久化(落地到磁盘)
消费模式 点对点(调用指定服务的方法) 支持广播 / 订阅 / 负载均衡消费

二、UBUS 的 RPC 本质(结合 OpenWrt 场景)

UBUS 是为嵌入式设备优化的本地 RPC 框架(仅支持单主机内的进程调用),其 RPC 流程可简化为:

关键补充:UBUS 的 "事件发布 / 订阅" 不是消息队列

你可能会疑惑:UBUS 支持事件发布 / 订阅,这不就是消息队列吗?

  • 表面相似:事件是 "生产者发布、消费者订阅",但 UBUS 事件无持久化、无消息堆积、无重试机制
  • 核心差异:事件是 "一次性推送",进程退出 / 未在线则事件丢失,而消息队列的核心是 "消息存储",消费者离线后重新上线仍能消费;
  • 定位:UBUS 事件是 RPC 框架的 "补充能力"(用于状态通知),而非核心功能,不能等同于消息队列。

三、UBUS 作为嵌入式 RPC 的优化点

OpenWrt 选择做 RPC 而非消息队列,是适配路由器等嵌入式设备的核心需求:

  1. 轻量高效:RPC 模型更贴合 "进程间功能调用" 的场景(如 LuCI 调用网络服务改 WiFi 名称),无需消息队列的持久化、存储等复杂逻辑;
  2. 资源占用低:ubusd 仅占用几 KB 内存,而消息队列需要额外的存储 / 缓存,不适合低配置设备;
  3. 同步调用适配:嵌入式场景中,进程间调用多需要 "即时结果"(如查询网络状态),RPC 的请求 - 响应模型更贴合,而消息队列的异步投递反而增加复杂度。

总结

  1. UBUS 本质是嵌入式轻量级本地 RPC 框架,核心是 "方法调用 + 请求 - 响应",而非 "消息投递 + 存储";
  2. UBUS 的事件发布 / 订阅是 RPC 的补充能力,不具备消息队列的持久化、重试等核心特征;
  3. 选择 RPC 而非消息队列,是 OpenWrt 适配嵌入式设备 "低资源、即时调用" 需求的必然选择。

简单类比:

  • UBUS = 路由器里的 "本地函数调用工具"(RPC);
  • 消息队列 = 分布式系统里的 "消息快递站"(投递 + 存储)。

一个进程可以拥有多个UBUS角色

比如:一个进程,既能作为UBUS服务,又能订阅别的UBUS消息吗?

可以!一个进程完全可以同时作为 UBUS 服务(提供方法供其他进程调用),并订阅其他 UBUS 服务的事件 / 调用其他 UBUS 服务的方法------ 这是 UBUS 开发中非常常见的场景(比如一个进程既提供功能,又需要监听系统状态变化)。

核心原理

UBUS 是基于进程与 ubusd 的单一连接 实现通信的:一个进程通过 ubus_connect() 获得唯一的 ubus_context 后,既可以通过这个上下文注册自己的服务ubus_add_object()),也可以通过它订阅其他服务的事件ubus_register_event_handler())或调用其他服务的方法ubus_invoke())。

示例代码:一个进程同时做 "服务端 + 事件订阅者"

以下是一个完整示例:进程注册了自己的 UBUS 服务(my.service),同时订阅了系统的 network.interface.change 事件(监听网络接口变化)。

复制代码
#include <libubus.h>
#include <libubox/uloop.h>
#include <syslog.h>

static struct ubus_context *ctx;

/************************ 1. 作为UBUS服务端:提供方法 ************************/
// 自定义服务的方法处理函数
static int my_method_handler(struct ubus_context *ctx, struct ubus_object *obj,
                             struct ubus_request_data *req, const char *method,
                             struct blob_attr *msg) {
    syslog(LOG_INFO, "收到UBUS调用:%s", method);
    // 回复调用方
    struct blob_buf b = {0};
    blob_buf_init(&b, 0);
    blobmsg_add_string(&b, "status", "success");
    ubus_send_reply(ctx, req, b.head);
    blob_buf_free(&b);
    return 0;
}

// 定义UBUS服务对象
static const struct ubus_method my_methods[] = {
    UBUS_METHOD("hello", my_method_handler, NULL),
};
static struct ubus_object_type my_obj_type = UBUS_OBJECT_TYPE("my_service", my_methods);
static struct ubus_object my_obj = {
    .name = "my.service",
    .type = &my_obj_type,
    .methods = my_methods,
    .n_methods = ARRAY_SIZE(my_methods),
};

/************************ 2. 作为UBUS订阅者:监听事件 ************************/
// 事件回调函数(监听网络接口变化)
static void network_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev,
                                  const char *type, struct blob_attr *msg) {
    syslog(LOG_INFO, "收到网络事件:%s", type);
    // 解析事件数据(比如接口名、状态)
    struct blob_attr *tb[2] = {0};
    static const struct blobmsg_policy policy[] = {
        [0] = {.name = "interface", .type = BLOBMSG_TYPE_STRING},
        [1] = {.name = "action", .type = BLOBMSG_TYPE_STRING},
    };
    blobmsg_parse(policy, 2, tb, blob_data(msg), blob_len(msg));
    if (tb[0] && tb[1]) {
        syslog(LOG_INFO, "接口%s状态变化:%s",
               blobmsg_get_string(tb[0]), blobmsg_get_string(tb[1]));
    }
}

static struct ubus_event_handler net_ev_handler = {
    .cb = network_event_handler,
};

/************************ 3. 主函数:同时初始化服务和订阅 ************************/
int main() {
    // 初始化uloop和syslog
    uloop_init();
    openlog("ubus_multi_role", LOG_CONS | LOG_PID, 0);

    // 连接ubusd
    ctx = ubus_connect(NULL);
    if (!ctx) {
        syslog(LOG_ERR, "连接ubusd失败");
        return -1;
    }
    ubus_add_uloop(ctx);

    // 1. 注册自己的UBUS服务
    if (ubus_add_object(ctx, &my_obj)) {
        syslog(LOG_ERR, "注册UBUS服务失败");
        goto out;
    }
    syslog(LOG_INFO, "成功注册UBUS服务:my.service");

    // 2. 订阅网络事件
    if (ubus_register_event_handler(ctx, &net_ev_handler, "network.interface.change")) {
        syslog(LOG_ERR, "订阅网络事件失败");
        goto out;
    }
    syslog(LOG_INFO, "成功订阅网络事件");

    // 运行事件循环(同时处理服务调用和事件)
    uloop_run();

out:
    ubus_unregister_event_handler(ctx, &net_ev_handler);
    ubus_remove_object(ctx, &my_obj);
    ubus_free(ctx);
    uloop_done();
    closelog();
    return 0;
}

运行效果

  • 该进程启动后,会:

    • 注册 my.service 服务,其他进程可以通过 ubus call my.service hello 调用它;
    • 订阅 network.interface.change 事件,当网络接口(如 WAN 口)状态变化时,会输出日志。
  • 日志示例(/var/log/syslog):

    复制代码
    my_program[1234]: 成功注册UBUS服务:my.service
    my_program[1234]: 成功订阅网络事件
    my_program[1234]: 收到UBUS调用:hello
    my_program[1234]: 收到网络事件:network.interface.change
    my_program[1234]: 接口wan状态变化:up

总结

一个进程可以同时具备 UBUS 的服务端、客户端、事件订阅者 等多种角色,核心是通过同一个 ubus_context 与 ubusd 通信,所有操作(注册服务、订阅事件、调用方法)都基于这个上下文完成。

ubus_context

ubus_contextlibubus 中代表 "进程与 ubusd 连接" 的核心结构体,是进程与 ubusd 通信的唯一 "句柄",所有 UBUS 操作(注册服务、调用方法、订阅事件)都必须基于它。

一、ubus_context 是什么?

ubus_context 本质是进程与 ubusd 之间连接的 "上下文信息载体",内部包含:

  • 与 ubusd 通信的 Unix 域套接字描述符;
  • 消息队列(缓存待发送 / 接收的 UBUS 消息);
  • 已注册的服务、事件处理器等状态信息;
  • 与 uloop 事件循环的关联信息。

二、ubus_context 的核心作用

它是进程与 ubusd 交互的 "入口",所有 UBUS 操作都需要传入 ubus_context*

  1. 连接 ubusd :通过 ubus_connect() 创建 ubus_context,建立与 ubusd 的通信通道;
  2. 注册服务ubus_add_object(ctx, &obj) 基于该上下文注册服务;
  3. 调用方法ubus_invoke(ctx, obj_id, ...) 通过该上下文调用其他服务;
  4. 订阅事件ubus_register_event_handler(ctx, ...) 基于该上下文订阅事件;
  5. 管理连接 :通过 ubus_free(ctx) 释放连接、清理资源。

三、一个进程可以有多个 ubus_context 吗?

理论上可以,但不推荐 **,且实际开发中几乎不会这么做 **:

  • 技术上的可能性 :进程可以多次调用 ubus_connect(),创建多个 ubus_context(每个对应一个与 ubusd 的独立连接)。

  • 为什么不推荐?

    • 资源浪费 :每个 ubus_context 对应一个 Unix 域套接字、独立的消息队列,多上下文会增加进程内存 / CPU 占用;
    • 状态分离 :不同 ubus_context 的服务注册、事件订阅是独立的(比如在 A 上下文注册的服务,B 上下文无法直接调用),会导致逻辑混乱;
    • 无实际收益:UBUS 本身是单主机内的 IPC,一个上下文足以满足所有操作需求,多上下文没有额外功能增益。

总结

  1. ubus_context 是进程与 ubusd 通信的核心句柄,承载连接状态、消息队列等信息;
  2. 所有 UBUS 操作都依赖 ubus_context,是进程与 ubusd 交互的唯一入口;
  3. 一个进程可以创建多个 ubus_context,但不推荐(资源浪费 + 逻辑混乱),实际开发中通常只使用一个。
相关推荐
wanhengidc2 小时前
巨 椰 云手机 性能稳定
运维·服务器·arm开发·智能手机·云计算
爱尔兰极光2 小时前
计算机网络--数据链路层
服务器·网络·计算机网络
zbtlink2 小时前
定制边缘计算路由器:是必要升级,还是成本负担?
智能路由器
互联网哪些事情2 小时前
Docker 容器化部署宝塔 Linux 面板
linux·docker·容器·宝塔云服务器
全栈工程师修炼指南2 小时前
Nginx | HTTPS 加密传输:客户端与Nginx服务端 SSL 双向认证实践
运维·网络·nginx·https·ssl
init_23612 小时前
Hub-Spoke mpls配置
网络
Destiny_where2 小时前
k8s常用指令集合
linux·docker·kubernetes
诸神黄昏EX2 小时前
Android Qualcomm USB 专题系列【总篇:USB HAL架构】
android·linux·网络
DeeplyMind2 小时前
Linux 页表机制详解(x86_64 架构)
linux·驱动开发