分享

openwrt 常用库用法

 薛董_艾瑞 2018-03-19

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

暂略


以上就是openwrt 常用库用法的全文介绍,希望对您学习和使用c++编程开发有所帮助.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多