《Android的设计与实现:卷I》——第3章3.4.4解析Service

    xiaoxiao2021-04-18  134

    3.4.4 解析Service

    1.parse_service

    解析Service先从parse_service开始,代码如下:static void parse_service(struct parse_state state,int nargs, charargs){ struct servicesvc;//service结构体,用于保存当前解析出的Service ……//省略错误处理代码 nargs -= 2; /为Service分配存储空间/ svc = calloc(1, sizeof(svc) + sizeof(char) nargs); /用解析到的内容构造service结构体/ svc->name = args[1]; svc->classname = "default"; memcpy(svc->args, args + 2, sizeof(char) nargs); svc->args[nargs] = 0; svc->nargs = nargs; svc->onrestart.name = "onrestart"; /初始化Service中restart Option的Commands链表,然后 将Servic的节点slist放入service_list双向链表/ list_init(&svc->onrestart.commands); /将Service节点的指针部分放入service_list中/ list_add_tail(&service_list, &svc->slist); return svc;}parse_service函数主要做了三项工作:1)为新建的Service分配存储空间,2)初始化Service,

    3)将Service放入一个service_list链表。其中涉及几个重要的数据类型和函数:service_list、list_init和list_add_tail,以及service结构体。

    (1)service_list

    service_list由list_declare定义,list_declare实际上是一个宏,位于/system/core/include/cutils/list.h。其源码如下:

    define list_declare(name) \

    struct listnode name = { \ .next = &name, \ .prev = &name, \ }

    service_list声明了一个双向链表,存储了前向和后向指针。

    (2)list_init和list_add_tail

    list_init和list_add_tail的实现代码位于/system/core/libcutils/list.c中,提供了基本的双向链表操作。list_init的源码如下:void list_init(struct listnode node){ node->next = node; node->prev = node;}list_add_tail的源码如下:void list_add_tail(struct listnode head, struct listnode item){ item->next = head; item->prev = head->prev; head->prev->next = item; head->prev = item;}list_add_tail只是将item加入到双向链表的尾部。

    注意 Android借鉴了Linux内核中常用的链表实现方法。把链表的指针部分和数据部分分离。   首先定义了node结构体:   struct listnode   {   struct listnode *next;   struct listnode *prev;   };   将链表的前向指针和后项指针放入这个struct listnode的结构中。当需要处理不同数据节点时,就把这个listnode嵌入不同的数据节点中。这样操作链表就是操作这个listnode,与具体的数据无关。如parse_service函数中,list_add_tail(&service_list, &svc->slist);便是将Service节点的listnode指针部分放入service_list链表。当需要操作listnode对应的数据时,就可通过成员偏移量找到对应的数据,这部分以后分析。

    (3)service结构体

    parse_service中最重要的一个数据类型便是service,它存储了Service这个Section的内容。service结构体定义在/system/core/init/init.h中,代码如下:struct service { / list of all services / struct listnode slist; const char *name; //Service的名字 const char *classname; //Service的分类名 unsigned flags; //Service的属性标志 pid_t pid; //Service的进程号 time_t time_started; //上次启动时间 time_t time_crashed; //上次异常退出的时间 int nr_crashed; //异常退出的次数 uid_t uid; //用户ID gid_t gid; //组ID gid_t supp_gids[NR_SVC_SUPP_GIDS]; size_t nr_supp_gids; struct socketinfo sockets; //Service使用的Socket struct svcenvinfo esnvvars; //Service使用的环境变量 /Service重启时要执行的Action。这里其实是由关键字onrestart声明的Option。由于onrestart

    声明的Option后面的参数是Command,而Action就是一个Command序列,所以这里用Action代替/ struct action onrestart; /触发该 service 的组合键,通过/dev/keychord获取 / int keycodes; int nkeycodes; int keychord_id; /IO优先级,与IO调度有关/ int ioprio_class; int ioprio_pri; /参数个数/ int nargs; /参数名/ char args[1];

    }; /args 必须位于结构体末端 /可见,Service需要填充的内容很多,parse_service函数只是初始化了Service的基本信息,详细信息需要由parse_line_service填充。

    2.parse_line_service

    parse_line_service的源码如下:static void parse_line_service(struct parse_state state, int nargs, charargs){

    / 从state的context变量中取出刚才创建的Service / struct service svc = state->context; struct command cmd; int i, kw, kw_nargs; if (nargs == 0) { return; } svc->ioprio_class = IoSchedClass_NONE; / 根据lookup_keyword函数匹配关键字信息,这次匹配的是Service对应的Option关键字/ kw = lookup_keyword(args[0]); switch (kw) { case K_class: if (nargs != 2) { ……//省略错误处理内容 }else { svc->classname = args[1]; } break; ……//省略部分case语句 case K_onrestart: nargs--; args++; kw = lookup_keyword(args[0]); ……//省略部分内容 /这里对应onrestart Option的Command创建过程,也是调用了list_add_tail函数操作双向链表/ cmd = malloc(sizeof(cmd) + sizeof(char) nargs); cmd->func = kw_func(kw); cmd->nargs = nargs; memcpy(cmd->args, args, sizeof(char) nargs); list_add_tail(&svc->onrestart.commands, &cmd->clist); break; ……//省略部分case语句 case K_socket: {/ name type perm [ uid gid ] / struct socketinfo si; ……//省略部分内容 /以下是解析Socket,有些服务需要使用Socket,socketinfo描述Socket的信息/ si = calloc(1, sizeof(si)); si->name = args[1];//以下设置了Socket的基本信息 …… break;

    } ……//省略部分case语句 default: //只支持固定的关键字,否则出错

    parse_error(state, "invalid option '%s'\n", args[0]);

    }}

    到这里Service就解析完了,接着分析Action的解析过程。

    相关资源:七夕情人节表白HTML源码(两款)

    最新回复(0)