
嵌入式操作系统 | ubus把自己的程序状态暴露出去本课程开源地址Giteehttps://gitee.com/fujianxinxi/qianrushixitongyingyongkaifa.git课件、示例代码与验收脚本都在该仓库可直接git clone或下载 ZIP 使用。模块六 · 2 学时 · 环境VMware Ubuntu 20.04一句话概述讲清 ubus 的对象/方法模型先启动 ubusd再写一个注册 sensordata 对象的 C 程序把运行状态设备名、运行时长、被查询次数、传感器读数用 blobmsg 返回去最后用 ubus list 和 ubus call 一条命令一条命令地查询配套课堂练习与课后作业。本节目标能说出 ubus 是干什么的以及 ubusd 和 socket 文件的作用会写一个注册对象和方法、返回程序状态的 C 程序blobmsg 组返回值会先起 ubusd、再跑自己的程序、最后用ubus list/ubus call查询一、讲解内容1.1 知识讲解ubus 是什么。它是 OpenWrt 里的进程间通信IPC机制一个程序把自己的能力注册成一个对象对象里可以有若干方法别的程序或命令行的ubus通过名字就能调用它、拿回数据。路由器上执行ubus call network.interface.lan status查询网络状态用的就是这套机制。两个关键东西名字是什么ubusd一个守护进程相当于总机所有程序都连它/var/run/ubus/ubus.sock通信用的 unix socket 文件程序启动时先ls -l看它一眼就知道 ubusd 在不在这个路径是课时 15 编译时钉死在库里的默认值ubusd启动时会自动创建所在的目录。写一个 ubus 服务端就三步步骤函数干什么① 连上总机ubus_connect(NULL)连接 ubusd并ubus_add_uloop(ctx)让它的消息走 uloop② 注册对象ubus_add_object(ctx, obj)把自己登记成sensordata方法是status③ 等人调用方法回调别人ubus call时回调被调用用blobmsg组装返回值发回去blobmsg 是什么。它是 libubox 提供的一套键值对表接口先用blob_buf_init开一个空表再用blobmsg_add_string / add_u32 / add_double往里放字段最后通过ubus_send_reply发回去。命令行那边看到的就是一段 JSON正好接上下一课。1.2 操作步骤一条命令一步看现象第 1 步编译注意要链接 4 个库cd~/lab/课时20-ubus状态查询 gcc-Wall-Wextra-oubus_status ubus_status.c-lubus-lubox-lblobmsg_json-ljson-c为什么这么多-l程序用了 ubus 的接口-lubus、uloop 和 blobmsg-lubox、blobmsg 与 JSON 互转-lblobmsg_json -ljson-c。少一个就会看到undefined reference to ...。第 2 步先启动 ubusd终端 Asudoubusdls-l/var/run/ubus/ubus.sock现象出现 socket 文件说明总机已经开机srw-rw-rw- 1 root root 0 Sep 25 08:15 /var/run/ubus/ubus.sock第 3 步运行我们的程序终端 A会让出提示符前先打印三行sudo./ubus_status现象程序注册完就不动了正等别人来查① 连接 ubusd… 已连接 ② 已注册对象 sensordata方法 status ③ 现在到另一个终端执行 ubus list ubus call sensordata status第 4 步另开一个终端 B先看有哪些对象ubus list现象能看到我们刚注册的对象sensordata第 5 步查询状态这就是让程序把状态说出来ubus call sensordata status现象返回一段 JSON多查几次query_count会加 1{ device: ubuntu-gateway, uptime_s: 49321, query_count: 1, sensor: 温度, value: 25.500000, status: ok }再执行一次同样的命令{ device: ubuntu-gateway, uptime_s: 49321, query_count: 2, sensor: 温度, value: 26.000000, status: ok }第 6 步回到终端 A 看程序那边的现象← 收到第 1 次 ubus 查询 ← 收到第 2 次 ubus 查询两边对应上了终端 B 每执行一次ubus call终端 A 的程序回调就被调用一次——这就是查询程序状态的完整链路。第 7 步收工# 终端 A 按 CtrlC 停掉 ubus_status然后sudopkill-xubusd# 关掉总机1.3 常见错误与排查现象原因解决程序打印连不上 ubusdubusd 没启动先sudo ubusd 再ls -l /var/run/ubus/ubus.sock确认ubus list是空的自己的程序没在运行或刚退出先把./ubus_status跑起来它会一直等ubus call ... : Object not found对象名写错或程序没注册成功对象名必须是sensordata先ubus list看一眼ubus call没反应程序卡住了比如注册后没进 uloop检查是否调用了uloop_run()编译报undefined reference to ubus_...链接参数少写了四条都要写-lubus -lubox -lblobmsg_json -ljson-cubusd: command not found课时 15 没装好 ubus回课时 15 检查which ubusd应为/usr/local/sbin/ubusd二、学生练习本节命令速查表命令作用示例gcc … -lubus -lubox -lblobmsg_json -ljson-c编译 ubus 程序见第 1 步sudo ubusd 启动总机sudo ubusd ls -l /var/run/ubus/ubus.sock确认总机在ls -l /var/run/ubus/ubus.socksudo ./ubus_status运行自己的服务程序sudo ./ubus_statusubus list看有哪些对象ubus listubus call 对象 方法调用方法查询ubus call sensordata statussudo pkill -x ubusd关掉总机sudo pkill -x ubusd编号任务提示预期结果L20.1按步骤跑通查询先 ubusd → 再程序 → 再 callubus list有 sensordatacall 返回 JSONL20.2连查 3 次连续执行ubus callquery_count依次 1、2、3L20.3加一个字段在handle_status里加blobmsg_add_u32(b, heartbeat, 42)call 结果里多一项L20.4加第二个方法仿照status加一个ping方法返回{pong:true}ubus call sensordata ping能调通L20.5不启动 ubusd 就运行程序先sudo pkill -x ubusd程序打印连不上 ubusd并退出L20.6观察程序退出后的现象停掉 ubus_status 后再ubus list对象消失ubus call报 Object not found三、作业必做完成 L20.1、L20.2提交两个终端截图程序端 ubus call 的 JSON 输出并回答① ubusd 是什么socket 文件有什么用② 我们的程序是怎么把状态交出去的③ 为什么ubus call的输出是 JSON选做把课时 18 的采集程序和新知识合起来——在 ubus 对象里返回最后一次采集的温度让查询能拿到实时值提示把采集结果存到全局变量回调里读它提交截图 文字回答命名课时20-作业截图.png四、本节小结与下节预告本节把程序状态暴露给了别人启动 ubusd、注册sensordata对象、用 blobmsg 返回字段再用ubus list/ubus call查询。你注意到没有——ubus call打出来的就是 JSON。下节课我们就专门学 JSON怎么用它生成和解析数据。附录怎么运行 完整代码一个课件就够用先按附录 A 的顺序敲命令服务端/对端先开自己的程序后开再按附录 B 抄代码。行尾请用 LF。附录 A怎么运行注意先后顺序gcc-Wall-Wextra-oubus_status ubus_status.c-lubus-lubox-lblobmsg_json-ljson-csudoubusd# 终端A先启动 ubusdls-l/var/run/ubus/ubus.sock# 确认 socket 在sudo./ubus_status# 再运行自己的程序前台等着被查ubus list# 终端B看对象ubus call sensordata status# 查询状态返回 JSONsudopkill-xubusd# 收工附录 B文件ubus_status.c/* * ubus_status.c —— 课时 20注册一个 ubus 对象把程序状态暴露出去 * * 编译gcc -Wall -Wextra -o ubus_status ubus_status.c -lubus -lubox -lblobmsg_json -ljson-c * 运行sudo ./ubus_status # 需要先启动 ubusd见 ubus_check.sh start * * 做法三步 * ① 连上 ubusdubus_connect * ② 注册一个对象 sensordata里面有一个方法 status * ③ 别人执行 ubus call sensordata status 时我们的回调被调用 * 用 blobmsg 组一个表返回去ubus 命令行会把它打成 JSON */#includestdio.h#includesignal.h#includetime.h#includeunistd.h#includelibubox/blobmsg.h#includelibubox/uloop.h#includelibubus.hstaticstructubus_context*ctx;staticunsignedintquery_count0;/* 被查询了几次 *//* 方法回调有人 ubus call sensordata status 时uloop 会走到这里 */staticinthandle_status(structubus_context*c,structubus_object*obj,structubus_request_data*req,constchar*method,structblob_attr*msg){structblob_bufb{0};(void)c;(void)obj;(void)method;(void)msg;query_count;blob_buf_init(b,0);/* 准备一个空表 */blobmsg_add_string(b,device,ubuntu-gateway);/* 字符串字段 */blobmsg_add_u32(b,uptime_s,(uint32_t)(time(NULL)%100000));blobmsg_add_u32(b,query_count,query_count);/* 自己被查了几次 */blobmsg_add_string(b,sensor,温度);blobmsg_add_double(b,value,25.0query_count*0.5);/* 假装最新读数 */blobmsg_add_string(b,status,ok);ubus_send_reply(c,req,b.head);/* 把表发回去 */blob_buf_free(b);printf(← 收到第 %u 次 ubus 查询\n,query_count);fflush(stdout);return0;}/* 方法表本对象只有 status 一个方法不用参数 */staticconststructubus_methodstatus_methods[]{UBUS_METHOD_NOARG(status,handle_status),};staticstructubus_object_typestatus_typeUBUS_OBJECT_TYPE(sensordata,status_methods);staticstructubus_objectstatus_obj{.namesensordata,/* ubus call 时用的对象名 */.typestatus_type,.methodsstatus_methods,.n_methodsARRAY_SIZE(status_methods),};staticvoidon_signal(intsig){(void)sig;uloop_end();}intmain(void){uloop_init();signal(SIGINT,on_signal);signal(SIGTERM,on_signal);printf(① 连接 ubusd…\n);ctxubus_connect(NULL);/* NULL 用默认 socket /var/run/ubus/ubus.sock */if(!ctx){printf( 连不上 ubusd先执行 ./ubus_check.sh start 把 ubusd 起起来\n);return1;}printf( 已连接\n);ubus_add_uloop(ctx);/* 让 ubus 的消息也走 uloop 事件循环 */if(ubus_add_object(ctx,status_obj)!0){printf(② 注册对象失败\n);return1;}printf(② 已注册对象 sensordata方法 status\n);printf(③ 现在到另一个终端执行 ubus list ubus call sensordata status\n);fflush(stdout);uloop_run();/* 事件循环等别人来调用 */ubus_free(ctx);uloop_done();printf(\n④ 程序退出共被查询 %u 次\n,query_count);return0;}