这里接着上期的小岛居民买水果的故事来说,我们可以把 Binder 驱动当做地下市场。 A 岛的居民找到地下市场的特殊组织,告诉他我想吃 B 岛的水果,地下市场的特殊组织就去 B 岛上进货, 回来之后再给 A 岛居民。 用户空间就是 不同的岛民,内核空间就是地下市场,而 Binder 驱动就是地下市场中的一个特殊的组织。
1. 概述
地基 ------ Binder 驱动 ------ 标准的 Linux 驱动
Binder Driver 会将自己注册成一个 misc device, 并向上提供一个 /dev/binder 节点
但这个节点并不对应真实的硬件设备,Binder 驱动运行于内核态,可以提供 open(), ioctl(), mmap() 等常用的文件操作。
Binder 驱动需要填写 file_operations 结构体,特殊组织
也有自己的架构,不同的部门有着不同的职能。
ini
/drivers/staging/android/Binder.c
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
这里可以看出, Binder 驱动总共为上层应用提供了 6 个接口,其中最多的是 binder_ioctl, binder_mmap, binder_open 。
这里一般文件操作需要用到的 read() 和 write() 则没有出现,因为它们的功能完全可以用 ioctl() 和 mmap() 来代替。
打开 Binder 驱动 ------ binder_open
binder_mmap
mmap() 可以把设备指定的内存块直接映射到应用程序的内存空间中。(这块我觉得很神奇,也很好奇)
- 对于应用程序而言,它通过 mmap() 返回值得到一个内存地址(虚拟地址),这个地址通过虚拟内存转换(分段、分页)后最终将指向物理内存的某个位置。
- 对于 Binder 驱动而言,它也有一个指针(binder_proc->buffer), 指向某个虚拟地址,经过虚拟地址的转换后,它和应用程序中指向的物理内存处于同一个位置。
这里的 binder_proc->buffer 是 Binder 驱动为应用程序分配的一个数据结构,用于存储和该进程有关的所有信息,如内存分配、线程管理等。
binder_ioctl
实现了应用进程与Binder 驱动之间的命令交互,承载了 Binder 驱动中的大部分业务。
DNS 服务器 ------ ServiceManager(Binder Server)
SM 是一个标准的 Binder Server
ServiceManager 的构建
arduino
// framework/native/cmds/servicemanager.Service_manager.c
int main (int argc, char **argv) {
struct binder_state *bs;
void *svcmgr = BINDER_SERVICE_MANAGER;
// 1. 打开Binder设备,做好初始化
bs = binder_open(128*1024);
if (binder_become_context_manager(bs)) {
// 将自己设置成 Binder 大管家,整个android系统只允许一个 ServiceManager 存在
// 因而后面还有人调用这个函数就会失败
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler); // 进入循环,等待客户的请求
return 0;
}
- 由binder驱动决定被映射到进程空间中的内存起始地址
- 映射区块大小为 128KB
- 映射区只读
- 映射区的改变是私有的,不需要保存文件
- 从文件的起始地址开始映射
在 Binder Server 进入循环之前,它要先告知 Binder 驱动这一状态变化:
ini
bwr.write_size = 0;
bwr.write_cinsumed = 0;
bwr.write_buffer = 0;
redbuffer[0] = BC_ENTER_LOOPER;
binder_write (bs, readbuf, sizeof(unsigned));
之后,SM 进入了循环,循环和典型的基于事件驱动的程序循环框架类似。
- 从消息队列中读取消息
- 如果消息是 退出, 则马上结束循环,如果消息是空,则继续读取或者等待一段时间内后再读取,如果消息不为空且不是退出命令,则根据具体情况处理。
- 如此循环往复直到退出
BR_TRANSACTION
因为 ServiceManager 是为了完成 "Binder Server Name"(域名) 和 "Server Handle"(IP地址)间的对应关系而存在的。
- 注册
当一个Binder Server 创建后,他们要将自己的【名称, Binder句柄】对应关系告知 SM 进行备案
- 查询
应用程序可以向 SM 发起查询请求,已获得某个 Binder Server 所对应的句柄
- 其他信息查询
比如SM版本号,当前的状态等。
Service Manager 的功能架构比较简洁------内部维护着一个 svlist 列表,用于存储所有 Server 相关信息(以scvinfo为数据结构),查询和注册都是基于这个表展开的。
- SVC_MGR_GET_SERVICE
- SVC_MGR_CHECK_SERVICE
根据Server 名称来找到它的 handle 值
- SVC_MGR_ADD_SERVICE
用于注册一个 Binder Server
首先在 SM 所维护的数据列表中查找是否一已经有对应的节点存在,否则需要创建一个新的节点来记录这个 Server, 然后将这个 Server 中所带的信息写入列表的相应节点中,以备后期查询。
- SVC_MGR_LIST_SERVICES
获取列表中的对应 Server
函数 svcmgr_handler 处理完成后,binder_parse 会进一步通过 binder_send_reply 来将执行结果回复给底层 binder 驱动,进而传递给客户端。然后 binder_parse 会进入下一轮的 while 循环,直到 ptr < end 为 false , 此时说明 Service Manager 上一次从驱动层读取的消息都已经处理完成,因而它还会继续向 Binder Driver 发送 BINDER_WRITE_READ 以查询有没有新的消息。如果有的话就处理,否则会进入休眠等待。
获取 ServiceManager 服务 ------ 设计思考
问题:
需要访问 SM (Binder Server) 的服务, 流程应该怎么样?
- 打开 Binder 设备
- 执行 mmap
- 通过 Binder 驱动向 SM 发送请求(SM 的 handle 为 0)
- 获得结果
这里:
- 向 SM 发起请求的 Binder Client 可能是 Android APK 应用程序,所以 SM 必须要提供 Java 层接口。
- 如果每个 Binder Client 都要亲历亲为地执行上面几个步骤来获取 SM 服务,会浪费不少时间。所以它提供了更好的封装来使整个 SM 调用过程更加精简实用。
- 如果应用程序代码中每次使用 SM 服务(或者其他 Binder Server 服务),都需要打开一次 Binder 驱动、执行 mmap, 后果就是消耗的系统资源越来越多,直到崩溃。
每个进程只允许打开一次 Binder 设备,且只做一次内存映射 ------ 所有需要使用 Binder 驱动的线程共享这 一资源
这里我们就要想着如何设计一个满足上面条件的 Binder Client
- 要创建一个类专门管理每个应用进程中的 Binder 操作 ------ 执行 Binder 驱动的一系列命令对上层用户必须是"透明的" ------ ProcessState 类
- 与 Binder 驱动进行实际命令通信的是 IPCThreadState
Proxy 代理
对 SM 提供的服务进行封装, 交给代理去做 ------ ServiceManagerProxy
- ServiceManagerProxy 的接口,这里它提供的服务和服务端的 SM 必须是一致的。将这些方法提取出来,就是 ServiceManagerProxy 的接口 ------ IServiceMananager
csharp
public interface IServiceMangaer {
public IBinder getService(String name) throws RemoteException;
public IBinder checkService();
...
}
显然, ServiceManagerProxy 需要继承 IServiceMananager
- 接口实现
- 与Binder 建立关系
因为进程中已经有了 ProcessState 和 IPCThreadState 这两个专门与 Binder 驱动通信的类,所以 Java 层 代码使用 Binder 驱动实际上是基于它们来完成的,称为 BpBinder.
- 向 Binder 发送命令,从而获得 SM 提供的服务
- 总结
- Binder 架构
驱动、SM、Binder Client、Binder Server
- Binder 驱动
驱动是其他元素的基础
- Service Manager
SM 既是 Binder 框架的支撑者,同时也是一个标准的 Server
ServiceManagerProxy
这里 Android 系统在 ServiceManagerProxy 上面加了一层封装 ServiceManager.java
这样应用程序使用 SM 就更加方便了,连 ServiceManagerProxy 都不用创建了。
ServiceManager.getService(name);
SM 中的所有服务接口都设计成了 static 的,这样用户不需要额外的创建对象就能使用其功能了。
typescript
// framework/base/core/java/android/os/ServiceManager.java
public static IBinder getService(String name) {
try {
// 1.sCache 中记录的是 getService 的历史查询结果
IBinder service = sCashe.get(name); // 查看缓存
if (service != null) {
return service; //从缓存中找到结果,直接返回
} else {
//2. sCache中没有查询到就会发起查询请求
return getIServiceManager().getService(name); // 向 SM 发起查询
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
public static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager; // 返回一个 IServiceManager 对象
}
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
scss
// framework/base/core/java/android/os/ServiceManagerNative.java
static public IServiceManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
// 1. 先查询本地是否已经有 IServiceManager 存在
IServiceManager in = (IServiceManager)obj.queryLoaclInterface(descripor);
if (in != null) {
return in
}
// 这里终于出现了,如果没有查询到,就新建一个 ServiceManagerProxy
return new ServiceManagerProxy(obj);
}
// 作为 SM 的代理,ServiceManagerProxy 必定要参与 Binder 通信的
// 它的构造函数中传入了 IBinder 的对象
public ServiceManagerProxy(IBinder remote) {
mRemote = remote; // 只是简单的记录下了这个 IBinder 对象,类似于电话号码
}
// 用的时候再去拨打这个号码
public IBinder getService(String name) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IServiceManager.descriptor);
data.writeString(name);
// 利用 IBinder 对象执行命令
mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();
return binder;
}
这里详细说一下这个流程:
- 准备数据
通过 Parcel 打包数据
- IBinder.transact
利用 IBinder 的 transact 将请求发送出去,而不用理会 Binder 驱动的 open, mmap 等具体的 binder 协议中的命令。所以这个 IBinder 一定会在 ProcessState 和 IPCThreadState 来与 Binder 驱动进行通信。
- 获取结果
上面 transact 之后,我们就可以直接获取到结果了。
因为涉及到进程间通信,结果并不是马上就能获取到,Binder 驱动一定要现将调用者线程挂起,直到有了结果才能将它唤醒。这样做的好处是调用者可以像进程内函数调用一样去编写程序,而不用考虑很多的 IPC 的细节。
注意: 客户端和服务端的代码要保持一致。通常,使用 AIDL 产生的 Binder Server 会自动生成这些业务码,而不需要手工编写。
IBinder 和 BpBinder
Binder 提供的功能可以统一在 IBinder 中表示,至少有如下接口方法:
java
// framework/base/core/java/android/os/IBinder.java
public interface IBinder {
public IInterface queryLocalInterface (String descriotor);
public boolean transact(int code, Parcel data, Parcel reply, inte flags)
throws Remote Exception;
.....
}
此外,还应该有获取 IBinder 对象的一个类,即 BinderInternal
arduino
// framework/base/core/java/android/os/IBinder.java
public class BinderInternal {
public static final native IBinder getContextObject();
...
}
// framework/base/core/jni/android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz) {
// 这里返回的就是一个 BpBinder对象
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
getContextObject 是通过 ProcessState 来实现的,然后 把 ProcessState 中创建的对象转化成 Java层的 Binder 对象。
IBinder 只是一个接口类,显示还会有具体的实现类继承于它。
Native 层: BpBinder.cpp ------> 由 ProcessState 创建的。
Java 层: Binder.java 中的 BinderProxy ---> javaObjectForIBinder 通过 JNI 的 NewObject() 创建的。
这里继续看看 mRemote->transact 时的实现
java
// framework/base/core/java/android/os/Binder.java
final class BinderProxy implements IBinder {
public native boolean transact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException;
...
}
BinderProxy 中的 transact 就是一个 native 接口,真正的实现还是在 android_util_Binder.cpp 中
最后还是通过 BpBinder.transact 来处理用户的 Binder 请求:
arduino
// framework/native/libs/binder/BpBinder.java
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
if (mAlive) {
status_t status = IPCThreadState::slef()->transact(mHandle,code,data,reply,flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
绕了这么大的圈子,最后还是通过 IPCThreadState 及 ProcessState 来实现的。
IPCThreadState 及 ProcessState
关键点:
- 保证同一个进程中只有一个 ProcessState 实例存在,而且只有在 ProcessState 对象创建时才打开 Binder 设备以及做内存映射。
- 向上层提供 IPC 服务
- 跟 IPCThreadState 分工合作,各司其职。
php
// framework/native/libs/binder/ProcessState.cpp
sp<ProcessState> ProcessState::self() {
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState; // 创建对象
return gProcess;
}
我们接下来看看 ProcessState 的构造函数:
scss
ProcessState::ProcessState()
: mDriverFD(open_driver())
,mVMStart(MAP_FAILED), mManagesContexts(false)
, mBinderContextCheckFunc(NULL), mBinderContextUserData(NULL)
, mThreadPoolStarted(false), mThreadPoolSeq(1)
{
if (mDriverFD >= 0) { // 成功打开 /dev/binder
#if !defined(HAVE_WIN32_IPC)
// 开始执行 mmap, 内存块为 BINDER_VM_SIZE ,接近1M 的空间
mVMStart = mmap(0, BINDER_VM_SIZE, PORT_READ, MAP_PRIVATE
| MAP_NORESERVE, mDriverFD, 0);
这里调用了 open_driver() 打开了 /dev/binder 节点,然后执行 mmap() ,映射的内存块大为 BINDER_VM_SIZE.
之前我们在获取 IBinder 时使用了 BinderInternal 中的 getContextObject() ,这个方法最后的实现是在 ProcessState 中。
javascript
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
return getStrongProxyForHandle(0); // 传入0, 代表 SM
}
BpBinder 是 Native 层的代理,最后由 javaObjectForIBinder 转化为 Java 层的 BinderProxy
scss
// BpBinder.cpp
BpBinder::BpBinder(int32_t handle)
: mHandle(handle) // 如果是 SM, handle 为 0
, mAlive(1), mObitsSent(0), mObituaries(NULL)
{
...
extendObjectLifetime(OBJECT_LIFETIME_WEAK);
IPCThreadState::self()->incWeakHandle(handle);
}
IPCThreadState
有的变量希望只在本线程内是全局的,其他线程无法访问到,
TLS (Thread Local Storage) 机制就是为了解决这个问题提出来的,能够保证某个变量仅在自己线程中内访问有效,而其他线程中得到的是这个变量的独立副本,互不干扰。
我们来看一下 IPCThreadState 的构造函数
scss
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()), // ProcessState 整个进程只有一个
mMyThreadId(androidGetTid()), // 当前线程的 id
mStrictModePolicy(0), mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
// mIn 是一个 Parcel ,用于接收 Binder 发过来的数据
mIn.setDataCapacity(256);
// mOut 用于存储要发送给 Binder 的命令数据的
mOut.setDataCapacity(256);
IPCThreadState 负责与 Binder 驱动进行具体的命令交互(ProcessState 只是负责打开了 Binder 节点并做 mmap), 因此它的 transact 函数非常重要。
status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
java
getService@ServiceManagerProxy
transact@BinderProxy
transact@BpBinder
transact@IPCThreadState
Transaction 有 4 种 flag
- TF_ONE_WAY: 表示当前业务是异步的,不需要等待 (这个经常用到)
- TF_ROOT_OBJECT: 所包含的内容是根对象
- TF_SATUS_CODE: 所包含的内容是 32-bit 的状态值
- TF_ACCEPT_FDS = 0x10: 允许回复中包含文件的表述符
IPCThreadState 中与 Binder 驱动真正通信的地方就在 talkWithDriver 中。
arduino
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) { // Binder 设备还没有打开
return -EBADF;
}
binder_write_read bwr; // 读写都使用这个数据结构
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
Binder 的执行过程多数是阻塞型的(同步操作),Binder 去调用服务进程提供的接口函数,那么此函数执行结束时结果就已经产生,不涉及回调机制。
常见的机制就是,让调用者进程暂时挂起,直到目标进程返回结果后,Binder 再唤醒等待的进程。
binder_thread_write()
arduino
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
void _user *buffer, int size, signed long *consumed)
在 getService() 时
- proc: 调用者进程
- thread: 调用者线程
- buffer: 即 bwr.write_buffer , 在 talkWithDriver 中,它被设置成:
bwr.write_buffer = (long unsigned int)mOut.data();
// 先空一下,这块作者说的啰嗦,我看的好晕,先进入总结吧
总结
- ServiceManagerProxy
当某个 Binder Server 在启动时,会把自己的名称 name 和对应的 Binder 句柄值保存在 ServiceManager 中。调用者通常只知道 Binder Server 的名称,所以必须先向 Service Manager 发起查询请求,也就是 getService(name)。
而 Service Manager 自身也是一个 Server, 就好像互联网上的 DNS 服务器本身也需要提供IP地址才能访问一样。只不过这个IP地址是预先就设定好的(句柄值为0),因而任何Binder Client 都可以通过 0 这个 Binder 句柄创建一个 BpBinder, 再通过 Binder 驱动去获取 Service Manager 的服务。具体而言,就是调用 BinderInternal.getContextObject()来获得 Service Manager 的 BpBinder。
Android 系统同时支持 Java 与 c/c++ 层的 Binder 机制,因此很多对象都必须持有"双重身份",如BpBinder 在 Java 层以 IBinder 来表示。对于 Service Manager 而言,IBinder 的真正持有者与使用者是 ServiceManagerProxy。
- ProcessState 和 IPCThreadState
这个是Android 系统特别为程序进程使用 Binder 机制封装的两个类。
在 getService() 这个场景中,调用者是从 Java 层的 IBinder.transact() 开始,层层往下调用到 IPCThreadState.transact(), 然后通过 waitForResponse 进入主循环 ------ 直到收到 Service Manager 的回复后才跳出循环,并将结果再次层层传到应用层。真正与 Binder 驱动打交道的地方是 talkWithDriver()中的 ioctl()。
- Binder 驱动
- Service Manager 的实现