OpenWrt源碼分析之ubus


摘自:https://blog.csdn.net/iampisfan/article/details/78107903

摘自:https://www.shangmayuan.com/a/541693cbb2d046dc87ec76a5.html

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,
};

 

  1. server_cb中通過accept來接受client的連接,套接字函數accept執行退出之后會創建一個新的socket,新的socket的fd為accept函數值,而舊的socket不變,也就是accept之后,server端存在兩個socket了,舊的socket依然用來listen,新的socket與client建立pair,用於和client的通訊;
  2. 根據新的socket的fd(int client_fd)來構建一個ubus_client對象,ubus_client的uloop回調函數是client_cb,將ubus_client插入到avl樹中(struct avl_tree clients),每個向ubusd注冊的client都在對應到avl樹中的一個ubus_client;
  3. ubusd_send_hello,向client端的socket發送一個字符串”hello”;
  4. 將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;

 1 static struct {
 2     const char *name;
 3     int (*cb)(struct ubus_context *ctx, int argc, char **argv);
 4 } commands[] = {
 5     { "list", ubus_cli_list },
 6     { "call", ubus_cli_call },
 7     { "listen", ubus_cli_listen },
 8     { "send", ubus_cli_send },
 9     { "wait_for", ubus_cli_wait_for },
10     { "monitor", ubus_cli_monitor },
11 };

 

  • list
    命令的使用格式是:
ubus [-v] list [path]

 

命令用於列舉出系統注冊的ubus object和method;
例如:

root@OpenWrt:/# ubus list
dhcp
hostapd.wdev0ap0
hostapd.wdev1ap0
log
network
network.device
network.interface
network.interface.lan
network.interface.loopback
network.interface.wan
network.interface.wan6
network.wireless
service
session
system
uci
root@OpenWrt:/# ubus -v list network
'network' @26c90ada
        "restart":{}
        "reload":{}
        "add_host_route":{"target":"String","v6":"Boolean","interface":"String"}
        "get_proto_handlers":{}
        "add_dynamic":{"name":"String"}

 

  • call
    命令的使用格式是:
ubus call path method [message]

 

命令用於執行某個ubus client注冊的某個的object的某個method;
例如:

root@OpenWrt:/# ubus -v call network.interface.lan status
{
        "up": true,
        "pending": false,
        "available": true,
        "autostart": true,
        "dynamic": false,
        "uptime": 1698,
        "l3_device": "br-lan",
        "proto": "static",
        "device": "br-lan",
        "updated": [
                "addresses"
        ],
        "metric": 0,
        "delegation": true,
        "ipv4-address": [
                {
                        "address": "192.168.1.1",
                        "mask": 24
                }
        ],
        ...
}

 

  • listen
    命令的使用格式是:
ubus listen [path]

 

設置一個監聽socket並觀察進入的事件;
例如:

root@OpenWrt:/# ubus listen &
root@OpenWrt:/# /etc/init.d/network restart
{ "ubus.object.remove": {"id":-369156789,"path":"network.interface.wan6"} }
{ "ubus.object.remove": {"id":10693651,"path":"network.interface.wan"} }
{ "ubus.object.remove": {"id":1946155556,"path":"network.interface.lan"} }
{ "ubus.object.remove": {"id":-696825625,"path":"network.interface.loopback"} }
{ "ubus.object.remove": {"id":-1930323617,"path":"network.interface"} }
{ "ubus.object.remove": {"id":-1647942339,"path":"network.wireless"} }
{ "ubus.object.remove": {"id":-886262541,"path":"network.device"} }
{ "ubus.object.remove": {"id":-1084952745,"path":"network"} }
{ "ubus.object.add": {"id":-22896841,"path":"network"} }
{ "ubus.object.add": {"id":-1554358219,"path":"network.device"} }
{ "ubus.object.add": {"id":-1777113328,"path":"network.wireless"} }
{ "ubus.object.add": {"id":1365477379,"path":"network.interface"} }
{ "ubus.object.add": {"id":1148732338,"path":"network.interface.loopback"} }
{ "ubus.object.add": {"id":1802312926,"path":"network.interface.lan"} }
{ "ubus.object.add": {"id":982976204,"path":"network.interface.wan"} }
{ "ubus.object.add": {"id":-1790820211,"path":"network.interface.wan6"} }
{ "network.interface": {"action":"ifup","interface":"lan"} }
{ "network.interface": {"action":"ifup","interface":"loopback"} }
{ "ubus.object.remove": {"id":-627066478,"path":"hostapd.wdev1ap0"} }
{ "ubus.object.remove": {"id":-208119521,"path":"hostapd.wdev0ap0"} }
{ "ubus.object.add": {"id":154802105,"path":"hostapd.wdev0ap0"} }
{ "ubus.object.add": {"id":-1230601970,"path":"hostapd.wdev1ap0"} }
{ "ubus.object.remove": {"id":-1230601970,"path":"hostapd.wdev1ap0"} }
{ "ubus.object.remove": {"id":154802105,"path":"hostapd.wdev0ap0"} }
{ "ubus.object.add": {"id":914025961,"path":"hostapd.wdev0ap0"} }
{ "ubus.object.add": {"id":488528099,"path":"hostapd.wdev1ap0"} }

 

  • send
    命令的使用格式是:
ubus send type [message]

 

發送一個消息;
例如:

root@OpenWrt:/# ubus listen &
root@OpenWrt:/# ubus send network.interface '{"action":"ifup","interface":"lan"}'
{ "network.interface": {"action":"ifup","interface":"lan"} }

 

  • wait_for
    命令的使用格式是:
ubus wait_for object [...]

 

等待某個事件;
例如啟動netifd后,/etc/init.d/network會wait_for network_interface這個object的add事件;

ubus -t 30 wai_for network.interface

 

  • monitor
    監聽,一般不用

下面以”ubus call”命令為例,來說明ubus client的工作流程。
1. ubus_connect做了三部分工作,a)構造結構體ubus_context,ubux_context表示client端的ubus上下文,包含client注冊的object avl tree,client sock,msgbuf;b)創建client unix socket;c)connect to server socket。
2. ubus_lookup_id向ubusd發送消息UBUS_MSG_LOOKUP,查到path對應的object id;ubusd對應的處理函數為ubusd_handle_lookup,ubusd中維護了所有注冊過的object的path avl tree,遍歷path avl tree進行字符串匹配即可找到對應的object,然后通過ubusd_send_obj將object的UBUS_ATTR_OBJPATH、UBUS_ATTR_OBJID、UBUS_ATTR_OBJTYPE等信息發回給ubus cli,消息類型是UBUS_MSG_DATA。
3. ubus cli端ubus_lookup_id的回調函數是ubus_look_id_cb,所有ubus cli接收到消息后在ubus_lookup_id_cb中解析出來一個id號。
4. ubus_invoke向ubusd發送消息UBUS_MSG_INVOKE,消息中攜帶了信息UBUS_ATTR_OBJID和UBUS_ATTR_METHOD,ubusd對應UBUS_ATTR_METHOD的處理函數是ubusd_handle_invoke,ubusd根據OBJID找到注冊的object,並發UBUS_MSG_INVOKE消息給該client進程,該client進程執行對應的method后,將執行結果發回給ubusd。
5. ubusd將method的執行結果發給ubus cli,ubus cli在回調函數receice_call_result_data將執行結果打印出來。

總體流程如下圖所示:
這里寫圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM