上文用简单的 AIDL 实例应用程序之间的进程间通讯,它是一种 c/s 架构。
client端:Proxy.transact() 来发送事务请求;
server端:Stub.onTransact() 会接收到相应事务。
这篇介绍 Server 如何通过 binder 向 ServiceManager 注册服务的?
ServiceManager
源码中用到 binder 最多的就是 ServiceManager。Binder 通信采用C/S架构,从组件视角来说,包含 Client、Server、ServiceManager 以及 binder 驱动,其中 ServiceManager 用于管理系统中的各种服务。

Server 端注册服务和 Client 端获取服务的过程都需要 ServiceManager,这篇先了解下 ServiceManager 的启动流程。
ServiceManager 是由 init 进程解析 init.rc 文件而创建的,在 \frameworks\native\cmds\servicemanager\servicemanager.rc 中定义了可执行程序 /system/bin/servicemanager,所对应的源文件是 service_manager.c,也在同一目录下。
service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
//。。。
init 进程解析完后会执行 service_manager 的入口函数 main。
int main(int argc, char** argv)
{
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
//打开binder驱动,申请128k字节大小的内存空间
bs = binder_open(driver, 128*1024);
//成为上下文管理者
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
//进入无限循环,处理client端发来的请求
binder_loop(bs, svcmgr_handler);
return 0;
}
binder 对应的几个重要函数已在代码中标注。
binder_open
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
//...
//通过系统调用陷入内核,打开Binder设备驱动
bs->fd = open(driver, O_RDWR | O_CLOEXEC);
if (bs->fd < 0) {
//...
}
//通过系统调用,ioctl获取binder版本信息
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
//...
}
bs->mapsize = mapsize;
//通过系统调用,mmap内存映射,mmap必须是page的整数倍
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
if (bs->mapped == MAP_FAILED) {
fprintf(stderr,"binder: cannot map device (%s)\n",
strerror(errno));// binder设备内存无法映射
goto fail_map;
}
}
先调用 open() 打开 "/dev/binder" 的 binder 设备,open 函数属于 Linux中系统 IO,用于“打开”文件,代码打开一个文件意味着获得了这个文件的访问句柄。O_RDWR 表示以可读可写方式打开,这样就获取了 binder 的句柄 fd。
之后调用 ioctl 函数,它是一个系统调用,作用于一个文件描述符。它接收一个确定要进行的命令的数字和(可选地)另一个参数, 常常是一个指针,可以实现几个用来调试用的 ioctl 命令,这些命令可以从驱动拷贝相关的数据结构到用户空间。实现了 ioctl,用户程序所作的只是通过命令码 (cmd) 告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。后续很多地方都用到此函数。这里 ioctl() 检验当前 binder 版本与 Binder 驱动层的版本是否一致。
接着调用 mmap 映射内存,将一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对应关系;实现这样的映射关系后,进程就可以采用指针的方式读写操作这一块内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必调用 read,write 等系统调用函数,相反,内核空间堆这段区域的修改也直接反应到用户空间,从而可以实现不同进程间的文件共享。
bs 的结构:
struct binder_state
{
int fd;
void *mapped;
size_t mapsize;
};
这里就先了解这么多,具体驱动层的事情我也不太清楚,也不是 Android 软件开发人员关注的重点,想要深入了解可点击这里
binder_become_context_manager
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
这里将 binder 注册成为上下文的管理者,也就是整个系统只有这样一个管理者。
binder_loop
void binder_loop(struct binder_state *bs, binder_handler func)
{
readbuf[0] = BC_ENTER_LOOPER;
//将BC_ENTER_LOOPER命令发送给binder驱动,让Service Manager进入循环
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
//进入循环,不断地binder读写过程
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
// 解析binder信息
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
}
}
binder_handler 指向 service_manager 的 svcmgr_handler 函数(之后交互会用到),通过 ioctl() 将 BC_ENTER_LOOPER 命令发送给 bi

本文深入探讨了Android系统中Binder机制的应用,详细讲解了ServiceManager的启动流程、如何通过binder向ServiceManager注册服务,以及SurfaceFlinger如何利用Binder与ServiceManager交互。
最低0.47元/天 解锁文章
757

被折叠的 条评论
为什么被折叠?



