全部博文(51)
分类: C/C++
2016-08-24 16:26:14
OpenWrt 启动Service流程及struct blob_buf相关代码
以/sbin/ubusd的启动为例来说明服务启动流程:
./procd/state.c
procd在STATE_UBUS状态时启动ubusd;
char ubus_cmd[] = "/sbin/ubusd";
service_init(); service_start_early("ubus", ubus_cmd); |
void service_init(void) { avl_init(&services, avl_strcmp, false, NULL); service_validate_init(); } |
void service_validate_init(void) { avl_init(&validators, avl_strcmp, true, NULL); } |
这里主要是初始化两个avl数,services和validators;
该函数处理分为两部分:1、生成blob消息;2、根据blob消息处理service内容
service_start_early(char *name, char *cmdline) { void *instances, *instance, *command, *respawn; char *t;
blob_buf_init(&b, 0); blobmsg_add_string(&b, "name", name); instances = blobmsg_open_table(&b, "instances"); instance = blobmsg_open_table(&b, "instance1"); command = blobmsg_open_array(&b, "command"); t = strtok(cmdline, " "); while (t) { blobmsg_add_string(&b, NULL, t); t = strtok(NULL, " "); } blobmsg_close_array(&b, command); respawn = blobmsg_open_array(&b, "respawn"); blobmsg_add_string(&b, NULL, "3600"); blobmsg_add_string(&b, NULL, "1"); blobmsg_add_string(&b, NULL, "0"); blobmsg_close_array(&b, respawn); blobmsg_close_table(&b, instance); blobmsg_close_table(&b, instances);
return service_handle_set(NULL, NULL, NULL, "add", b.head); } |
int blob_buf_init(struct blob_buf *buf, int id) { if (!buf->grow) buf->grow = blob_buffer_grow; //buf空间不够调用该函数分配空间
buf->head = buf->buf; //此时都为NULL if (blob_add(buf, buf->buf, id, 0) == NULL) //增加首个struct blob_attr *attr结构 return -ENOMEM;
return 0; } |
static struct blob_attr * blob_add(struct blob_buf *buf, struct blob_attr *pos, int id, int payload) { int offset = attr_to_offset(buf, pos); //此时偏移量为BLOB_COOKIE值; int required = (offset - BLOB_COOKIE + sizeof(struct blob_attr) + payload) - buf->buflen; //需要的buf空间 struct blob_attr *attr;
if (required > 0) { if (!blob_buf_grow(buf, required)) //首次使用必须申请空间,由blob_buffer_grow完成,同时将buf->head指向新申请的空间; return NULL; attr = offset_to_attr(buf, offset); } else { attr = pos; }
blob_init(attr, id, payload + sizeof(struct blob_attr)); //设置id_len字段值,包括:id和长度信息(payload + sizeof(struct blob_attr)); blob_fill_pad(attr); //初始化填充字段; return attr; //返回申请到的attr结构指针 } |
其包裹的函数为
blobmsg_add_field(buf, BLOBMSG_TYPE_STRING, name, string, strlen(string) + 1);
int blobmsg_add_field(struct blob_buf *buf, int type, const char *name, const void *data, unsigned int len) { struct blob_attr *attr; void *data_dest;
attr = blobmsg_new(buf, type, name, len, &data_dest); // data_dest返回存放数据的地址; if (!attr) return -1;
if (len > 0) memcpy(data_dest, data, len); //拷贝string(“ubus”)到数据部分 return 0; } |
再次申请一个attr结构,用来存放name信息;在struct blob_attr *attr的data字段首先内包裹了struct blobmsg_hdr *hdr;结构,用来存放属性类别,之后才是真实数据部分;
static struct blob_attr * blobmsg_new(struct blob_buf *buf, int type, const char *name, int payload_len, void **data) { struct blob_attr *attr; struct blobmsg_hdr *hdr; int attrlen, namelen; char *pad_start, *pad_end;
if (!name) name = "";
namelen = strlen(name); attrlen = blobmsg_hdrlen(namelen) + payload_len; //the length of (struct blobmsg_hdr + namelen + payload_len) attr = blob_new(buf, type, attrlen); //get the address of next attr if (!attr) return NULL;
attr->id_len |= be32_to_cpu(BLOB_ATTR_EXTENDED); hdr = blob_data(attr); hdr->namelen = cpu_to_be16(namelen); strcpy((char *) hdr->name, (const char *)name); //fill-in the name pad_end = *data = blobmsg_data(attr); pad_start = (char *) &hdr->name[namelen]; if (pad_start < pad_end) memset(pad_start, 0, pad_end - pad_start); //set 0 to padding
return attr; } |
struct blob_attr * blob_new(struct blob_buf *buf, int id, int payload) { struct blob_attr *attr; attr = blob_add(buf, blob_next(buf->head), id, payload); //computer buffer again, and get the address of next attr if (!attr) return NULL;
blob_set_raw_len(buf->head, blob_pad_len(buf->head) + blob_pad_len(attr)); //set attr->id_len again return attr; } |
添加instances服务属性;
包裹函数为blobmsg_open_nested(buf, name, false); 从函数名看有个nested单词就可以猜测到是上一个attr的角色的子attr;
void * blobmsg_open_nested(struct blob_buf *buf, const char *name, bool array) { struct blob_attr *head; int type = array ? BLOBMSG_TYPE_ARRAY : BLOBMSG_TYPE_TABLE;// type is BLOBMSG_TYPE_TABLE unsigned long offset = attr_to_offset(buf, buf->head); void *data;
if (!name) name = "";
head = blobmsg_new(buf, type, name, 0, &data); //returrn a new struct blob_attr; if (!head) return NULL; blob_set_raw_len(buf->head, blob_pad_len(buf->head) - blobmsg_hdrlen(strlen(name))); //注意这里是减号,在从blobmsg_new返回之前,buf->head就已经被更新了一次,这里又减去了一部分内容,与下面blobmsg_close_array内容一起看就知道为什么这么做了 buf->head = head; //这是与上一个attr的子attr,此处更换buf->head指针; return (void *)offset; } |
与上面内容一致,又嵌套一次;
command = blobmsg_open_array(&b, "command"); t = strtok(cmdline, " "); //此处cmdline为 /sbin/ubusd; while (t) { blobmsg_add_string(&b, NULL, t); //添加cmdline t = strtok(NULL, " "); } blobmsg_close_array(&b, command); |
blobmsg_open_array 嵌套函数为blobmsg_open_nested(buf, name, true);添加command属性
blobmsg_close_array的作用是结束此时的嵌套,返回上一级attr;其包裹函数为blob_nest_end
void blob_nest_end(struct blob_buf *buf, void *cookie) //这cookie表示是上一级attr的偏移信息; { struct blob_attr *attr = offset_to_attr(buf, (unsigned long) cookie); blob_set_raw_len(attr, blob_pad_len(attr) + blob_len(buf->head)); //这里对id_len的改变,没有再次增加sizeof(struct blob_attr),因为在blobmsg_open_nested中没有修改id_len时,已经将struct blob_attr包含在内了; buf->head = attr; } |
static inline void * blob_data(const struct blob_attr *attr) { return (void *) attr->data; }
/* * blob_id: returns the id of an attribute */ static inline unsigned int blob_id(const struct blob_attr *attr) { int id = (be32_to_cpu(attr->id_len) & BLOB_ATTR_ID_MASK) >> BLOB_ATTR_ID_SHIFT; return id; }
static inline bool blob_is_extended(const struct blob_attr *attr) { return !!(attr->id_len & cpu_to_be32(BLOB_ATTR_EXTENDED)); }
/* * blob_len: returns the length of the attribute's payload */ static inline unsigned int blob_len(const struct blob_attr *attr) { return (be32_to_cpu(attr->id_len) & BLOB_ATTR_LEN_MASK) - sizeof(struct blob_attr); }
/* * blob_raw_len: returns the complete length of an attribute (including the header) */ static inline unsigned int blob_raw_len(const struct blob_attr *attr) { return blob_len(attr) + sizeof(struct blob_attr); }
/* * blob_pad_len: returns the padded length of an attribute (including the header) */ static inline unsigned int blob_pad_len(const struct blob_attr *attr) { unsigned int len = blob_raw_len(attr); len = (len + BLOB_ATTR_ALIGN - 1) & ~(BLOB_ATTR_ALIGN - 1); return len; } |
接下来代码就依据上面流程再次添加respawn属性,并依次返回上一层attr并修改id_len字段
借网上一张图,可参考这个来看代码;
service_handle_set(NULL, NULL, NULL, "add", b.head);
static int service_handle_set(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { struct blob_attr *tb[__SERVICE_SET_MAX], *cur; struct service *s = NULL; const char *name; bool add = !strcmp(method, "add"); int ret;
blobmsg_parse(service_set_attrs, __SERVICE_SET_MAX, tb, blob_data(msg), blob_len(msg)); //查找消息的每个attr,struct blob_attr *tb[__SERVICE_SET_MAX]每个entry指向消息内对应服务信息的地址; cur = tb[SERVICE_ATTR_NAME]; //cur指向name attr if (!cur) return UBUS_STATUS_INVALID_ARGUMENT;
name = blobmsg_data(cur); //提取name信息
s = avl_find_element(&services, name, s, avl); //avl树查找是否已经存在该项; if (s) { DEBUG(2, "Update service %s\n", name); return service_update(s, tb, add); }
DEBUG(2, "Create service %s\n", name); s = service_alloc(name); //创建服务对应的struct service *s结构 if (!s) return UBUS_STATUS_UNKNOWN_ERROR;
ret = service_update(s, tb, add); if (ret) return ret;
avl_insert(&services, &s->avl); //插入avl数;
service_event("service.start", s->name, NULL); //此时ctx为NULL,不发送任何消息;
return 0; } |
static struct service * service_alloc(const char *name) { struct service *s; char *new_name;
s = calloc_a(sizeof(*s), &new_name, strlen(name) + 1); strcpy(new_name, name);
vlist_init(&s->instances, avl_strcmp, service_instance_update); //最重要的是一个回调函数,service_instance_update用于服务启动/更新 s->instances.keep_old = true; s->name = new_name; s->avl.key = s->name; INIT_LIST_HEAD(&s->validators);
return s; } |
该函数对blob记录的各个属性做对应的处理,这里我们只关心SERVICE_SET_INSTANCES属性;
if (tb[SERVICE_SET_INSTANCES]) { if (!add) vlist_update(&s->instances); blobmsg_for_each_attr(cur, tb[SERVICE_SET_INSTANCES], rem) { //get instances attribute,该attr只有一个,那么下面函数只执行一次; service_instance_add(s, cur); } if (!add) vlist_flush(&s->instances); }
rc(s->name, "running"); //若存在/etc/init.d/ubus,则执行running动作,但这里没有该文件; |
static void service_instance_add(struct service *s, struct blob_attr *attr) { struct service_instance *in; //进程管理由该结构来完成;
if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE) return;
in = calloc(1, sizeof(*in)); if (!in) return;
instance_init(in, s, attr); //各个字段初始化,主要的是提取此时attr的信息,记录该需要启动进程的路径,nice,respawn信息等;in->command记录“/sbin/ubusd” vlist_add(&s->instances, &in->node, (void *) in->name); //此函数会简介启动需要的服务; } |
void vlist_add(struct vlist_tree *tree, struct vlist_node *node, const void *key) { struct vlist_node *old_node = NULL; struct avl_node *anode; …… avl_insert(&tree->avl, &node->avl); //插入avl
update_only: tree->update(tree, node, old_node); //此处调用service_instance_update } |
这里我们关心create操作;
static void service_instance_update(struct vlist_tree *tree, struct vlist_node *node_new, struct vlist_node *node_old) { …… } else if (in_n) { DEBUG(2, "Create instance %s::%s\n", in_n->srv->name, in_n->name); instance_start(in_n); } blob_buf_init(&b, 0); trigger_event("instance.update", b.head); } |
void instance_start(struct service_instance *in) { ……
pid = fork(); if (pid < 0) return;
if (!pid) { //子进程启动服务 uloop_done(); //调用uloop_done关闭由父进程继承来的epoll信息; closefd(opipe[0]); closefd(epipe[0]); instance_run(in, opipe[1], epipe[1]); //该函数从blob消息中提取需要执行的指令,并设置环境变量;最终调用execvp(argv[0], argv); return; }
DEBUG(2, "Started instance %s::%s\n", in->srv->name, in->name); in->proc.pid = pid; //记录子进程pid clock_gettime(CLOCK_MONOTONIC, &in->start); //记录启动时间 uloop_process_add(&in->proc); //添加到processes链表,若进程退出时会调用cb函数instance_exit; …… } |