3.4.4 解析Service

1.parse_service

解析Service先从parse_service开始,代码如下:


static voidparse_service(struct parse_statestate, int nargs, char**args)

{

struct service*svc;//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(charnargs);

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 listnodehead, struct listnodeitem)

{

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_statestate, int nargs, char*args)

{

/从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(charnargs);

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的解析过程。