OpenWrt:ubus編譯安裝

ubusopenwrt平臺開發中的進程間通訊提供了一個通用的框架,它讓進程間通訊的實現變得很是簡單,而且ubus具備很強的可移植性,能夠很方便地移植到其餘linux平臺上使用。ubus源碼可經過Gitgit://nbd.name/luci2/ubus.git得到,其依賴的ubox庫的gitgit://nbd.name/luci2/ubox.gitlinux

ubus的實現框架

ubus實現的基礎是unix socket,即本地socket,它相對於用於網絡通訊的inet socket更高效,更具可靠性。unix socket客戶端和服務器的實現方式和網絡socket相似,一個簡單的unix socket服務器和客戶端須要作以下工做:git

  1. 創建一個socket server端,綁定到一個本地socket文件,並監聽clients的鏈接;shell

  2. 創建一個或多個socket client端,鏈接server編程

  3. clientserver相互發送消息;json

  4. clientserver收到對方消息後,針對具體消息進行相應處理。服務器

ubus一樣實現了上述組件,並對socket鏈接以及消息傳輸和處理進行了封裝:網絡

  1. ubus提供了一個socket serverubusd);多線程

  2. ubus提供了建立socket client端的接口,而且提供了三種現成的客戶端供用戶直接使用:a.爲shell腳本提供client端,b.爲lua腳本提供client接口,c.爲C語言提供client接口;框架

  3. ubusclientserver之間通訊的消息格式進行了定義,clientserver都必須將消息封裝成json消息格式;socket

  4. ubusclient端的消息處理抽象出對象(object)和方法(method)的概念。一個對象中包含多個方法,client須要向server註冊收到特定json消息時的處理方法,對象和方法都有本身的名字,發送請求方只需在消息中指定要調用的對象和方法的名字便可。

使用ubus時須要引用一些動態庫,主要包括:

  • libubus.soubus向外部提供的編程接口,例如建立socket、進行監聽和鏈接、發送消息等接口函數;

  • libubox.soubus向外部提供的編程接口,例如等待和讀取消息;

  • 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的初始化所作的工做以下:

  1. epoll_create(32)建立出一個poll_fd

  2. 建立一個UDP unix socket,並添加到poll_fd的監聽隊列;

  3. 進行epoll_wait()等待消息,收到消息後的處理函數定義以下:

static struct uloop_fd server_fd = {  
    .cb = server_cb,  
};

server_cb()函數中的工做爲:

  1. 進行accept(),接受client鏈接,併爲該鏈接生成一個client_fd

  2. client分配一個client id,用於ubusd區分不一樣的client

  3. client發送一個HELLO消息做爲鏈接創建的標誌;

  4. client_fd添加到poll_fd的監聽隊列中,用於監聽client發過來的消息,消息處理函數爲client_cb()

也就是說ubusd監聽兩種消息,一種是新client的鏈接請求,一種是現有的每一個client發過來的數據。

ubusd收到一個client的數據後,調用client_cb()函數的處理過程:

  1. 先檢查一下是否有須要向這個client回覆的數據(多是上一次請求沒處理完),若是有,先發送這些遺留數據。

  2. 讀取socket上的數據,根據消息類型(數據中都指定了消息類型的)調用相應的處理函數;

  3. 處理完成後,向client發送處理結果,例如UBUS_STATUS_OK

client的工做流程

ubus call obj method的工做流程:

  1. 建立一個unix socket(UDP)鏈接ubusd,並接收到server發過來的HELLO消息;

  2. ubus call命令由ubus_cli_call()函數進行處理,先向ubusd發送lookup消息請求objid,而後向ubusd發送invoke消息來調用objmethod方法;

  3. 建立epoll_fd並將clientfd添加到監聽列表中等待消息;

  4. client收到消息後的處理函數爲ubus_handle_data(),其中UBUS_MSG_DATA類型的數據receive_call_result_data()函數協助解析。

參考文章

ubus移植到openwrt
openwrt中使用ubus實現進程通訊的原理
ubus實現進程間通訊舉例

相關文章
相關標籤/搜索