Android-Framework:Binder全解析(一)这篇看完还不懂,请给我寄刀片!(2)

{
signal(SIGPIPE, SIG_IGN);
//获取ProcessState实例
sp proc(ProcessState::self());
//获取BpServiceManager对象
sp sm(defaultServiceManager());

InitializeIcuOrDie();
//注册MediaPlayerService
MediaPlayerService::instantiate();//1
ResourceManagerService::instantiate();
registerExtensions();
//启动Binder线程池
ProcessState::self()->startThreadPool();
//当前线程加入到线程池
IPCThreadState::self()->joinThreadPool();
}

第二行代码是获取ProcessStateProcessState是何许人也?ProcessState从名称就可以看出来,用于代表进程的状态

我们看一下ProcessState::self()这个方法:

//frameworks/native/libs/binder/ProcessState.cpp
sp ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState(“/dev/binder”);//1
return gProcess;
}

这是一个单例模式用于获取ProcessState,每个进程只有一个。眼尖的小伙伴应该发现了一个熟悉的字符串,没错就是这个/dev/binder,这就是没有物理介质的Binder驱动。那么ProcessState的构造方法又做了哪些事情呢?

ProcessState

//frameworks/native/libs/binder/ProcessState.cpp
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver))//1 这一行很重要,打开了Binder驱动
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if (mDriverFD >= 0) {
//2 mmap内存映射
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// sigh
ALOGE(“Using %s failed: unable to mmap transaction memory.\n”, mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, “Binder driver could not be opened. Terminating.”);
}

注释1处,ProcessState的构造方法首先调用了open_driver()方法,这个方法一看名字就知道,是打开驱动嘛,它的参数不就是我们刚才看到的传进去的/dev/binder,也就是说ProcessState在构造方法处打开了Binder驱动。看看它是怎么打开驱动的吧

open_driver

//frameworks/native/libs/binder/ProcessState.cpp
static int open_driver(const char *driver)
{
int fd = open(driver, O_RDWR | O_CLOEXEC);//1
if (fd >= 0) {

size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);//2
if (result == -1) {
ALOGE(“Binder ioctl to set max threads failed: %s”, strerror(errno));
}
} else {
ALOGW(“Opening ‘%s’ failed: %s\n”, driver, strerror(errno));
}
return fd;
}

注释1处用于打开/dev/binder设备并返回文件操作符fd,这样就可以操作内核的Binder驱动了。

Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。

注释2处的ioctl函数的作用就是和Binder设备进行参数的传递,这里的ioctl函数用于设定binder支持的最大线程数为15(maxThreads的值为15)。

在用户空间,使用ioctl方法系统调用来控制设备。这是方法原型:

