servicemanager 是 Binder 进程间通信机制的核心,它扮演了 Binder 通信机制上下文管理者(context manager)的角色,既负责系统中 Service 组件的注册,又负责向 Client 组件提供获取 Service 组件代理对象的服务。
1. servicemanager 何时启动?
servicemanager 运行在一个独立进程当中,从Android启动之Android Framework启动可知,servicemanager 由 init 进程负责启动的,所以它和 Zygote 等系统核心服务是兄弟关系。以下是其启动脚本:
service servicemanager /system/bin/servicemanager [1]
class core [2]
user system [3]
group system [4]
critical [5]
onrestart restart healthd
onrestart restart zygote
onrestart restart media
onrestart restart surfaceflinger
onrestart restart drm
[1] service 关键字表示这是一个系统服务,后面跟着两个参数: servicemanager 是服务名称; /system/bin/servicemanager 是程序执行路径。
[2] class core 表示启动类别。
[3] user system 表示 servicemanager 属于 system 用户。
[4] group system 表示 servicemanager 属于 system 组。
[5] critical 关键字表示 servicemanager 是一个关键服务,异常退出后,该服务需要被重启。重启时,onrestart 描述的5个服务也会被重新启动。
2. servicemanager 如何成为 Binder 通信机制的上下文管理者?
servicemanager 对应的程序源码位于 frameworks/native/cmds/servicemanager 中,入口函数 main 的实现在文件 service_manager.c 中,如下所示:
代码路径:frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char **argv)
{
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}
以上是 main 函数的所有内容,简直短小精悍。main 函数主要完成三件事情:第一,初始化 Binder 通信环境,打开 Binder 设备文件 /device/binder 并将其映射到 servicemanager 进程的用户地址空间。第二,调用 binder_become_context_manager 函数将自身注册为上下文管理者;第三,进入循环等待,接收并处理 Client 进程发送过来的通信请求。下面看看 binder_open 函数和 binder_become_context_manager 函数的具体实现。
2.1 打开和映射 Binder 设备文件
通过调用 binder_open 函数来初始化 Binder 通信环境,打开和映射 Binder 设备文件 /dev/binder 。代码如下所示:
代码路径:frameworks/native/cmds/servicemanager/binder.c
struct binder_state *binder_open(unsigned mapsize)
{
struct binder_state *bs;
bs = malloc(sizeof(*bs)); [1]
if (!bs) {
errno = ENOMEM;
return 0;
}
bs->fd = open("/dev/binder", O_RDWR); [2]
if (bs->fd < 0) {
fprintf(stderr,"binder: cannot open device (%s)\n",
strerror(errno));
goto fail_open;
}
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); [3]
......
}
binder_open 函数由三部分组成:
[1] 创建 binder_state 结构体 bs,其成员变量 fd 用于保存 Binder 设备文件的文件描述符,mapped 和 mapsize 分别保存调用 mmap 映射设备文件后,返回的用户地址空间的起始地址和大小。
struct binder_state
{
int fd;
void *mapped;
unsigned mapsize;
};
[2] 通过 open 系统调用打开 Binder 设备文件。 当 open 系统函数被调用时,最终会调用到 Binder 驱动中的 binder_open 函数。binder_open 创建了一个描述通信进程信息的 binder_proc 结构体 proc,然后对它的一些成员变量进行初始化。
[3] 通过 mmap 系统调用将 Binder 设备文件映射到 servicemanager 进程的用户地址空间。 当 mmap 系统函数被调用时,Binder 驱动中的 binder_mmap 函数就会被调用。binder_mmap 函数实现比较复杂,核心功能是为 servicemanager 进程分配内核缓存区,并映射到其用户地址空间。函数定义如下所示:
代码路径:drivers/staging/android/binder.c
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
......
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); [3.1]
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
proc->buffer = area->addr; [3.2]
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; [3.3]
mutex_unlock(&binder_mmap_lock);
......
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);[3.4]
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
proc->buffer_size = vma->vm_end - vma->vm_start; [3.5]
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) { [3.6]
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
buffer = proc->buffer;
INIT_LIST_HEAD(&proc->buffers);
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
binder_insert_free_buffer(proc, buffer); [3.7]
......
}
[3.1] 调用函数 get_vm_area 在内核空间中分配一段长度为 vma->vm_end - vma->vm_start 的地址空间。
[3.2] 将内核空间起始地址保存到 proc->buffer 中。
[3.3] 将用户空间起始地址和内核空间起始地址的差值保存到 proc->user_buffer_offset 中,因此,只要知道其中一个地址,就可以计算出另外一个地址,如下图所示:
由于将 Binder 设备文件映射到 Server 进程的用户地址空间,因此使用 Binder 进程间通信机制时,只需要将 Client 用户地址空间的通信数据拷贝到内核空间, 拷贝一次 Server 进程就可以访问到通信数据。对比其他 IPC 机制(Socket,命名管道,消息队列等),需要先将 Client 用户地址空间的通信数据拷贝到内核空间,然后再从内核空间将通信数据拷贝到 Server 的用户地址空间,一共需要拷贝二次。
[3.4] 创建保存物理页面指针的数组。
[3.5] 将内核空间大小保存在 proc->buffer_size 中。
[3.6] 调用 binder_update_page_range 函数为 proc->buffer 指向的内核空间分配大小为 PAGE_SIZE 的物理页面。
[3.7] 调用 binder_insert_free_buffer 函数,将刚分配的物理页面添加结构体 proc 的成员变量 free_buffers 所指向的红黑树中,该红黑树保存了所有空闲的内核缓存区。
2.2 注册 servicemanager 为上下文管理者
binder_become_context_manager 函数通过 BINDER_SET_CONTEXT_MGR 命令将自己注册为上下文管理者,代码如下所示:
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
最终由 Binder 驱动中的 binder_ioctl 函数来处理 BINDER_SET_CONTEXT_MGR 命令,如下所示:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
......
switch (cmd) {
......
case BINDER_SET_CONTEXT_MGR:
if (binder_context_mgr_node != NULL) {
printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto err;
}
if (binder_context_mgr_uid != -1) {
if (binder_context_mgr_uid != current->cred->euid) {
printk(KERN_ERR "binder: BINDER_SET_"
"CONTEXT_MGR bad uid %d != %d\n",
current->cred->euid,
binder_context_mgr_uid);
ret = -EPERM;
goto err;
}
} else
binder_context_mgr_uid = current->cred->euid;
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto err;
}
binder_context_mgr_node->local_weak_refs++;
binder_context_mgr_node->local_strong_refs++;
binder_context_mgr_node->has_strong_ref = 1;
binder_context_mgr_node->has_weak_ref = 1;
break;
......
}
首先,进行一些合法性检查,确保之前没有组件注册成为上下文管理者。然后,调用 binder_new_node 函数为 servicemanager 创建一个对应的 Binder 实体对象,并将其保存在全局变量 binder_context_mgr_node 中。最后,初始化 binder_context_mgr_node 内部的引用计数。
至此,servicemanager 已经成功将自己注册为 Binder 通信机制的上下文管理者。
3. servicemanager 如何提供服务?
servicemanager 在系统运行时为 Service 组件提供注册服务,为 Client 组件提供获取 Service 组件代理对象的服务,这些都是通过调用函数 binder_loop 来实现的。binder_loop 函数定义如下:
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
unsigned readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(unsigned)); [1]
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); [2]
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func); [3]
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
[1] 调用 binder_write 函数将 BC_ENTER_LOOPER 命令协议写入 Binder 驱动,从而通知 Binder 驱动 servicemanager 已经准备就绪,可以接收处理进程间通信请求。调用过程如下所示:
-> binder_write(bs, readbuf, sizeof(unsigned));
-> ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
-> binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
[2] 通过 ioctl 系统调用检查 Binder 驱动是否有进程间通信请求需要 servicemanager 处理。如果没有,那么 servicemanager 线程就会在 Binder 驱动中睡眠等待,直到有新的进程间通信请求。调用过程如下所示:
-> ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
-> binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
binder_thread_read 函数会检查 Binder 驱动是否有新的进程间通信请求需要它来处理,并且可能会在 Binder 驱动中睡眠等待。如下所示:
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
void __user *buffer, int size,
signed long *consumed, int non_block)
{
....
wait_for_proc_work = thread->transaction_stack == NULL &&
list_empty(&thread->todo) [2.1]
if (wait_for_proc_work) {
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) {
binder_user_error("binder: %d:%d ERROR: Thread waiting "
"for process work before calling BC_REGISTER_"
"LOOPER or BC_ENTER_LOOPER (state %x)\n",
proc->pid, thread->pid, thread->looper);
wait_event_interruptible(binder_user_error_wait,
binder_stop_on_user_error < 2);
}
binder_set_nice(proc->default_priority);
if (non_block) {
if (!binder_has_proc_work(proc, thread))
ret = -EAGAIN;
} else
ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));[2.2]
} else {
if (non_block) {
if (!binder_has_thread_work(thread))
ret = -EAGAIN;
} else
ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); [2.3]
}
......
[2.1] 判断当前线程的 todo 队列中是否有待处理工作项,如果有的话则将 wait_for_proc_work 的值赋值为0,当前线程会优先处理 todo 队列中的待处理工作项。否则 wait_for_proc_work 值为1,当前线程可以处理所属进程的 todo 队列中的待处理工作项。
[2.2] 如果 wait_for_proc_work 值为1,并且当 non_block 值为0时,当前线程调用 wait_event_freezable_exclusive 函数,睡眠等待在所属进程的 wait 等待队列中,直到所属进程的 todo 队列有新的待处理工作项。
[2.3] 如果 wait_for_proc_work 值为0,并且当 non_block 值为0时,当前线程调用 wait_event_freezable 函数,睡眠等待在其 wait 等待队列中,直到当前线程的 todo 队列有新的待处理工作项。
non_block 值为1时,表示当前线程使用非阻塞模式。如果当前线程或所属进程中没有待处理工作项,那么则不在 Binder 驱动中睡眠等待,而是马上返回用户空间。
[3] ioctl 系统调用返回,说明有新的 Binder 通信请求需要处理,通过调用 binder_parse 函数对 Binder 通信请求内容进行解析。binder_parse 函数定义如下:
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uint32_t *ptr, uint32_t size, binder_handler func)
{
int r = 1;
uint32_t *end = ptr + (size / 4);
while (ptr < end) {
uint32_t cmd = *ptr++;
switch(cmd) {
......
case BR_TRANSACTION: {
struct binder_txn *txn = (void *) ptr;
if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
ALOGE("parse: txn too small!\n");
return -1;
}
binder_dump_txn(txn);
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
struct binder_io reply;
int res;
bio_init(&reply, rdata, sizeof(rdata), 4);
bio_init_from_txn(&msg, txn);
res = func(bs, txn, &msg, &reply);
binder_send_reply(bs, &reply, txn->data, res);
}
ptr += sizeof(*txn) / sizeof(uint32_t);
break;
}
......
}
return r;
}
解析过程与封装过程相对应,因此所使用的数据结构也与封装使用的数据结构(请参考Binder之数据结构(二))对应。结构体 binder_txn 对应于 binder_transaction_data;结构体 binder_io 对应于 Parcel 类;结构体 binder_object 对应于 flat_binder_object,如下图所示:
如果 Binder 驱动通知 servicemanager 处理 Client 进程的服务请求,那么使用的协议为 BR_TRANSACTION,于是调用 func 为 Client 进程提供服务。func 是一个函数指针,指向的实现函数为 svcmgr_handler,如下所示:
int svcmgr_handler(struct binder_state *bs,
struct binder_txn *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
unsigned len;
void *ptr;
uint32_t strict_policy;
int allow_isolated;
......
switch(txn->code) {
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
s = bio_get_string16(msg, &len);
ptr = do_find_service(bs, s, len, txn->sender_euid); [3.1]
if (!ptr)
break;
bio_put_ref(reply, ptr);
return 0;
case SVC_MGR_ADD_SERVICE:
s = bio_get_string16(msg, &len);
ptr = bio_get_ref(msg);
allow_isolated = bio_get_uint32(msg) ? 1 : 0;
if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated)) [3.2]
return -1;
break;
case SVC_MGR_LIST_SERVICES: {
unsigned n = bio_get_uint32(msg);
si = svclist;
while ((n-- > 0) && si)
si = si->next;
if (si) {
bio_put_string16(reply, si->name);
return 0;
}
return -1;
}
default:
ALOGE("unknown code %d\n", txn->code);
return -1;
}
bio_put_uint32(reply, 0);
return 0;
}
[3.1] 如果 Client 端调用 getService 函数,则命令协议为 SVC_MGR_GET_SERVICE 或者 SVC_MGR_CHECK_SERVICE,使用 do_find_service 函数为 Client 组件提供获取 Service 组件代理对象的服务。
[3.2] 如果 Client 端调用 addService 函数,则命令协议为 SVC_MGR_ADD_SERVICE,使用 do_add_service 函数为 Service 组件提供 Service 组件注册服务。
最后,一图胜千言,servicemanager 启动流程图如下所示:
参考资料:
1. Android 系统源代码情景分析的第5章 - Binder 进程间通信系统