openwrt之ubus机制
最近工作上的项目使用的是openwrt,并且我负责的某一任务要获取网络状态,因此需要使用到ubus,以下内容仅为学习笔记。
1、openwrt简介
OpenWRT是一个高度模块化、高度自动化的嵌入式Linux系统,拥有强大的网络组件和扩展性,常常被用于工控设备、电话、小型机器人、智能家居、路由器以及VOIP设备中。 同时,它还提供了100多个已编译好的软件,而且数量还在不断增加,而OpenWrt SDK 更简化了开发软件的工序。
OpenWRT不同于其他许多用于路由器的发行版,它是一个从零开始编写的、功能齐全的、容易修改的路由器操作系统。实际上,这意味着您能够使用您想要的功能而不加进其他的累赘,而支持这些功能工作的[linux kernel](https://baike.baidu.com/item/linux kernel/765824)又远比绝大多数发行版来得新。
2、ubus
简介
ubus就是一个用于进程间通讯的通用框架。它具有很强的可移植性,能够很方便的移植到其余的Linux平台上使用。
ubus模块被设计用于提供守护进程(daemons)和应用程序(applications)间的通信,包含了守护进程ubusd、库以及一些例子。ubusd能够认为是一个消息管理服务器(Server),须要通讯的进程能够经过提供的libubus使用ubus,而ubus又依赖于ubox。其源码下载地址以下:web
ubus:git://nbd.name/luci2/ubus.git或者http://git.openwrt.org/project/ubus.git
ubox:git://nbd.name/luci2/ubox.gitshell
ubus的内部框架大体如下图:
其中的Ubus Daemon就是ubusd,它是一个服务管理的服务器。上图右下角的组件是一个Client,用于向ubusd请求服务。而左下角是一个服务提供者(相对于ubusd它其实也是个Client,这里称之为Server实际上是相对于服务请求者Client而言,不要搞混了)。上图中Server和Client之间通讯的消息采用json格式。
名词解释
对象(object):
为了便于对消息进行处理,ubusd抽象出了“对象”和“方法”的概念。一个对象中包含多个方法,它们都可以指定名字,便于其余地方使用。多线程
方法(Method)
方法是对象提供的一些能够被调用的操做。在使用ubus调用(Call)某个方法时,Client会发送call消息给服务提供端,并等待回复。架构
通知(Notification)& 订阅者(Observer)
订阅者只关心本身感兴趣的服务,通知者经过该服务广播消息,通知全部的订阅者。它们之间是多对一的关系。
ubus进程间通信方式
ubus进程间通信方式总共有三种:P2P方式;Subscribe-notify方式;Event broadcast方式。
1)P2P方式:1对1
对于提供服务(Service)的进程(服务提供者)
- 使用ubus_connect链接到服务管理进程ubusd,获得ubus_context(包含了链接fd、注册fd的回调等)
- 经过ubus_add_uloop将ubus_context里包含的链接fd加入到epoll的描述符集,用于监听
注意:在使用ubus前必需要调用uloop_init准备好epoll专用句柄。 - 将定义的对象经过ubus_add_object加入到ubusd
- 调用uloop_run循环epoll的文件描述符集
- 资源释放
若是不想再使用ubus,要依次调用ubus_free和uloop_done将前面申请的资源释放掉。
对于获取服务的请求进程(client)
-
调用ubus_connect链接到ubusd,获得ubus_context
-
经过ubus_add_uloop将ubus_context里包含的链接fd加入到epoll的描述符集,用于监听
注意:在使用ubus前必需要调用uloop_init准备好epoll专用句柄。 -
调用ubus_lookup_id查找对象对应的id
-
将调用参数填充到blob_buf
① 经过blob_buf_init初始化blob_buf
② 经过blobmsg_add_u32等函数往blob_buf中加入数据 -
调用ubus_invoke,等待回应
若是不须要等待回复,则调用ubus_invoke_async
-
资源释放
若是不想再使用ubus,要依次调用ubus_free和uloop_done将前面申请的资源释放掉。
2)Subscribe-notify方式: 1对多
A是被订阅者,B,C,D是订阅者。
B,C,D通过ubusd订阅A,A通过ubusd向B,C,D广播消息
对于订阅者(Subscriber)
- 调用ubus_connect链接到ubusd,获得ubus_context
- 经过ubus_add_uloop将ubus_context里包含的链接fd加入到epoll的描述符集,用于监听
- 定义ubus_subscriber实例并调用ubus_register_subscriber将其注册到ubusd
- 订阅感兴趣的对象(Object)
- 使用uloop_run函数,等待被订阅者广播消息。
对于通知发送者(Notifier,Notification Sender)
- 调用ubus_connect链接到ubusd,获得ubus_context
- 经过ubus_add_uloop将ubus_context里包含的链接fd加入到epoll的描述符集,用于监听
- 定义一个通知对象(ubus_object)
- ubus_add_object将通知对象加入到ubusd
- 当事件发生,准备消息类型及参数
- ubus_notify将通知广播到ubusd
- 资源释放
若是不想再使用ubus,要依次调用ubus_free和uloop_done将前面申请的资源释放掉。
3)Event broadcast方式: 1对多
A是广播者, B,C,D是监听者
A广播event,若event与B,C,D监听消息匹配,则B,C,D的相应处理函数被调用。
event是一个广播消息,事件的发送方不须要知道谁要接收这个消息。网卡的状态通知就是经过这种方式实现的,谁要是关心这个事件就本身接收处理。
实现的步骤和前面两种相似,这里只介绍不一样的部分。
对于事件接收者
- 定义事件监听器ubus_event_handler
- 调用ubus_register_event_handler注册事件处理机制
ubus_register_event_handler(ctx, ubus_event_handler, “eventA”);
对于事件发送者
- 填充blob_buf,构造事件内容
- 调用ubus_send_event将事件广播出去
ubus_send_event(ctx, “eventA", b.head);
对于事件发送者
- 填充blob_buf,构造事件内容
- 调用ubus_send_event将事件广播出去
ubus_send_event(ctx, “eventA", b.head);
参考文献
https://www.cnblogs.com/sky-heaven/p/6394267.html