5.2 servicemanager进程的启动过程

servicemanager是在init.rc中配置的Daemon System Service,在系统启动过程中由boot Action启动。servicemanager先于其他服务启动,在其启动后,便可以对外提供服务注册、服务检索功能。除此之外,servicemanager还维护了一个Binder通信的上下文管理者(context manager)。定位到servicemanager初始化配置文件,代码如下:


service servicemanager/system/bin/servicemanager

class core#类别为core,将由boot Action启动

user system#属于system用户

group system#属于system组

critical服务,异常退出后,该服务需要重启

如果critical服务4分钟内重启超过4次,则系统需要重启

critical

servicemanager重启会导致以下4个服务重启

onrestart restart zygote

onrestart restart media

onrestart restart surfaceflinger

onrestart restart drm


servicemanager是在系统启动阶段由init启动的Service,该Service对应的可执行程序名为/system/bin/servicemanager。

servicemanager可执行程序的源码位于frameworks/base/cmds/servicemanager中,其程序入口为service_manager.c。定位到main函数,代码如下:


void*svcmgr_handle;

int main(int argc, char**argv)

{

struct binder_state*bs;

/#define BINDER_SERVICE_MANAGER((void)0)*/

void*svcmgr=BINDER_SERVICE_MANAGER;

//打开Binder设备,映射共享内存用于接收IPC通信数据

bs=binder_open(128*1024);

//将servicemanager注册为context manager

if(binder_become_context_manager(bs)){

return-1;

}

svcmgr_handle=svcmgr;

//进入无限循环等待接收IPC通信数据

binder_loop(bs, svcmgr_handler);

return 0;

}


servicemanager进程的启动过程分为以下三步:

1)初始化Binder通信环境,打开Binder设备并映射共享内存。

2)将自身注册为上下文管理者(context manager)。

3)进入无限循环中等待接收并处理IPC通信请求。

下面详细分析这三个步骤。

5.2.1 初始化Binder通信环境

servicemanager调用binder_open函数初始化Binder通信环境,这是使用Binder通信的第一步,该函数位于frameworks/base/cmds/servicemanager/binder.c中,其代码如下:


struct binder_state*binder_open(unsigned mapsize)

{

//创建binder_state类型的bs结构体,并分配内存

struct binder_state*bs;

bs=malloc(sizeof(*bs));

if(!bs){

errno=ENOMEM;

return 0;

}

//通过open系统调用以读写方式打开设备文件

bs->fd=open("/dev/binder",O_RDWR);

if(bs->fd<0){

goto fail_open;

}

//通过mmap系统调用将设备文件映射到当前进程的虚拟地址空间

bs->mapsize=mapsize;//mapsize由传入的参数指定为128KB

bs->mapped=mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd,0);

if(bs->mapped==MAP_FAILED){

goto fail_map;

}

return bs;

//错误处理代码,回收资源,关闭设备

fail_map:

close(bs->fd);

fail_open:

free(bs);

return 0;

}


binder_open的主要工作可以分为三部分:

1)创建binder_state类型的bs结构体,并分配内存。

2)通过open系统调用以读写方式打开设备文件。

3)通过mmap系统调用将设备文件映射到当前进程的虚拟地址空间。

binder_open需要借助binder_state结构体保存open和mmap系统调用的返回信息,该结构体定义如下:


struct binder_state

{

int fd;

void*mapped;

unsigned mapsize;

};


binder_state结构体包含3个成员。fd用于保存open系统调用返回的文件描述符;mapped用于保存mmap系统调用返回的映射区的起始地址;mapsize用于保存上述映射区的大小。

由于进程的地址空间是彼此隔离的,但内核空间是可以共享的,因此要实现进程间通信,可以在内核中开辟缓冲区保存进程间通信数据,以此实现共享内存。open和mmap系统调用的组合用来实现共享内存,首先open系统调用打开Binder设备文件,这样便可以访问Binder驱动程序;然后mmap系统调用将Binder设备文件映射到进程的虚拟地址空间,并通知Binder驱动程序在内核空间创建128KB的缓冲区用来保存IPC数据。mmap系统调用的结果是进程空间的某个内存区域和内核空间的某个内存区域建立了映射关系,当前进程servicemanager可以利用内核缓冲区共享数据。

open系统调用导致Binder驱动程序中的binder_open函数被调用,该函数位于kernel/drivers/staging/android/binder.c中,代码如下:


static int binder_open(struct inodenodp, struct filefilp)

{

struct binder_proc*proc;//创建binder_proc类型的结构体proc

proc=kzalloc(sizeof(*proc),GFP_KERNEL);

if(proc==NULL)

return-ENOMEM;

//保存打开Binder设备的进程信息,即servicemanager

get_task_struct(current);

proc->tsk=current;

//初始化可执行任务列表

INIT_LIST_HEAD(&proc->todo);

//初始化等待队列列表,用于切换current进程到wait状态

init_waitqueue_head(&proc->wait);

//记录进程默认优先级

proc->default_priority=task_nice(current);

mutex_lock(&binder_lock);

binder_stats_created(BINDER_STAT_PROC);

//将proc的节点加入全局列表binder_procs中

hlist_add_head(&proc->proc_node,&binder_procs);

proc->pid=current->group_leader->pid;

INIT_LIST_HEAD(&proc->delivered_death);

//将proc存入filp结构体的private_date成员

filp->private_data=proc;

mutex_unlock(&binder_lock);

//在/proc/binder/proc目录下创建Binder通信信息文件,文件以PID命名

if(binder_debugfs_dir_entry_proc){

char strbuf[11];

snprintf(strbuf, sizeof(strbuf),"%u",proc->pid);

proc->debugfs_entry=debugfs_create_file(strbuf, S_IRUGO,

binder_debugfs_dir_entry_proc, proc,&binder_proc_fops);

}

return 0;

}


可见,驱动层binder_open函数的作用是创建并初始化了binder_proc结构体,该结构体记录了打开Binder设备的进程所对应的Binder通信信息,这些信息可以通过/proc/binder/proc/<PID>输出。

与open系统调用类似,mmap系统调用导致驱动层的binder_mmap函数被调用,这部分内容涉及Linux内存管理的知识,有兴趣的读者可以参考相关书籍。