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++编程开发有所帮助. |
|