openwrt 常用庫用法

openwrt常用庫用法


  1. libubox

1.1.概述

libuboxopenwrt的一個基礎庫,openwrt下大部分應用都是基於它開發的(ubusuhttpduci等)。

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_READULOOP_WRITEULOOP_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,

};

其中arraytable兩種數據類型還支持嵌套使用。

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

備註:完成一條arraytable消息的創建需要成對調用開啓、關閉兩個函數。


1.5. json

暫略

1.6. avl

暫略

1.7.md5

暫略

1.8socket

目前只封裝了unix-sockinet-sock,個人覺得比較雞肋,暫不分析。


  1. libubus

ubusopenwrt引入的一個消息總線,類似於桌面linux系統中的dbus,其設計理念也基本一致,就是提供系統級的IPCRPC

2.1基本原理

整套ubus基於libubox庫實現,通信的基礎是unix-sockubus提供了一個後臺服務器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客戶端註冊到libuboxepoll監聽池中

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

暫略


發佈了61 篇原創文章 · 獲贊 15 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章