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

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

时间: 2024-09-21 16:53:19

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

《Android的设计与实现:卷I》——第3章 3.6init循环监听处理事件

3.6 init循环监听处理事件 init触发所有Action后,便进入一个无限循环,在这个无限循环里首先执行两条指令: execute_one_command()和restart_processes(). 其中execute_one_command()已经分析过,用来启动Action和Service:restart_processes()也容易理解,就是重启这些Action和Service.此后便在init中调用了系统函数poll等待一些事件发生,代码如下: nr = poll(ufds, f

《Android的设计与实现:卷I》——第3章 Android启动过程的底层实现

第3章 Android启动过程的底层实现 Android支持多种启动模式,主要有正常模式(normal mode).安全模式(safe mode).恢复模式(recovery mode).工厂模式(factory mode).快速启动模式(fastboot mode)等.除正常模式外,都是刷机或者测试模式,本书只讲解正常模式下Android的启动过程.如果读者对其他启动模式感兴趣,可以自行查阅相关资料. 3.1 Android正常模式启动流程 Android的正常模式启动流程大体如下:步骤1 系

《Android的设计与实现:卷I》——第3章 3.4.2init.rc的内容

3.4.2 init.rc的内容 熟悉了Android初始化语言,再来阅读init.rc文件就容易多了.以下是init.rc的内容: service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_s

Android UI设计的幻灯片:新的UI设计模式

文章描述:谷歌Android UI设计技巧:新的UI设计模式. 本系列文章原是Android的官方开发者博客的一份Android UI设计的幻灯片,51CTO的译者将这份教程5部分进行翻译整理,希望对Android开发者能有帮助.本文为<谷歌Android UI设计技巧>第四部分:新的UI设计模式. 本文为<谷歌Android UI设计技巧>第四部分:新的UI设计模式. [1] [2]  下一页

Android UI设计的幻灯片:图标与指导说明

文章描述:谷歌Android UI设计技巧:图标与指导说明. 本系列文章原是Android的官方开发者博客的一份Android UI设计的幻灯片,51CTO的译者将这份教程5部分进行翻译整理,希望对Android开发者能有帮助.本文为<谷歌Android UI设计技巧>第五部分也就是最后一部分:图标与指导说明. 本文为<谷歌Android UI设计技巧>第五部分也就是最后一部分:图标与指导说明.

Android应用设计:选项菜单Options Menu

文章描述:Android硬体键交互之"选项菜单". 众所周知Android没有明确的GuideLine,虽说没有严格的规范来限制设计与创新很赞,但这也导致市场上的Android应用设计上的混乱.一个典型例子就是选项菜单Options Menu. 混乱的菜单 Android机器采用的硬体键来呼出菜单,这种方式在表现上隐性的,用户对于何种情况下可以呼出何种菜单没有预见性,甚至是否可以呼出菜单都没有预期.   如何解决 为降低用户的认知成本,建议设计中遵循以下方式. Question 1:何

谷歌Android UI设计技巧:新的UI设计模式

本系列文章原是Android的官方开发者博客的一份Android UI设计的幻灯片,51CTO的译者将这份教程5部分进行翻译整理,希望对Android开发者能有帮助.本文为<谷歌Android UI设计技巧>第四部分:新的UI设计模式. 本文为<谷歌Android UI设计技巧>第四部分:新的UI设计模式.

谷歌Android UI设计技巧:框架特性

本系列文章原是Android的官方开发者博客的一份Android UI设计的幻灯片,51CTO的译者将这份教程5部分进行翻译整理,希望对Android开发者能有帮助.本文为<谷歌Android UI设计技巧>第三部分:框架特性. 本文为<谷歌Android UI设计技巧>第三部分:框架特性. 注:相对布局和线性布局是Android里面常用的两种布局,线性布局比较简单,而相对布局可以做出比较复杂的布局管理,所以仅仅了解线性布局,很多时候是不够的.不过以作者之前Qt的经验来看,Andr

谷歌Android UI设计技巧:优秀UI设计准则

本文原是Android的官方开发者博客的一份Android UI设计的幻灯片,51CTO的译者将这份教程5部分进行翻译整理,希望对Android开发者能有帮助.本文为<谷歌Android UI设计技巧>第二部分:优秀UI设计准则. 本文为<谷歌Android UI设计技巧>第二部分:优秀UI设计准则.