ubus
爲openwrt
平臺開發中的進程間通訊提供了一個通用的框架,它讓進程間通訊的實現變得很是簡單,而且ubus
具備很強的可移植性,能夠很方便地移植到其餘linux
平臺上使用。ubus
源碼可經過Git
庫git://nbd.name/luci2/ubus.git
得到,其依賴的ubox
庫的git
庫git://nbd.name/luci2/ubox.git
。linux
ubus
的實現框架ubus
實現的基礎是unix socket
,即本地socket
,它相對於用於網絡通訊的inet socket
更高效,更具可靠性。unix socket
客戶端和服務器的實現方式和網絡socket
相似,一個簡單的unix socket
服務器和客戶端須要作以下工做:git
創建一個socket server
端,綁定到一個本地socket
文件,並監聽clients
的鏈接;shell
創建一個或多個socket client
端,鏈接server
;編程
client
和server
相互發送消息;json
client
或server
收到對方消息後,針對具體消息進行相應處理。服務器
ubus
一樣實現了上述組件,並對socket
鏈接以及消息傳輸和處理進行了封裝:網絡
ubus
提供了一個socket server
(ubusd
);多線程
ubus
提供了建立socket client
端的接口,而且提供了三種現成的客戶端供用戶直接使用:a
.爲shell
腳本提供client
端,b
.爲lua
腳本提供client
接口,c
.爲C
語言提供client
接口;框架
ubus
對client
和server
之間通訊的消息格式進行了定義,client
和server
都必須將消息封裝成json
消息格式;socket
ubus
對client
端的消息處理抽象出對象(object
)和方法(method
)的概念。一個對象中包含多個方法,client
須要向server
註冊收到特定json
消息時的處理方法,對象和方法都有本身的名字,發送請求方只需在消息中指定要調用的對象和方法的名字便可。
使用ubus
時須要引用一些動態庫,主要包括:
libubus.so
:ubus
向外部提供的編程接口,例如建立socket
、進行監聽和鏈接、發送消息等接口函數;
libubox.so
:ubus
向外部提供的編程接口,例如等待和讀取消息;
libblobmsg.so
/libjson.so
:提供了封裝和解析json
數據的接口,編程時不須要直接使用libjson.so
,而是使用libblobmsg.so
提供的更靈活的接口函數。
使用ubus
進行進程間通訊不須要編寫大量代碼,只需按照固定模式調用ubus
提供的API
便可,在ubus
源碼中examples
目錄下有一些例子能夠參考。
ubus
內部的處理機制:
下載:
$ git clone git://nbd.name/luci2/ubus.git
修改CMakeList.txt
:
cmake_minimum_required(VERSION 2.6) PROJECT(ubus C) ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) OPTION(BUILD_LUA "build Lua plugin" ON) OPTION(BUILD_EXAMPLES "build examples" ON) OPTION(ENABLE_SYSTEMD "systemd support" ON) SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") SET(UBUS_UNIX_SOCKET "/var/run/ubus.sock") SET(UBUS_MAX_MSGLEN 1048576) INCLUDE_DIRECTORIES("xxx/json-c/include/json-c") INCLUDE_DIRECTORIES("xxx/json-c/include") LINK_DIRECTORIES("xxx/json-c/lib") INCLUDE_DIRECTORIES(xxx/libubox/include/libubox) INCLUDE_DIRECTORIES(xxx/libubox/include) LINK_DIRECTORIES(xxx/libubox/lib) SET(CMAKE_INSTALL_PREFIX "xxx/ubus") SET(DESTINATION "xxx/ubus") ADD_DEFINITIONS( -DUBUS_UNIX_SOCKET="${UBUS_UNIX_SOCKET}") ADD_DEFINITIONS( -DUBUS_MAX_MSGLEN=${UBUS_MAX_MSGLEN}) IF(APPLE) INCLUDE_DIRECTORIES(/opt/local/include) LINK_DIRECTORIES(/opt/local/lib) ENDIF() ADD_LIBRARY(ubus SHARED libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c) TARGET_LINK_LIBRARIES(ubus ubox) ADD_EXECUTABLE(ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c) TARGET_LINK_LIBRARIES(ubusd ubox) #find_library(json NAMES json-c json) ADD_EXECUTABLE(cli cli.c) SET_TARGET_PROPERTIES(cli PROPERTIES OUTPUT_NAME ubus) TARGET_LINK_LIBRARIES(cli ubus ubox blobmsg_json json-c) #ADD_SUBDIRECTORY(lua) #ADD_SUBDIRECTORY(examples) INSTALL(TARGETS ubus cli LIBRARY DESTINATION lib RUNTIME DESTINATION bin ) INSTALL(TARGETS ubusd RUNTIME DESTINATION sbin ) INSTALL(FILES ubusmsg.h ubus_common.h libubus.h DESTINATION include) # FIXME: this works but certainly can be done better: SET(UBUSD_BINARY "${CMAKE_INSTALL_PREFIX}/sbin/ubusd") # do this after the installs so we have the proper paths #IF(ENABLE_SYSTEMD) # INCLUDE(FindPkgConfig) # PKG_CHECK_MODULES(SYSTEMD systemd) # IF(SYSTEMD_FOUND) # ADD_SUBDIRECTORY(systemd) # ENDIF() #ENDIF()
編譯安裝:
$ mkdir cmake-build $ cd cmake-build/ $ cmake ../ $ make $ make install
ubus
的應用場景和侷限性ubus
可用於兩個進程之間的通訊,並以相似json
格式進行數據交互。ubus
的常見場景爲:
「客戶端-服務器」形式的交互,即進程A
註冊一系列的服務,進程B
去調用這些服務;
ubus
支持以「訂閱-通知」的方式進行進程通訊,即進程A
提供訂閱服務,其它進程能夠選擇訂閱或退訂該服務,進程A
能夠向全部訂閱者發送消息。
因爲ubus
實現方式的限制,在一些場景中不適宜使用ubus
:
ubus
用於少許數據的傳輸,若是數據量很大或是數據交互很頻繁,則不宜用ubus
,當ubus
一次傳輸數據量超過60KB
,就不能正常工做了;
ubus
對多線程支持的很差,例如在多個線程中去請求同一個服務,就有可能出現不可預知的結果;
不建議遞歸調用ubus
,例如進程A
去調用進程B
的服務,而B
的該服務須要調用進程C
的服務,以後C
將結果返回給B
,而後B
將結果返回給A
。
ubus
源碼簡析ubusd
工做流程ubusd
的初始化所作的工做以下:
epoll_create(32)
建立出一個poll_fd
;
建立一個UDP unix socket
,並添加到poll_fd
的監聽隊列;
進行epoll_wait()
等待消息,收到消息後的處理函數定義以下:
static struct uloop_fd server_fd = { .cb = server_cb, };
server_cb()
函數中的工做爲:
進行accept()
,接受client
鏈接,併爲該鏈接生成一個client_fd
;
爲client
分配一個client id
,用於ubusd
區分不一樣的client
;
向client
發送一個HELLO
消息做爲鏈接創建的標誌;
將client_fd
添加到poll_fd
的監聽隊列中,用於監聽client
發過來的消息,消息處理函數爲client_cb()
。
也就是說ubusd
監聽兩種消息,一種是新client
的鏈接請求,一種是現有的每一個client
發過來的數據。
當ubusd
收到一個client
的數據後,調用client_cb()
函數的處理過程:
先檢查一下是否有須要向這個client
回覆的數據(多是上一次請求沒處理完),若是有,先發送這些遺留數據。
讀取socket
上的數據,根據消息類型(數據中都指定了消息類型的)調用相應的處理函數;
處理完成後,向client
發送處理結果,例如UBUS_STATUS_OK
。
client
的工做流程ubus call obj method
的工做流程:
建立一個unix socket(UDP)
鏈接ubusd
,並接收到server
發過來的HELLO
消息;
ubus call
命令由ubus_cli_call()
函數進行處理,先向ubusd
發送lookup
消息請求obj
的id
,而後向ubusd
發送invoke
消息來調用obj
的method
方法;
建立epoll_fd
並將client
的fd
添加到監聽列表中等待消息;
client
收到消息後的處理函數爲ubus_handle_data()
,其中UBUS_MSG_DATA
類型的數據receive_call_result_data()
函數協助解析。