ubus是OpenWrt中的进程间通信机制,类似于桌面版linux的dbus,Android的binder。ubus相当于简化版的dbus,ubus基于unix socket实现,socket绑定到一个本地文件,具有较高的效率;
unix socket是C/S模型,建立一个socket连接,server端和client端分别要做如下步骤:
1. 建立一个socket server端,绑定到一个本地socket文件,监听client的连接;
2. 建立一个或多个socket client端,连接到server端;
3. client端和server端相互发送消息;
4. client端或server端收到对方消息后,针对具体消息进行相应处理。
如下图所示:
ubus同样基于这套流程,其中ubusd实现server,其他进程实现client,例如ubus(cli)、netifd、procd;
两个client通信需要通过server转发。
ubusd
ubusd作为ubus的server端,已经由OpenWrt实现好了,不需要做任何修改,下面来分析一下ubusd的工作流程;
1. 通过usock来创建server端socket,且socket bind到文件”/var/run/ubus.sock”,开启listen,等到client的连接;
2. 将socket添加到uloop中poll,触发条件是read,也就是说server socket可读,则触发poll回调server_cb,server_fd的回调函数是server_cb;
static struct uloop_fd server_fd = {
.cb = server_cb,
};
- server_cb中通过accept来接受client的连接,套接字函数accept执行退出之后会创建一个新的socket,新的socket的fd为accept函数值,而旧的socket不变,也就是accept之后,server端存在两个socket了,旧的socket依然用来listen,新的socket与client建立pair,用于和client的通讯;
- 根据新的socket的fd(int client_fd)来构建一个ubus_client对象,ubus_client的uloop回调函数是client_cb,将ubus_client插入到avl树中(struct avl_tree clients),每个向ubusd注册的client都在对应到avl树中的一个ubus_client;
- ubusd_send_hello,向client端的socket发送一个字符串”hello”;
- 将ubus_client中的socket fd添加到uloop中去轮询监听,触发条件是read,回调函数是client_cb,也就是client发消息到server,则触发回调client_cb;
client_cb通过write或sendmsg来发消息给client,通过read或recvmsg来接收来自client的消息,ubusd_proto_receive_message根据接收的不同消息类型做不同的处理,如下所示:
消息类型 | 处理函数 |
---|---|
UBUS_MSG_PING | ubusd_send_pong |
UBUS_MSG_ADD_OBJECT | ubusd_handle_add_object |
UBUS_MSG_REMOVE_OBJECT | ubusd_handle_remove_object |
UBUS_MSG_LOOKUP | ubusd_handle_lookup |
UBUS_MSG_INVOKE | ubusd_handle_invoke |
UBUS_MSG_STATUS | ubusd_handle_response |
UBUS_MSG_DATA | ubusd_handle_response |
UBUS_MSG_SUBSCRIBE | ubusd_handle_add_watch |
UBUS_MSG_UNSUBSCRIBE | ubusd_handle_remove_watch |
UBUS_MSG_NOTIFY | ubusd_handle_notify |
ubus cli
OpenWrt为ubus实现了一个cli可执行程序,这个可执行程序的名称是”ubus”,shell script中大部分情况下都是通过ubus命令做跨进程通讯,这个应用场景下,ubus作为client端,发送消息到server端ubusd,ubusd再转发到另一个client端。
ubus支持的commands有list、call、listen、send、wait_for和monitor;
static struct {
const char *name;
int (*cb)(struct ubus_context *ctx, int argc, char **argv);
} commands[] = {
{ "list", ubus_cli_list },
{ "call", ubus_cli_call },
{ "listen", ubus_cli_listen },
{ "send", ubus_cli_send