/*
fd:文件描述符
cmd:控制命令
…:可选参数:插入argp,具体内容依赖于cmd/
int ioctl(int fd,unsigned long cmd,…);

用户程序所作的只是通过命令码告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。所以在用户空间我们想做什么事情都是通过这个方法以命令的形式告诉Binder驱动,Binder驱动收到命令执行相应的操作。

mmap

在刚才的注释2处就是大名鼎鼎的内存映射。内存映射函数mmap,给binder分配一块虚拟地址空间。它会在内核虚拟地址空间中申请一块与用户虚拟内存相同大小的内存,然后再申请物理内存,将同一块物理内存分别映射到内核虚拟地址空间和用户虚拟内存空间,实现了内核虚拟地址空间和用户虚拟内存空间的数据同步操作。

这是函数原型:

//原型
/*
addr: 代表映射到进程地址空间的起始地址,当值等于0则由内核选择合适地址,此处为0;
size: 代表需要映射的内存地址空间的大小,此处为1M-8K;
prot: 代表内存映射区的读写等属性值,此处为PROT_READ(可读取);
flags: 标志位,此处为MAP_PRIVATE(私有映射,多进程间不共享内容的改变)和 MAP_NORESERVE(不保留交换空间)
fd: 代表mmap所关联的文件描述符,此处为mDriverFD;
offset:偏移量,此处为0。

此处 mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
/
void
mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset)

总的来说,ProcessState做了两件事情,一是打开Binder驱动并返回文件操作符fd,二是通过mmap为Binder分配了一块虚拟内存空间,以达到内存映射的目的。

MediaPlayerService::instantiate

这个方法呢就是注册服务:

void MediaPlayerService::instantiate() {
//注册服务
defaultServiceManager()->addService(String16(“media.player”), new MediaPlayerService());
}

defaultServiceManager返回的是BpServiceManager,这里先不讲BpServiceManager,你只需要知道BpServiceManager它实现了IServiceManager,并且通过内部有一个变量mRemote= BpBinderBpBinder用来实现跨进程通信。 故此处等价于调用BpServiceManager->addService

BpBinder是Client端与Server交互的代理类,而BBinder则代表了Server端。BpBinderBBinder是一一对应的。BpBinder会通过handle来找到对应的BBinder。在ServiceManager中创建了BpBinder,通过handle(值为0)可以找到对应的BBinder

来看一下具体代码

//frameworks/native/libs/binder/IServiceManager.cpp
virtual status_t addService(const String16& name, const sp& service, bool allowIsolated) {
Parcel data, reply; //Parcel是数据通信包
//写入头信息"android.os.IServiceManager"
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name); // name为 “media.player”
data.writeStrongBinder(service); // MediaPlayerService对象
data.writeInt32(allowIsolated ? 1 : 0); // allowIsolated= false
//remote()指向的是BpBinder对象
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}

服务注册过程:向ServiceManager注册服务MediaPlayerService,服务名为”media.player”。addService函数的作用就是将请求数据打包成data,然后传给BpBinder的transact函数。

data.writeStrongBinder(service)这行代码中,将Binder对象进行了扁平化,转换成flat_binder_object对象。

  • 对于Binder实体,则cookie记录Binder实体的指针;
  • 对于Binder代理,则用handle记录Binder代理的句柄;(句柄这个翻译真的是脑残,你可以直接理解为ID或者编号)
BpBinder::transact

我们看一下transact函数:

// frameworks/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}

return DEAD_OBJECT;
}

Binder代理类调用transact()方法,真正工作还是交给IPCThreadState来进行transact工作。先来 看看IPCThreadState::self的过程。

IPCThreadState::self

//frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState* IPCThreadState::self()
{
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState; //初始IPCThreadState
}

if (gShutdown) return NULL;

pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) { //首次进入gHaveTLS为false
if (pthread_key_create(&gTLS, threadDestructor) != 0) { //创建线程的TLS
pthread_mutex_unlock(&gTLSMutex);
return NULL;
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex);
goto restart;
}

TLS是指Thread local storage(线程本地储存空间),每个线程都拥有自己的TLS,并且是私有空间,线程之间不会共享。通过pthread_getspecific/pthread_setspecific函数可以获取/设置这些空间中的内容。从线程本地存储空间中获得保存在其中的IPCThreadState对象。这里可以得知IPCThreadState::self()实际上是为了创建IPCThreadState.

IPCThreadState

//frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
mMyThreadId(gettid()),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0)
{
pthread_setspecific(gTLS, this);
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
}

pthread_setspecific函数用于设置TLS,将IPCThreadState::self()获得的TLS和自身传进去。每个线程都有一个IPCThreadState,每个IPCThreadState中都有一个mIn、一个mOut,它们都是Parcel对象。成员变量mProcess保存了ProcessState变量(每个进程只有一个)。

  • mIn 用来接收来自Binder设备的数据,默认大小为256字节;
  • mOut用来存储发往Binder设备的数据,默认大小为256字节。

那这里构造完了,看一下IPCThreadStatetransact函数:

IPCThreadState::transact

status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck(); //数据错误检查
flags |= TF_ACCEPT_FDS;

if (err == NO_ERROR) { // 传输数据
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}

if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
//等待响应
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}

} else {
//oneway,则不需要等待reply的场景
err = waitForResponse(NULL, NULL);
}
return err;
}

IPCThreadState进行transact事务处理分3部分:

  • errorCheck() //数据错误检查
  • writeTransactionData() // 传输数据
  • waitForResponse() //等待响应

其中writeTransactionData函数用于传输数据,其中第一个参数BC_TRANSACTION代表向Binder驱动发送命令协议,向Binder设备发送的命令协议都以BC_开头,而Binder驱动返回的命令协议以BR开头。

我们就要看看到底是怎么写进去数据的:

IPCThreadState.writeTransactionData

//frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
tr.target.ptr = 0;
tr.target.handle = handle; // handle = 0
tr.code = code; // code = ADD_SERVICE_TRANSACTION
tr.flags = binderFlags; // binderFlags = 0
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;

// data为记录Media服务信息的Parcel对象
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize(); // mDataSize
tr.data.ptr.buffer = data.ipcData(); //mData
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t); //mObjectsSize
tr.data.ptr.offsets = data.ipcObjects(); //mObjects
} else if (statusBuffer) {

} else {
return (mLastError = err);
}

mOut.writeInt32(cmd); //cmd = BC_TRANSACTION
mOut.write(&tr, sizeof(tr)); //写入binder_transaction_data数据
return NO_ERROR;
}

binder_transaction_data结构体是binder驱动通信的数据结构,该过程最终是把Binder请求码BC_TRANSACTIONbinder_transaction_data结构体写入到mOut。其中handle的值用来标识目的端,注册服务过程的目的端为service manager,此处handle=0所对应的是binder_context_mgr_node对象,正是service manager所对应的binder实体对象。

transact方法中执行完writeTransactionData方法后,接下来就会执行waitForResponse()方法

IPCThreadState.waitForResponse

//frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;//1
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
cmd = (uint32_t)mIn.readInt32();
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;

case BR_DEAD_REPLY:
err = DEAD_OBJECT;
goto finish;

default:
//处理各种命令协议
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:

return err;
}

这里开启了一个死循环,然后从talkWithDriver()方法中发送并读取数据。在waitForResponse过程, 首先执行BR_TRANSACTION_COMPLETE;另外,目标进程也就是Server端收到事务后,处理BR_TRANSACTION事务。 然后发送给当前进程也就是Client端,再执行BR_REPLY命令。这是通信的具体流程:

[图片上传中…(image-683419-1599468127794-12)]

Binder协议包含在IPC数据中,分为两类:

  1. BINDER_COMMAND_PROTOCOL:binder请求码,以”BC_“开头,简称BC码,用于从IPC层传递到Binder Driver层;
  2. BINDER_RETURN_PROTOCOL :binder响应码,以”BR_“开头,简称BR码,用于从Binder Driver层传递到IPC层;

在这个过程中, 收到以下任一BR_命令,处理后便会退出waitForResponse()的状态:

  • BR_TRANSACTION_COMPLETE: binder驱动收到BC_TRANSACTION事件后的应答消息; 对于oneway transaction,当收到该消息,则完成了本次Binder通信;
  • BR_DEAD_REPLY: 回复失败,往往是线程或节点为空. 则结束本次通信Binder;
  • BR_FAILED_REPLY:回复失败,往往是transaction出错导致. 则结束本次通信Binder;
  • BR_REPLY: Binder驱动向Client端发送回应消息; 对于非oneway transaction时,当收到该消息,则完整地完成本次Binder通信;

talkWithDriver()这个方法是真正的向内核发送消息的方法。这是talkWithDriver

IPCThreadState.talkWithDriver()

//frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{

//和Binder驱动通信的结构体
binder_write_read bwr;
//mIn是否有可读的数据,接收的数据存储在mIn
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

bwr.write_size = outAvail;
//将要发送给Binder设备的消息填充到与Binder通信的结构体中
bwr.write_buffer = (uintptr_t)mOut.data();

if (doReceive && needRead) {
//接收数据缓冲区信息的填充。如果以后收到数据,就直接填在mIn中了。
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
//当读缓冲和写缓冲都为空,则直接返回
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
//通过ioctl不停的读写操作,跟Binder Driver进行通信
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;

} while (err == -EINTR); //当被中断,则继续执行

return err;
}

跟内核通讯都是通过ioctl方法进行的。在前面的介绍中已经讲解了这个方法的原型。binder_write_read结构体用来与Binder设备交换数据的结构, 通过ioctl与mDriverFD通信,是真正与Binder驱动进行数据读写交互的过程。 主要是操作mOut和mIn变量。

binder_write_read是整个Binder IPC过程,最为核心的数据结构之一。

看懵了?捋一下思路,数据的转变是这样的:

IServiceManager.addService()方法将数据打包为Parcel---->IPCThreadState.writeTransactionData()Parcel打包为binder_transaction_data----->IPCThreadState.talkWithDriver()方法将binder_transaction_data打包为binder_write_read

接下来就要进入到内核Binder中了。

Binder Driver

在了解Binder驱动之前,首先我们来了解一个概念,就是 binder_procs。什么是 binder_procsbinder_procs是一个链表。对于底层Binder驱动,通过 binder_procs 链表记录所有创建的 binder_proc 结构体,binder 驱动层的每一个 binder_proc 结构体都与用户空间的一个用于 binder 通信的进程一一对应。可以理解为,一个binder_proc 结构体对应一个服务进程而binder_procs记录了所有的binder_proc 结构体

每个进程有且只有一个 ProcessState 对象(上文中用来打开Binder驱动并分配内存的那个家伙),这是通过单例模式来保证的。在每个进程中可以有很多个线程,每个线程对应一个 IPCThreadState(上文中用来发送通信数据给Binder驱动的那个家伙)对象,IPCThreadState 对象也是单例模式,即一个线程对应一个 IPCThreadState 对象,在 Binder 驱动层也有与之相对应的结构,那就是 Binder_thread 结构体。在 binder_proc 结构体中通过成员变量 rb_root threads,来记录当前进程内所有的 binder_thread。

对于一个binder_proc,也就是一个服务进程来说。它有四个非常重要的结构体,分别是binder_threadbinder_nodebinder_refbinder_buffer

  • Binder实体对象binder_node,表示当前进程提供了哪些binder服务;

可以将binder_proc理解为一个进程,而将binder_noder理解为一个Service。binder_proc里面有一个红黑树,用来保存所有在它所描述的进程里面创建Service。而每一个Service在Binder驱动里面都有一个binder_node来描述。

  • Binder引用对象binder_ref,表示哪些进程持有当前进程的binder的引用;每一个Clinet在驱动中都有一个binder_ref和他对应,内部有对应的进程信息binder_proc和binder_node信息。client端拿着BpBinder引用对象,在驱动层对应着binder_ref,就可以找到binder_proc和binder_node信息,找到指定的进程可以实现交互。

  • Binder_thread,进程在启动时会创建一个binder线程池,并向其中注册一个Binder线程;驱动发现没有空闲binder线程时会向Server进程注册新的的binder线程。对于一个Server进程有一个最大Binder线程数限制,默认为16个binder线程。对于所有Client端进程的binder请求都是交由Server端进程的binder线程来处理的。

  • binder_buffer 内核缓冲区,用以在进程间传递数据。Binder驱动将Binder进程的内存分成一段一段进行管理,而这每一段则是使用binder_buffer来描述的。

上图是从结构体的角度来看待binder_procs,我们换个角度,从进程与线程的角度来看一下是什么样

有的小伙伴会发现,在Binder_proc或者binder_thread中有个list_head todo这样的东西。这是个什么东西呢?list_head看名字就知道了是个头结点,todo呢?是要做的事情。没错,这个是用来保存要做的事情的队列。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

如何做好面试突击,规划学习方向?

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。

学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

我搜集整理过这几年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节

img

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

XBsb2FkX2ltYWdlcy8xNjU5NTAzMS03NzcyMmNhYzA0YWM1NTI5?x-oss-process=image/format,png)

在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值