ubus
openwrt提供了一个系统总线ubus,类似linux桌面操作系统的d-bus,目标是提供系统级的进程间通信(IPC)功能。
为了提供各种后台进程和应用程序之间的通信机制,ubus被开发出来,由3部分组成:精灵进程,接口库和实用工具。
工程的核心是ubusd精灵进程,它提供了一个总线层,在系统启动时运行,负责进程间的消息路由和传递。其他进程注册到ubusd进程进行消息的发送和接收。这个接口是用linux文件socket和TLV手法消息实现的。每一个进程在指定命名空间下注册自己的路径。每一个路径都可以提供带有各种参数的多个函数处理过程。函数处理过程程序可以在完成处理后返回消息。
接口库名称为libubus.so,其他进程可以通过该动态链接库来简化对ubus总线的访问。
实用工具ubus是提供命令行的接口调用工具。可以基于该工具来进行脚本编程,也可以使用ubus来诊断问题。
ubus是为进程间发送消息而设计的,不适合传输大量数据(进程间传输大量数据应采用共享内存)。
网址:http://git.openwrt.org/project/ubus.git
$ ls ubus-2015-05-25
cli.c libubus.h libubus-req.c ubus_common.h ubusd_id.c ubusd_proto.c
CMakeLists.txt libubus-internal.h libubus-sub.c ubusd.c ubusd_id.h ubusmsg.h
examples libubus-io.c lua ubusd_event.c ubusd_obj.c
libubus.c libubus-obj.c systemd ubusd.h ubusd_obj.h
ubusd: ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c
libubus: libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c
cli/ubus: cli.c
1. ubusd
/etc/init.d/ubus中提供ubusd进程的启动,在系统进程启动完成后立即启动。他是在网络进程netifd之前启动的,该进程提供一个文件套接字接口和其他应用程序通信。
ubusd采用SOCK_STREAM的Unix域套接字实现与服务进程间通讯。
提供的功能:
1)提供注册对象和方法供其他实体调用。
2)调用其他应用程序提供的注册对象的控制接口。
3)在特定对象上注册监听事件。
4)向特定对象发送事件消息。
ubus将消息处理抽象为对象object和方法method的概念。一个对象中包含多个方法。对象和方法都有自己的名字,发送请求方在消息中指定要调用的对象和方法名字即可。
ubus的另外一个概念就是订阅(subscriber)。客户端需要向服务器注册收到特定消息时的处理方法。这样当服务器在状态发生改变时会通过ubus总线来通知客户端。
ubus可用于两个进程之间通信,进程之间以TLV格式传递消息。ubus能够以json格式和用户进行数据交换。常见场景(应用详见ubus实现进程间通信举例):
1)客户端/服务器模式。
2)订阅通知模式(struct ubus_subscriber)。
3)事件模式(ubus_event_handler)。
ubusd是一种总线消息服务器,任何消息均通过ubusd进程传递,因此多个进程在相互通信时,均通过ubus收发消息。
编译安装
1)去除lua支持
#ADD_SUBDIRECTORY(lua)
2)编译
mkdir build; cd build; cmake ..; make ;make install
[ %] Built target ubus
[ %] Built target ubusd
[ %] Built target cli
[ %] Built target server
[%] Built target client
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/lib/libubus.so
-- Installing: /usr/local/bin/ubus
-- Set runtime path of "/usr/local/bin/ubus" to ""
-- Installing: /usr/local/sbin/ubusd
-- Up-to-date: /usr/local/include/ubusmsg.h
-- Up-to-date: /usr/local/include/ubus_common.h
-- Up-to-date: /usr/local/include/libubus.h
-- Installing: /usr/lib/systemd/system/ubus.socket
-- Installing: /usr/lib/systemd/system/ubus.service
2. libubus
1) 数据结构
struct ubus_event_handler {
struct ubus_object obj;
ubus_event_handler_t cb;
};
struct ubus_context {
struct list_head requests;
struct avl_tree objects; /** client端object链表头 */
struct list_head pending;
struct uloop_fd sock; /** client端sock对象 */
uint32_t local_id; /** ubusd分配的client id */
uint16_t request_seq;
int stack_depth;
/** 断开连接回调函数 */
void (*connection_lost)(struct ubus_context *ctx);
struct {
struct ubus_msghdr hdr;
char data[UBUS_MAX_MSGLEN];
} msgbuf; /** 通信报文 */
};
struct ubus_object_data {
uint32_t id;
uint32_t type_id;
const char *path;
struct blob_attr *signature;
};
struct ubus_request_data {
uint32_t object;
uint32_t peer;
uint16_t seq;
/* internal use */
bool deferred;
int fd;
};
struct ubus_request {
struct list_head list;
struct list_head pending;
int status_code;
bool status_msg;
bool blocked;
bool cancelled;
bool notify;
uint32_t peer;
uint16_t seq;
ubus_data_handler_t raw_data_cb;
ubus_data_handler_t data_cb;
ubus_fd_handler_t fd_cb;
ubus_complete_handler_t complete_cb;
struct ubus_context *ctx;
void *priv;
};
struct ubus_notify_request {
struct ubus_request req;
ubus_notify_complete_handler_t status_cb;
ubus_notify_complete_handler_t complete_cb;
uint32_t pending;
uint32_t id[UBUS_MAX_NOTIFY_PEERS + ];
};
struct ubus_auto_conn {
struct ubus_context ctx;
struct uloop_timeout timer;
const char *path;
ubus_connect_handler_t cb;
};
2)接口函数
* 初始化client端context结构,并连接ubusd
struct ubus_context *ubus_connect(const char *path)
* 与ubus_connect()函数基本功能相同,但此函数在连接断开后会自动进行重连
void ubus_auto_connect(struct ubus_auto_conn *conn)
* 注册新事件
int ubus_register_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *pattern)
* 发出事件消息
int ubus_send_event(struct ubus_context *ctx, const char *id, struct blob_attr *data) * 向ubusd查询指定UBUS_ATTR_OBJPATH对应对象信息内容
* 内容通过输入回调函数ubus_lookup_handler_t由调用者自行处理
int ubus_lookup(struct ubus_context *ctx, const char *path, ubus_lookup_handler_t cb, void *priv) * 向ubusd查询指定UBUS_ATTR_OBJPATH对应的ID号
int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id) * 向uloop注册fd
static inline void ubus_add_uloop(struct ubus_context *ctx)
{
uloop_fd_add(&ctx->sock, ULOOP_BLOCKING | ULOOP_READ);
}
3. libubus-obj
数据结构
struct ubus_object {
struct avl_node avl; /** 关系到struct ubus_context的objects */
const char *name; /** UBUS_ATTR_OBJPATH */
uint32_t id; /** 由ubusd server分配的obj id */
const char *path;
struct ubus_object_type *type;
/** 第1次被订阅或最后1次补退订时被调用 */
ubus_state_handler_t subscribe_cb;
bool has_subscribers; /** 此对象是否被订阅 */
const struct ubus_method *methods; /** 方法数组 */
int n_methods; /** 方法数组个数 */
};
struct ubus_object_type {
const char *name;
uint32_t id; /** 由ubusd server分配的obj type id */
const struct ubus_method *methods; /** 方法数组 */
int n_methods; /** 方法数组个数 */
};
struct ubus_method {
const char *name; /** 方法名称 */
ubus_handler_t handler; /** 方法处理回调函数 */
unsigned long mask; /** 参数过滤掩码 */
const struct blobmsg_policy *policy; /** 参数过滤列表 */
int n_policy; /** 参数过滤列表个数 */
};
#define UBUS_OBJECT_TYPE(_name, _methods) \
{ \
.name = _name, \
.id = , \
.n_methods = ARRAY_SIZE(_methods), \
.methods = _methods \
} #define __UBUS_METHOD_NOARG(_name, _handler) \
.name = _name, \
.handler = _handler #define __UBUS_METHOD(_name, _handler, _policy) \
__UBUS_METHOD_NOARG(_name, _handler), \
.policy = _policy, \
.n_policy = ARRAY_SIZE(_policy) #define UBUS_METHOD(_name, _handler, _policy) \
{ __UBUS_METHOD(_name, _handler, _policy) } #define UBUS_METHOD_MASK(_name, _handler, _policy, _mask) \
{ \
__UBUS_METHOD(_name, _handler, _policy),\
.mask = _mask \
} #define UBUS_METHOD_NOARG(_name, _handler) \
{ __UBUS_METHOD_NOARG(_name, _handler) }
接口函数
* client端向ubusd server请求增加一个新object
int ubus_add_object(struct ubus_context *ctx, struct ubus_object *obj) * client端向ubusd server请求删除一个object
int ubus_remove_object(struct ubus_context *ctx, struct ubus_object *obj) * 处理收到与object相关报文
void __hidden ubus_process_obj_msg(struct ubus_context *ctx, struct ubus_msghdr *hdr) * 处理UBUS_MSG_INVOKE报文
static void
ubus_process_invoke(struct ubus_context *ctx, struct ubus_msghdr *hdr,
struct ubus_object *obj, struct blob_attr **attrbuf) * 处理UBUS_MSG_UNSUBSCRIBE报文
static void
ubus_process_unsubscribe(struct ubus_context *ctx, struct ubus_msghdr *hdr,
struct ubus_object *obj, struct blob_attr **attrbuf) * 处理UBUS_MSG_NOTIFY报文
static void
ubus_process_notify(struct ubus_context *ctx, struct ubus_msghdr *hdr,
struct ubus_object *obj, struct blob_attr **attrbuf)
4. 示例
1) 下面看下logd中ubus操作,ubos的logd注册了一个log对象,两个方法read和write。
static const struct ubus_method log_methods[] = {
{ .name = "read", .handler = read_log, .policy = &read_policy, .n_policy = },
{ .name = "write", .handler = write_log, .policy = &write_policy, .n_policy = },
};
static struct ubus_object_type log_object_type =
UBUS_OBJECT_TYPE("log", log_methods);
static struct ubus_object log_object = {
.name = "log",
.type = &log_object_type,
.methods = log_methods,
.n_methods = ARRAY_SIZE(log_methods),
};
static const struct blobmsg_policy read_policy =
{ .name = "lines", .type = BLOBMSG_TYPE_INT32 }; static const struct blobmsg_policy write_policy =
{ .name = "event", .type = BLOBMSG_TYPE_STRING };
看下两个方法的处理函数(根据policy解析blobmsg):
static int
read_log(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct client *cl;
struct blob_attr *tb;
struct log_head *l;
int count = ;
int fds[];
int ret; if (msg) { // 解析blobmsg
blobmsg_parse(&read_policy, , &tb, blob_data(msg), blob_len(msg));
if (tb)
count = blobmsg_get_u32(tb);
}
// 具体业务处理
if (pipe(fds) == -) {
fprintf(stderr, "logd: failed to create pipe: %s\n", strerror(errno));
return -;
}
ubus_request_set_fd(ctx, req, fds[]);
cl = calloc(, sizeof(*cl));
cl->s.stream.notify_state = client_notify_state;
cl->fd = fds[];
ustream_fd_init(&cl->s, cl->fd);
list_add(&cl->list, &clients);
l = log_list(count, NULL);
while ((!tb || count) && l) {
blob_buf_init(&b, );
blobmsg_add_string(&b, "msg", l->data);
blobmsg_add_u32(&b, "id", l->id);
blobmsg_add_u32(&b, "priority", l->priority);
blobmsg_add_u32(&b, "source", l->source);
blobmsg_add_u64(&b, "time", l->ts.tv_sec * 1000LL);
l = log_list(count, l);
ret = ustream_write(&cl->s.stream, (void *) b.head, blob_len(b.head) + sizeof(struct blob_attr), false);
blob_buf_free(&b);
if (ret < )
break;
}
return ;
}
static int
write_log(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb;
char *event; if (msg) { //解析blobmsg
blobmsg_parse(&write_policy, , &tb, blob_data(msg), blob_len(msg));
if (tb) {
event = blobmsg_get_string(tb);
log_add(event, strlen(event) + , SOURCE_SYSLOG);
}
} return ;
}
上层调度
static struct ubus_auto_conn conn;
uloop_init();
conn.cb = ubus_connect_handler;
ubus_auto_connect(&conn);
uloop_run(); static void
ubus_connect_handler(struct ubus_context *ctx)
{
int ret; ret = ubus_add_object(ctx, &log_object);
if (ret) {
fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
exit();
}
fprintf(stderr, "log: connected to ubus\n");
}
logd是守护进程,永不退出,所以没必要remove object。
2) 客户端/服务器模式
/*
ubus_invoke()的声明如下:
int ubus_invoke(struct ubus_context *ctx, uint32_t obj, const char *method,
struct blob_attr *msg, ubus_data_handler_t cb, void *priv, int timeout); 其中cb参数是消息回调函数,其函数类型定义为:
typedef void (*ubus_data_handler_t)(struct ubus_request *req,
int type, struct blob_attr *msg);
参数type是请求消息的类型,如UBUS_MSG_INVOKE,UBUS_MSG_DATA等。
参数msg存放从服务端得到的回复消息,struct blob_attr类型,同样用blobmsg_parse()来解析。
参数req保存了请求方的消息属性,其中req->priv即ubus_invoke()中的priv参数,
用这个参数可以零活的传递一些额外的数据。
*/
// ser.c
#include <stdio.h>
#include <unistd.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubox/blobmsg_json.h>
#include <libubus.h> static int stu_no = ;
static char *ubus_socket = NULL;
struct ubus_context *ctx = NULL;
static struct blob_buf b; enum {
STU_NO,
__STU_MAX
}; static const struct blobmsg_policy stu_policy[__STU_MAX] ={
[STU_NO] = {.name = "no", .type = BLOBMSG_TYPE_INT32 }
}; static int stu_add(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__STU_MAX]; blobmsg_parse(stu_policy, ARRAY_SIZE(stu_policy), tb, blob_data(msg), blob_len(msg)); if(tb[STU_NO])
stu_no += blobmsg_get_u32(tb[STU_NO]); blob_buf_init(&b, );
blobmsg_add_u32(&b, "no", stu_no);
ubus_send_reply(ctx, req, b.head); return ;
} static int stu_sub(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__STU_MAX]; blobmsg_parse(stu_policy, ARRAY_SIZE(stu_policy), tb, blob_data(msg), blob_len(msg)); if(tb[STU_NO])
stu_no -= blobmsg_get_u32(tb[STU_NO]); blob_buf_init(&b, );
blobmsg_add_u32(&b, "no", stu_no);
ubus_send_reply(ctx, req, b.head); return ;
}
static const struct ubus_method stu_methods[] = {
/*
{ .name = "add", .handler = stu_add, .policy = stu_policy, .n_policy = 1 },
{ .name = "sub", .handler = stu_sub, .policy = stu_policy, .n_policy = 1 }
*/
UBUS_METHOD("add", stu_add, stu_policy),
UBUS_METHOD("sub", stu_sub, stu_policy),
}; static struct ubus_object_type stu_object_type =
UBUS_OBJECT_TYPE("stu", stu_methods); static struct ubus_object stu_object = {
.name = "stu",
.type = &stu_object_type,
.methods = stu_methods,
.n_methods = ARRAY_SIZE(stu_methods)
}; static void ubus_add_fd(void)
{
ubus_add_uloop(ctx); #ifdef FD_CLOEXEC
fcntl(ctx->sock.fd, F_SETFD,
fcntl(ctx->sock.fd, F_GETFD) | FD_CLOEXEC);
#endif
} static void ubus_reconn_timer(struct uloop_timeout *timeout)
{
static struct uloop_timeout retry =
{ .cb = ubus_reconn_timer,};
int t = ; if (ubus_reconnect(ctx, ubus_socket) != ) {
uloop_timeout_set(&retry, t * );
return;
} ubus_add_fd();
} static void ubus_connection_lost(struct ubus_context *ctx)
{
ubus_reconn_timer(NULL);
}
int main(int argc, char **argv)
{
char ch;
int ret = ; while((ch = getopt(argc, argv, "s:"))!= -){
switch(ch){
case 's':
ubus_socket = optarg;
break;
default: /* ? */
break;
}
} uloop_init(); ctx = ubus_connect(ubus_socket);
if(!ctx){
printf("ubus connect error.\n");
return -;
}
ctx->connection_lost = ubus_connection_lost; ret = ubus_add_object(ctx, &stu_object);
if(ret){
printf("Failed to add object to ubus:%s\n", ubus_strerror(ret));
return ;
} //ubus_add_uloop(ctx);
ubus_add_fd();
uloop_run(); if(ctx)
ubus_free(ctx); uloop_done();
return ;
}
#include <stdio.h>
#include <unistd.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubox/blobmsg_json.h>
#include <libubus.h> static struct blob_buf b; static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg)
{
char *str;
if(!msg)
return; str = blobmsg_format_json_indent(msg, true, );
printf("%s\n", str); free(str);
} static int client_main(struct ubus_context *ctx, int argc, char**argv)
{
uint32_t id;
int ret; printf("argc =%d, argv[0] =%s\n", argc, argv[]);
if(argc != )
return -; blob_buf_init(&b, );
if(!blobmsg_add_json_from_string(&b, argv[])){
printf("Failed to parse message data\n");
return -;
} ret = ubus_lookup_id(ctx, argv[], &id);
if(ret)
return ret; return ubus_invoke(ctx, id, argv[], b.head, receive_call_result_data, NULL, *);
} int main(int argc, char **argv)
{
struct ubus_context *ctx = NULL;
char ch;
char *ubus_socket = NULL;
int ret = ; while((ch = getopt(argc, argv, "s:"))!= -){
switch(ch){
case 's':
ubus_socket = optarg;
break;
default: /* ? */
break;
}
} argc -= optind;
argv += optind; ctx = ubus_connect(ubus_socket);
if(!ctx){
printf("ubus connect error.\n");
return -;
} ret = client_main(ctx, argc, argv);
if(ret < )
return ret; if(ctx)
ubus_free(ctx); return ;
}
编译:
gcc cli.c -Wall -lubox -lubus -lblobmsg_json -o cli
gcc ser.c -Wall -lubox -lubus -lblobmsg_json -o ser
执行:
ubuntu:~/test/ubus/test$ sudo ./cli stu add '{"no":20}'
argc =, argv[] =stu
{
"no":
}
ubuntu:~/test/ubus/test$ sudo ./cli stu add '{"no":20}'
argc =, argv[] =stu
{
"no":
}
ubuntu:~/test/ubus/test$ sudo ./cli stu add '{"no":20}'
argc =, argv[] =stu
{
"no":
}
参考文档:
在 Linux 上实现基于 Socket 的多进程实时通信 IBM
ubus的更多相关文章
- ubus socket always in connecting status
When we try to transplant ubus to uclinux, ubusd can't run but "ubus list" will hang up. 1 ...
- OpenWrt中开启usb存储和samba服务
在从官网安装的WNDR3800 15.05.1版本OpenWrt中, 不带usb存储支持以及samba, 需要另外安装 1. 启用usb支持 USB Basic Support https://wik ...
- CWMP开源代码研究2——easycwmp安装和学习
声明:本文是对开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文所有笔记和代码可以到csdn下载:http://download.csdn.n ...
- easycwmp在开发板上的配置
原创作品,转载请注明出处 copyright:weishusheng 2015.3.18 email:642613208@qq.com 平台: Linux version 2.6.32-279.e ...
- easycwmp的交叉编译
原创作品,转载请注明出处 copyright:weishusheng 2015.3.18 email:642613208@qq.com 平台: Linux version 2.6.32-279.e ...
- openacs与easycwmp的对接
原创作品,转载请注明出处 copyright:weishusheng 2015.3.18 email:642613208@qq.com 平台: Linux version 2.6.32-279.e ...
- OpenWrt镜像编译和ipv6支持
离成功实现路由器刷OpenWrt.接入校园网差不多一年了.路由工作比较稳定,还是很满意的. 这次回来有个新发现:学校有原生ipv6支持,在win7和ubuntu下什么都不用设置,自动获取global ...
- Openwrt 初探
最近想研究一下Openwrt,于是开始搭建openwrt环境,虽然现在没有现成的板子,但是 可以先编译起来. openwrt的特点是基于下载 -> patch -> 编译 的一个工作模式, ...
- vim下使用ctags+taglist
好几年前用过,但是后来就生疏了,好其次都没法鼓起勇气再捡起来...今天不得不用,那既然捡起来了,就好好的记录一下. 简介及安装 ctags是一个应用程序,可以用它来生产当前目录下所有c文件中变量和函数 ...
随机推荐
- ElementUI使用问题记录:设置路由+iconfont图标+自定义表单验证
一.关于导航怎么设置路由 1.在el-menu这个标签的属性中添加 router ,官方文档的解释是:启用vue-router 这种模式 2.在el-menu-item标签中的index属性直接书写路 ...
- Log文件太大,手机ROM空间被占满
客户要装车,进行项目验收了. 今天拿着几台手机去客户处,其中有一台手机从昨天晚上开始就一直开着我们的APP,今天早晨打开手机发现APP没有反应了. 在程序列表中将其杀掉,然后再启动程序,发现程序不能启 ...
- (转)Scala的“=>”符号简介
Scala中的=>符号可以看做是创建函数实例的语法糖.例如:A => T,A,B => T表示一个函数的输入参数类型是“A”,“A,B”,返回值类型是T.请看下面这个实例: scal ...
- (六)SSO之CAS框架扩展 改动CAS源代码实现与ESS动态password验证对接
题记: 偶尔的偶尔我们会听到这个站点的数据泄露了,那个站点的用户数据泄露了.让用户又一次改动登录password,所以,对于用户数据安全性越发的引起我们的重视了,尤其是一些保密性要求高的站点.更须要添 ...
- X 开启新的 X window 服务器
X -ac -terminate :3 开启服务,禁用访问控制约束,重置时关闭服务,$DISPLAY=:3 此时设置用户程序的 DISPLAY 值,就可在指定的 X 服务器上打开程序,比如 DISPL ...
- redis学习笔记——RDB和AOF持久化一
为防止数据丢失,需要将 Redis 中的数据从内存中 dump 到磁盘,这就是持久化.Redis 提供两种持久化方式:RDB 和 AOF.Redis 允许两者结合,也允许两者同时关闭. RDB 可以定 ...
- Android传感器的介绍
在Android2.3 gingerbread系统中,google提供了11种传感器供应用层使用. #define SENSOR_TYPE_ACCELEROMETER 1 //加速度#de ...
- WCF 之 概述
WCF全称是Windows Communication Foundation,它是.NET3.0的重要组成部分,用来解决Windows下的一些通信方面的问题.WCF是Microsoft平台上的SOA架 ...
- 【MVC5】使用域用户登录+记住我
1.配置Web.config文件 添加域链接字符串 <connectionStrings> <add name="ADConnectionString" conn ...
- android精确绘制文字位置的方法
android 中使用Canvas的drawText绘制文本的位置,是基于基线的. 例如以下图: 当中字母Q的小尾巴在横线以下了. 怎么样找准字母的中心位置呢? 先看以下的样例:(右边的数字,表示字体 ...