openwrt常用庫用法
-
libubox
1.1.概述
libubox是openwrt的一個基礎庫,openwrt下大部分應用都是基於它開發的(ubus、uhttpd、uci等)。
libubox主要提供了兩類功能:
[1].一套完整的基於事件驅動的機制
[2].多個常用的功能模塊(鏈表、avl樹、json、消息傳輸單元、md5)
1.2.基於事件驅動機制
基於事件驅動機制是libubox的核心部分,這套機制主要實現了一套通用的非阻塞I/O多路複用(epoll)平臺。
以下是這部分的常用API整理
API |
用法 |
intuloop_init(void) |
創建epoll句柄 |
voiduloop_done(void) |
註銷epoll模塊 |
intuloop_fd_add(struct uloop_fd *sock,unsigned int flags) |
將需要監聽的fd以及要監聽的事件註冊到epoll(默認擁有水平觸發和非阻塞的特性) @sock- 需要被epoll監聽的fd管理塊,主要記錄了自己創建的fd、事件處理回調 @flags- 監聽的事件類型,必須至少包含ULOOP_READ或ULOOP_WRITE或ULOOP_PRI(這個是本人補丁追加的) |
intuloop_fd_delete(struct uloop_fd *sock) |
從epoll監聽池中刪除指定的fd @sock- 要從監聽池中刪除的fd管理塊 |
voiduloop_run(void) |
事件驅動模型的主循環,在這裏面進行[等待事件/超時->處理事件/超時->等待事件]的循環,通過信號退出 |
intuloop_timeout_set(struct uloop_timeout *timeout,int msecs) |
給定時器設置一個超時值,並激活 @timeout- 定時器模塊,主要記錄了超時值、超時處理函數 @msecs- 超時值,單位ms |
intuloop_timeout_cancel(struct uloop_timeout *timeout) |
關掉定時器 @timeout- 要關掉的定時器模塊 |
代碼示範(未處理返回值):
voidrecv_handler(struct uloop_fd *h,uint32_t events)
{
/*do your recv handler*/
}
voidtimeout_handler(struct uloop_timeout *timer)
{
/*do your timeout handler*/
}
intmain(int argc,char **argv)
{
structuloop_fd myfd;
structuloop_timeout mytimer;
uloop_init();
myfd.fd= socket(PF_PACKET,SOCK_RAW,htons(ETH_P_PAE));
myfd.cb= recv_handler;
uloop_fd_add(&myfd);
mytimer.cb= timeout_handler;
uloop_timeout_set(&mytimer,100);
uloop_run();
uloop_fd_delete(&myfd);
close(myfd.fd);
uloop_timeout_cancel(&mytimer);
uloop_done();
return0;
}
1.3.鏈表
libubox的鏈表是完全參照內核鏈表的設計理念,跟普通鏈表的區別在於,它不是將數據結構塞入鏈表,而是將鏈表節點塞入數據結構。
以下是鏈表的數據結構:
structlist_head {
structlist_head *next;
structlist_head *prev;
};
structlist_head就是抽象出來的鏈表模塊,使用方法就是將其插入到自定義的結構中,範例如下:
structfox {
intlen;
intweight;
structlist_head list;
};
上述結構中,fox中的list.next指向下一個元素,list.prev指向前一個元素。
通常,對鏈表的管理還需要一個標準的索引指針指向整個鏈表,即鏈表的頭指針,這個特殊的頭指針事實上也就是一個常規的list_head.
以下是鏈表操作API:
LIST_HEAD(name) |
創建並初始化鏈表頭節點(前驅和後繼都指向自己) |
staticinline void INIT_LIST_HEAD(struct list_head *list) |
初始化鏈表頭節點 |
staticinline void list_empty(const struct list_head *head) |
判斷鏈表是否爲空,空返回1,非空返回0 |
staticinline void list_del(struct list_head *entry) |
刪除指定節點 |
staticinline void list_add(struct list_head *new,struct list_head *head) |
節點從頭部插入鏈表 |
staticinline void list_add_tail(struct list_head *new,struct list_head*head) |
節點從尾部插入鏈表 |
list_entry(ptr,type,field) |
根據鏈表模塊地址反推得到父結構地址 @ptr- 鏈表模塊地址(比如上面的“&fox.list”) @type- 父結構類型(比如上面的”structfox”) @field- 父結構中鏈表模塊的名字(比如上面的”list”) |
list_for_each_entry(p,h,field) |
遍歷鏈表的父結構(不支持遍歷中刪除操作) @p- 遍歷出來的父結構指針 @h- 鏈表頭節點指針 @field- 父結構中鏈表模塊的名字 |
list_for_each_entry_safe(p,n,h,field) |
遍歷鏈表的父結構(可以支持遍歷中刪除操作) @n- 備份指針 |
備註:操作鏈表(添加、刪除、移動、合併)的時間複雜度爲O(1),意味着無論操作的鏈表大小以及參數如何,他們都是在恆定時間內完成的;
遍歷鏈表的時間複雜度爲O(n),n是鏈表包含的節點數目,意味着遍歷時間跟節點數量呈線性關係
1.4.數據傳輸單元
libubox提供了一套通用的數據傳輸機制,數據類型可以是
enumblobmsg_type {
BLOBMSG_TYPE_UNSPEC,
BLOBMSG_TYPE_ARRAY,
BLOBMSG_TYPE_TABLE,
BLOBMSG_TYPE_STRING,
BLOBMSG_TYPE_INT64,
BLOBMSG_TYPE_INT32,
BLOBMSG_TYPE_INT16,
BLOBMSG_TYPE_INT8,
};
其中array和table兩種數據類型還支持嵌套使用。
structblob_buf表示一個完整的數據傳輸單元,這個數據結構是這套數據傳輸機制的核心,需要注意的是,blob_buf使用完後一定要調用blob_buf_free銷燬,特別是當定義成局部變量時!
以下是這套數據傳輸機制的常用API整理:
intblob_buf_init(struct blob_buf *buf,int id) |
blob_buf初始化,也可以理解爲復位 @buf- 數據傳輸單元 @id- 數據類型 |
voidblob_buf_free(struct blob_buf *buf) |
註銷blob_buf,實際就是釋放blob_buf使用過程中申請的空間 |
staticinline const char *blobmsg_name(const struct blob_attr *attr) |
獲取消息的名字 |
staticinline int blobmsg_type(const struct blob_attr *attr) |
獲取消息的類型 |
staticinline int blobmsg_len(const struct blob_attr *attr) |
獲取消息值長度 |
staticinline uint8_t blobmsg_get_u8(struct blob_attr *attr) |
獲取uint8_t類型的消息值 |
staticinline uint16_t blobmsg_get_u16(struct blob_attr *attr) |
獲取uint16_t類型的消息值 |
staticinline uint32_t blobmsg_get_u32(struct blob_attr *attr) |
獲取uint32_t類型的消息值 |
staticinline uint64_t blobmsg_get_u64(struct blob_attr *attr) |
獲取uint64_t類型的消息值 |
staticinline char *blobmsg_get_string(struct blob_attr *attr) |
獲取字符串類型的消息值 |
staticinline int blobmsg_add_u8(struct blob_buf *buf,const char*name,uint8_t val) |
添加一條uint8_t類型的消息 @buf- 數據傳輸單元 @name- 消息名 @val- uint8_t類型的消息值 |
staticinline int blobmsg_add_u16(struct blob_buf *buf,const char*name,uint16_t val) |
添加一條uint16_t類型的消息 @buf- 數據傳輸單元 @name- 消息名 @val- uint16_t類型的消息值 |
staticinline int blobmsg_add_u32(struct blob_buf *buf,const char*name,uint32_t val) |
添加一條uint32_t類型的消息 @buf- 數據傳輸單元 @name- 消息名 @val- uint32_t類型的消息值 |
staticinline int blobmsg_add_u64(struct blob_buf *buf,const char*name,uint64_t val) |
添加一條uint64_t類型的消息 @buf- 數據傳輸單元 @name- 消息名 @val- uint64_t類型的消息值 |
staticinline int blobmsg_add_string(struct blob_buf *buf,const char*name,const char *string) |
添加一條字符串類型的消息 @buf- 數據傳輸單元 @name- 消息名 @val- 字符串類型的消息值 |
staticinline void *blobmsg_open_array(struct blob_buf *buf,const char*name) |
開啓一個array類型的消息 @buf- 數據傳輸單元 @name- 消息名 返回值-指向這個開啓的array,也就是下面用到的”cookie” |
staticinline void blobmsg_close_array(struct blob_buf *buf,void *cookie) |
關閉一個array類型的消息 @buf- 數據傳輸單元 @cookie- 指向這個開啓的array |
staticinline void *blobmsg_open_table(struct blob_buf *buf,const char*name) |
開啓一個table類型的消息 @buf- 數據傳輸單元 @name- 消息名 返回值-指向這個開啓的array,也就是下面用到的”cookie” |
staticinline void blobmsg_close_table(struct blob_buf *buf,void *cookie) |
關閉一個table類型的消息 @buf- 數據傳輸單元 @cookie- 指向這個開啓的table |
備註:完成一條array或table消息的創建需要成對調用開啓、關閉兩個函數。
1.5. json
暫略
1.6. avl樹
暫略
1.7.md5
暫略
1.8socket
目前只封裝了unix-sock和inet-sock,個人覺得比較雞肋,暫不分析。
-
libubus
ubus是openwrt引入的一個消息總線,類似於桌面linux系統中的dbus,其設計理念也基本一致,就是提供系統級的IPC和RPC。
2.1基本原理
整套ubus基於libubox庫實現,通信的基礎是unix-sock,ubus提供了一個後臺服務器ubusd進行所有ubus消息的中轉處理。
所以,對於開發者來說,關注點就全放在客戶端。ubus將客戶端分爲2種角色:
服務提供者
服務消費者
備註:當然某個客戶端既可以是一些服務的提供者,同時又可以是另一些服務的消費者
ubus對其上面承載的消息格式進行了定義:採用json消息格式。
ubus將服務抽象成爲“對象”和“方法”,一個對象可以包含多個方法。“對象”必須先註冊到ubus後臺服務器,才能被消費者調用。
ubus支持以“閱訂-通知”的方式進行進程通信,即進程A提供閱訂服務,其他進程可以選擇閱訂或退訂該服務,進程A就可以向所有閱訂者廣播推送通知。
2.1.C接口使用方法
libubus就是其C接口庫,以下是常用API整理:
structubus_context *ubus_connect(const char *path) |
創建ubus客戶端併發起連接 @path- ubus後臺服務器socket地址,默認就是/var/run/ubus.sock 返回值-成功則返回一個ubus客戶端控制塊 |
voidubus_free(struct ubus_context *ctx) |
註銷ubus客戶端 |
staticinline void ubus_add_uloop(struct ubus_context *ctx) |
ubus客戶端註冊到libubox庫epoll監聽池中 |
intubus_add_object(struct ubus_context *ctx,struct ubus_object *obj) |
ubus服務提供者調用,用來註冊一個“對象” @ctx- ubus客戶端控制塊 @obj- 要註冊的對象控制塊 |
intubus_lookup_id(struct ubus_context *ctx,const char *path,uint32_t*id) |
ubus服務消費者調用,根據“對象”名查找對應的id號 @ctx- ubus客戶端控制塊 @path- 對象名 @id- 記錄查到的對象id |
intubus_register_subscriber(struct ubus_context *ctx,structubus_subscriber *obj) |
ubus客戶端註冊一個閱訂模塊,後續閱訂服務用 @ctx- ubus客戶端控制塊 @obj- 要註冊的閱訂控制塊 |
intubus_invoke(struct ubus_context *ctx,uint32_t obj const char*method,struct blob_attr *msg,ubus_data_handler_t cb,void*priv,int timeout) |
ubus服務消費者調用,用於調用一個指定服務(同步調用) @ctx- ubus客戶端控制塊 @obj- 對象id @method- 指定“對象”包含的某個“方法”名 @msg- 具體的調用消息 @cb- 收到返回消息的處理函數 @priv- 用戶自定義項,cb函數中可以使用 @timeout- 本次調用的超時時間 |
intubus_invoke_async(struct ubus_context *ctx,uint32_t obj const char*method,struct blob_attr *msg,struct ubus_request *req) |
ubus服務消費者調用,用於調用一個指定服務(異步調用) @ctx- ubus客戶端控制塊 @obj- 對象id @method- 指定“對象”包含的某個“方法”名 @msg- 具體的調用消息 @req- 用來記錄本次異步調用的相關信息 |
intubus_send_reply(struct ubus_context *ctx,struct ubus_request_data*req,struct blob_attr *msg) |
ubus服務提供者調用,用於回覆服務消費者的調用請求 @ctx- ubus客戶端控制塊 @req- 用來記錄本次調用的相關信息 @msg- 具體的回覆消息 |
intubus_notify(struct ubus_context *ctx,struct ubus_object *obj,constchar *type,struct blob_attr *msg,int timeout) |
ubus客戶端向所有閱訂者廣播推送通知 @ctx- ubus客戶端控制塊 @obj- 自身對象控制塊 @type- 通知類型名 @msg- 具體的通知消息 @timeout- 本次通知的超時值 |
2.2使用範例(不考慮異常情況)
2.2.1作爲服務提供者
enum{
TEST_HELLO,
_MAX_TEST,
};
staticstruct ubus_context *ctx;
staticconst struct blobmsg_policy policy[] = {
[TEST_HELLO]{.name= “test_hello”, .type = BLOBMSG_TYPE_STRING},
};
staticint test_handler(struct ubus_context *ctx,struct ubus_object*obj,struct ubus_request_data *req,const char *method,structblob_attr *msg)
{
/*do your handler*/
}
staticstruct ubus_method methods[] = {
UBUS_METHOD(“test_hello”,test_handler,policy),
};
staticstruct ubus_object_type object_type =UBUS_OBJECT_TYPE(“test”,methods);
staticstruct ubus_object object = {
.name= “test”,
.type= &object_type,
.methods= methods,
.n_methodds= ARRAY_SIZE(methods),
};
intmain(int argc,char **argv)
{
uloop_init();
ctx= ubus_connect(NULL);
uloop_add_uloop(ctx);
ubus_add_object(ctx,&object);
uloop_run();
ubus_free(ctx);
uloop_done();
return0;
}
2.2.2作爲服務消費者
staticstruct blob_buf b;
staticvoid reply_handler(struct ubus_request *req,int type,struct blob_attr*msg)
{
/*do your handler*/
}
staticint call_server(struct ubus_context *ctx)
{
intid;
ubus_lookup_id(ctx,”test”,&id);
blob_buf_init(&b,0);
blobmsg_add_string(&b,”name”,”value”);
intret = -1;
returnubus_invoke(ctx,id,”test_hello”,b.head,reply_handler,&ret,1000);
}
intmain(int argc,char **argv)
{
uloop_init();
ctx= ubus_connect(NULL);
uloop_add_uloop(ctx);
call_server();
uloop_run();
ubus_free(ctx);
uloop_done();
return0;
}
2.2.3作爲閱訂者
staticstruct ubus_subscriber event;
staticvoid remove_handler(struct ubus_context *ctx,struct ubus_subscriber*s,uint32_t id)
{
/*doyour handler*/
}
staticint event_handler(struct ubus_context *ctx,struct ubus_object*obj,struct ubus_request_data *req,const char *method,structblob_attr *msg)
{
/*doyour handler*/
}
intmain(int argc,char **argv)
{
uloop_init();
ctx= ubus_connect(NULL);
uloop_add_uloop(ctx);
ubus_register_subscriber(ctx,&event);
event.remove_cb= remove_handler;
event.cb= event_handler;
ubus_lookup_id(ctx,”test”,id);
ubus_subscribe(ctx,&event,id);
ubus_run();
ubus_free(ctx);
uloop_done();
return0;
}
2.2.4作爲通知方
intmain(int argc,char **argv)
{
uloop_init();
ctx= ubus_connect(NULL);
uloop_add_uloop(ctx);
ubus_add_object(ctx,&object);
ubus_notify(ctx,&object,”notify”,NULL,-1);
ubus_run();
ubus_free(ctx);
uloop_done();
return0;
}
3.libuci
暫略