openwrt的ubus进程间通信等使用blob & blobmsg格式封装,blob & blobmsg 由libubox库提供。
blobmsg json有ubox包的libblobmsg_json库提供,它提供了从json字符串转换blobmsg的支持等。
This part is a helper to pass data in binary. There are several supported datatypes, and it creates blobs that could be sent over any socket, as endianess is taken care in the library itself.
The method is to create a type-value chained data, supporting nested data. This part basically covers putting and getting datatypes. Further functionality is implemented in blobmsg.h
Blobmsg sits on top of blob.h, providing tables and arrays, providing it's own but parallel datatypes.
blob是一个个id-value块。id-value块是对齐到4字节。
enum {
BLOB_ATTR_UNSPEC,
BLOB_ATTR_NESTED,
BLOB_ATTR_BINARY,
BLOB_ATTR_STRING,
BLOB_ATTR_INT8,
BLOB_ATTR_INT16,
BLOB_ATTR_INT32,
BLOB_ATTR_INT64,
BLOB_ATTR_DOUBLE,
BLOB_ATTR_LAST
};
#define BLOB_ATTR_ID_MASK 0x7f000000
#define BLOB_ATTR_ID_SHIFT 24
#define BLOB_ATTR_LEN_MASK 0x00ffffff
#define BLOB_ATTR_ALIGN 4
#define BLOB_ATTR_EXTENDED 0x80000000
struct blob_attr {
uint32_t id_len;
char data[];
} __packed;
一个blob由id_len字段,后面跟着blob的value。
id_len是32位的,其中存储了类型和长度。高8位的低7位存储了id。低24位存储长度。
id_len是以大端存储的。长度是包括头的长度。
blob_attr操作接口:
void * blob_data(const struct blob_attr *attr) | 返回data指针 |
unsigned int blob_id(const struct blob_attr *attr) | 返回类型 |
bool blob_is_extended(const struct blob_attr *attr) | 是否是extend |
size_t blob_len(const struct blob_attr *attr) | 返回value的长度 |
size_t blob_raw_len(const struct blob_attr *attr) | 返回header + value 的长度 |
size_t blob_pad_len(const struct blob_attr *attr) | 返回 header + value + pad 的长度 |
uint8_t blob_get_u8(const struct blob_attr *attr) uint16_t blob_get_u16(const struct blob_attr *attr) uint32_t blob_get_u32(const struct blob_attr *attr) uint64_t blob_get_u64(const struct blob_attr *attr) int8_t blob_get_int8(const struct blob_attr *attr) int16_t blob_get_int16(const struct blob_attr *attr) int32_t blob_get_int32(const struct blob_attr *attr) int64_t blob_get_int64(const struct blob_attr *attr) const char * blob_get_string(const struct blob_attr *attr) | 返回value的格式化数据数据 |
struct blob_attr *blob_next(const struct blob_attr *attr) | 返回下一个blob的指针 |
void blob_fill_pad(struct blob_attr *attr) | 将pad置0 |
void blob_set_raw_len(struct blob_attr *attr, unsigned int len) | 设置len |
使用结构体blob_buf维护blob,需要一个buf层,来管理blob用到的buf。
struct blob_buf {
struct blob_attr *head;
bool (*grow)(struct blob_buf *buf, int minlen);
int buflen;
void *buf;
};
在blob buf层,blob自己的pad不计算在blob的长度中。
但是父blob的长度,包括子blob的pad,在IPC的时候,会一起发送。
head通常指向buf,只有在添加nest属性时,才临时指向nest blob。在nest_end后,又指向head。
初始化的时候,会添加一个id为0的blob。用户添加的blob都是这个blob的子blob
blob buf 结构图
blob 是一个个挨着放到buf中的。head是一个最外层的父blob,新增一个blob时,使用 blob_next(buf->head),得到上图第一个
白色的位置,加到那个位置,然后更新head的长度。
添加一个嵌套blob:blob_nest_start
是将head指向上图第一个白色位置,添加一个长度为0的,blob,然后返回旧的head的位置,调用者存起来,结束嵌套的时候传入。
结束一个嵌套:
恢复旧的head,然后更新head的长度(加上嵌入blob的长度)。
上述设计就可以实现多级嵌套。
初始化blob_buf
int blob_buf_init(struct blob_buf *buf, int id);
可以初始化未用过的blob_buf,此时blob_buf必须清零。也可以初始化用过的blob_buf,已经分配的内存会复用。
初始化最外层blob,id为传入的参数id,长度为0。
释放blob buf
void blob_buf_free(struct blob_buf *buf)
添加blob
extern void *blob_nest_start(struct blob_buf *buf, int id);
extern void blob_nest_end(struct blob_buf *buf, void *cookie);
extern struct blob_attr *blob_put(struct blob_buf *buf, int id, const void *ptr, unsigned int len);
struct blob_attr * blob_put_string(struct blob_buf *buf, int id, const char *str)
struct blob_attr *blob_put_u8(struct blob_buf *buf, int id, uint8_t val)
struct blob_attr *blob_put_u16(struct blob_buf *buf, int id, uint16_t val)
struct blob_attr *blob_put_u32(struct blob_buf *buf, int id, uint32_t val)
struct blob_attr *blob_put_u64(struct blob_buf *buf, int id, uint64_t val)
parse blob
通常对于一个ipc通信,blob的id都是连续的,从1到n,(0被保留为未定义)。然后定义每一个id的内容的type,以及校验函数。
blob提供接口,将blob解析到一个blob指针数组,数组的下表是id。
struct blob_attr_info {
unsigned int type;
unsigned int minlen;
unsigned int maxlen;
bool (*validate)(const struct blob_attr_info *, struct blob_attr *);
};
解析接口:
extern int blob_parse(struct blob_attr *attr, struct blob_attr **data, const struct blob_attr_info *info, int max);
extern int blob_parse_untrusted(struct blob_attr *attr, size_t attr_len, struct blob_attr **data, const struct blob_attr_info *info, int max);
在ubus中,json格式的数据,是通过blobmsg的格式存储在一个blob中的。blob id 是 UBUS_ATTR_DATA。
blobmsg层也是使用blob buf层,一个个blob,但是blob的payload是blob msg格式。
#define BLOBMSG_ALIGN 2
#define BLOBMSG_PADDING(len) (((len) + (1 << BLOBMSG_ALIGN) - 1) & ~((1 << BLOBMSG_ALIGN) - 1))
enum blobmsg_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,
BLOBMSG_TYPE_BOOL = BLOBMSG_TYPE_INT8,
BLOBMSG_TYPE_DOUBLE,
__BLOBMSG_TYPE_LAST,
BLOBMSG_TYPE_LAST = __BLOBMSG_TYPE_LAST - 1,
BLOBMSG_CAST_INT64 = __BLOBMSG_TYPE_LAST,
};
struct blobmsg_hdr {
uint16_t namelen;
uint8_t name[];
} __packed;
blob的data存储一个blobmsg的头,头存储的是blobmsg的name,头要对齐到4字节,然后再存储blobmsg的data。
这样设计后,blobmsg就可以存储json的key value了。
blob的id字段存储的是blobmsg的type。
blob的id_len字段的extend属性也会设置上。
int blobmsg_hdrlen(unsigned int namelen) | 返回blobmsg_hdr的长度(包括pad) |
const char *blobmsg_name(const struct blob_attr *attr) | 返回blobmsg的name |
int blobmsg_type(const struct blob_attr *attr) | 返回blobmsg的type,由于type存储在blob的id中,实际上是返回blob的id |
uint16_t blobmsg_namelen(const struct blobmsg_hdr *hdr) | 返回blobmsg的name的长度 |
void *blobmsg_data(const struct blob_attr *attr) | 返回blobmsg的value指针 |
size_t blobmsg_data_len(const struct blob_attr *attr) size_t blobmsg_len(const struct blob_attr *attr) | 返回blobmsg的value的长度,根据blob_len的长度 |
int blobmsg_add_u8(struct blob_buf *buf, const char *name, uint8_t val) int blobmsg_add_u16(struct blob_buf *buf, const char *name, uint16_t val)int int blobmsg_add_u32(struct blob_buf *buf, const char *name, uint32_t val) | 添加一个整数blobmsg |
blobmsg_add_string(struct blob_buf *buf, const char *name, const char *string) | 添加一个字符串blobmsg |
还有一些读取接口,这里就不一一列举了:
blobmsg_get_string
blobmsg_get_u32
字符串接口,比如通过printf的方式添加一个blobmsg:
int blobmsg_printf(struct blob_buf *buf, const char *name, const char *format, ...)
数组接口:
void * blobmsg_open_array(struct blob_buf *buf, const char *name)
void blobmsg_close_array(struct blob_buf *buf, void *cookie)
close的cookie参数,需要使用open返回的指针,
table接口:
void * blobmsg_open_table(struct blob_buf *buf, const char *name)
void blobmsg_close_array(struct blob_buf *buf, void *cookie)
数组和table对应json中的数组和object。
合法性检测接口:
检查一个attr是否合法包括id、name、长度等。jj
bool blobmsg_check_attr_len(const struct blob_attr *attr, bool name, size_t len);
parse接口
和blob的parse接口类似,提供一组policy,解析blobmsg到一个blobmsg指针的数组。
struct blobmsg_policy {
const char *name;
enum blobmsg_type type;
};
int blobmsg_parse(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len);
int blobmsg_parse_array(const struct blobmsg_policy *policy, int policy_len,
struct blob_attr **tb, void *data, unsigned int len);
参考:
https://openwrt.org/docs/techref/libubox
https://blog.csdn.net/iampisfan/article/details/78108100