Android Binder进程间通信机制

在Binder驱动中,通过binder_procs集合记录了所有使用Binder的进程。每个初次打开Binder设备的进程都会创建一个binder_proc结构体对象,用来描述使用Binder的进程,然后被添加到这个列表中的。

3.3 内存映射——binder_mmap


在打开Binder设备之后,上层进程就要通过mmap进行内存映射。mmap的作用有如下两个:

  • 申请一块内存空间,用来接收Binder通信过程中的数据

  • 对这块内存进行地址映射,以便将来访问

mmap在内核对应的就是binder_mmap()函数:在这个函数中,会申请一块物理内存,然后将用户空间和内核空间虚拟地址同时对应到这块物理内存上。在这之后,当有Client要发送数据给Server的时候,只需一次copy_from_user动作,将Client发送过来的数据拷贝到Server端的内核空间指定的内存地址即可(从而实现前文说的Binder通信机制只需要一次内存拷贝的效果),由于这个内存地址在服务端已经同时映射到用户空间,因此无需再做一次复制,Server即可直接访问,整个过程如下图所示: mmap_and_transaction.png

这幅图的说明如下:

  1. Server在启动之后,调用对/dev/binder设备调用mmap;

  2. 内核中的binder_mmap函数进行对应的处理:申请一块物理内存,然后在用户空间和内核空间同时进行映射

  3. Client通过BINDER_WRITE_READ命令发送请求,这个请求将先到驱动中,同时需要将数据从Client进程的用户空间拷贝到内核空间;

  4. 驱动通过BR_TRANSACTION通知Server有人发出请求,Server进行处理。由于这块内存也在用户空间进行了映射,因此Server进程的代码可以直接访问。

3.4 Binder驱动控制协议——binder_ioctl


binder_ioctl()实现了上层应用进程与Binder驱动之间的交互命令,可以说承载了Binder驱动的大部分业务,也是我们学习的重中之重。下面表格中列出binder_ioctl支持的命令:

| 命令 | 说明 |

| — | — |

| BINDER_WRITE_READ | 读写操作,可以用此命令向Binder读取或写入数据 |

| BINDER_SET_MAX_THREADS | 设置支持的最大线程数。因为客户端可以并发向服务器端发送请求,如果Binder驱动发现当前线程数已经超过设定值,就会告知Binder Server停止启动新的线程 |

| BINDER_SET_CONTEXT_MGR | Service Manager专用,将自己设置为“Binder大管家”。系统中只能有一个SM存在 |

| BINDER_THREAD_EXIT | 通知Binder线程退出。每个线程退出时都应该告知Binder驱动、才能释放相关资源;否则会造成内存泄漏 |

| BINDER_VERSION | 获取Binder版本号 |

其中BINDER_WRITE_READ这个命令是重点,又分为若干子命令,如下表所示:

| 命令 | 说明 |

| — | — |

| BC_TRANSACTION | Binder事务,即:Client对于Server的请求 |

| BC_REPLY | 事务的应答,即:Server对于Client的回复 |

| BC_ENTER_LOOPER | 通知驱动主线程ready |

| BC_REGISTER_LOOPER | 通知驱动子线程ready |

| BR_REPLY | 通知进程收到Binder请求的回复(Client) |

| BR_TRANSACTION_COMPLETE | 驱动对于接受请求的确认回复 |

| BR_TRANSACTION | 通知进程收到一次Binder请求(Server端) |

| BR_DEAD_BINDER | 发送死亡通知 |

| BR_SPAWN_LOOPER | 通知Binder进程创建一个新的线程 |

其中BC_TRANSACTION和BC_REPLAY是最关键的两个命令,Binder机制中Client与Server交互基本靠它们完成。

单独看上面的协议可能很难理解,这里我们以一次Binder请求过程来详细看一下Binder协议是如何通信的,就比较好理解了。 binder_request_sequence.png

这幅图的说明如下:

  • Binder是C/S架构的,通信过程牵涉到:Client,Server以及Binder驱动三个角色

  • Client对于Server的请求以及Server对于Client回复都需要通过Binder驱动来中转数据

  • BC_XXX命令是进程发送给驱动的命令

  • BR_XXX命令是驱动发送给进程的命令

  • 整个通信过程由Binder驱动控制

4 Binder Framework C++层

=====================================================================================

Binder Framework的C++部分:主要功能是实现向下与Binder驱动的对接交互,并封装复杂的内部实现,对外提供使用接口。头文件定义位于:/frameworks/native/include/binder/,实现位于这个路径:/frameworks/native/libs/binder/ 。Binder库最终会编译成一个动态链接库libbinder.so,供其他进程链接使用。为了便于说明,下文中我们将Binder Framework 的C++部分称为libbinder。

4.1 主要类结构


先用一张类图描述libbinder中的主要类结构之间的关系:

对照上面这张libbinder的设计类图,我们来理一下各个核心类的功能与职责:

  • 基类
  1. IBinder:Binder对象的基类,这个类描述了所有在Binder上传递的对象,它既是Binder服务端对象BBinder的父类,也是Binder客户端对象BpBinder的父类;主要定义的方法有:a.transact ———进行一次Binder操作;b.queryLocalInterface——尝试获取本地Binder对象;c.getInterfaceDescriptor ——获取Binder的服务接口唯一的描述;d.isBinderAlive——查询Binder服务是否还活着等;

  2. IInterfaceBinder服务接口的基类,Binder服务通常需要同时提供客户端接口和服务端接口。每个Binder服务都是为了某个功能而实现的,因此其本身会定义一套接口集来描述自己提供的所有功能。而Binder服务既有自身实现服务的类,也要有给客户端进程调用的类。为了便于开发,这两中类里面的服务接口应当是一致的。因此为了实现方便,本地实现类和远程接口类需要有一个公共的描述服务接口的基类(即上图中的IXXXService)来继承。而这个基类通常是IInterface的子类。

  • 客户端类
  1. BpBinder :BpBinder的实例代表了客户端Binder,这个类的对象将被客户端调用。这个类最重要就是提供了transact方法,这个方法会将客户端调用的参数封装好通过IPCThreadState逻辑封装后发送给Binder驱动。

  2. BpInterface:客户端接口的基类,远程接口是供客户端调用的接口集。BpInterface是个模板类,它们在继承自INTERFACE的基础上还继承了BpRefBase,通过这个类的remote方法可以获取到指向服务实现方的句柄。

  • 服务端类
  1. BBinder:BBinder的实例代表了服务端Binder,它描述了服务的提供方,所有Binder服务的实现者都要继承这个类(的子类),在继承类中,最重要的就是实现onTransact方法,因为这个方法是所有请求的入口。因此,这个方法是和BpBinder中的transact方法对应的,这个方法同样也有一个uint32_t code参数(统一在IBinder中定义),在这个方法的实现中,由服务提供者通过code对请求的接口进行区分,然后调用具体实现服务的方法。

  2. BnInterface:服务端接口的基类,是需要服务端服务中真正实现的接口集。BnInterface是个模板类,它们在继承自INTERFACE(Binder服务接口的基类,继承自IInterface)的基础上还继承了BBinder,由此可以通过复写onTransact方法来提供实现

  • 与驱动通信的类
  1. ProcessState : 代表使用Binder的进程。在讲解Binder驱动的时候我们就提到:任何使用Binder机制的进程都必须要对/dev/binder设备进行open以及mmap之后才能使用,这部分逻辑是所有使用Binder机制进程共同的。对于这种共同逻辑的封装便是Framework层的职责之一。libbinder中,ProcessState类封装了这个逻辑,从而负责进程Binder的初始化

  2. IPCThreadState代表了进程中使用Binder的线程,这个类中封装了与Binder驱动通信的具体实现逻辑

在客户端接口的实现类中,每个接口在组装好参数之后,都会调用remote()->transact来发送请求,而这里其实就是调用的BpBinder的transact方法,这样请求便通过Binder到达了服务实现方的onTransact中。这个过程如下图所示

4.2 进程的Binder初始化(ProcessState)


之前讲解Binder驱动的时候讲过,需要使用Binder的进程,需要先通过binder_open打开Binder设备,然后通过binder_mmap进行内存映射。由于这部分逻辑是所有使用Binder的进程共有的,所以libbinder中使用ProcessState类统一封装了这个逻辑,具体代码如下:

/framework/native/libs/binder/ProcessState.cpp/

#define BINDER_VM_SIZE ((110241024) - (4096 *2)) // 1M - 8k

ProcessState::ProcessState(const char *driver)
mDriverName(String8(driver))

, mDriverFD(open_driver(driver))// 1.调用open_driver打开binder驱动设备

, mVMStart(MAP_FAILED)

, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)

, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)

, mExecutingThreadsCount(0)

, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)

, mStarvationStartTimeMs(0)

, mBinderContextCheckFunc(nullptr)

, mBinderContextUserData(nullptr)

, mThreadPoolStarted(false)

, mThreadPoolSeq(1)

, mCallRestriction(CallRestriction::NONE)

{

// TODO(b/139016109): enforce in build system

#if defined(ANDROID_APEX)

LOG_ALWAYS_FATAL(“Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.”);

#endif

if (mDriverFD >= 0) {

// 2.mmap执行内存映射动作

// mmap the binder, providing a chunk of virtual address space to receive transactions.

mVMStart = mmap(nullptr, 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();

}

}

}

这是ProcessState构造函数,Binder的初始化工作都是在这个函数中完成的。在这个函数中,初始化mDriverFD的时候调用了open_driver方法打开binder设备,然后又在函数体中,通过mmap进行内存映射。这里需要注意的是mmap函数的参数中有个BINDER_VM_SIZE,这个就是指定在内核上开辟的内存空间的大小,也是一次Binder通信传递的最大数据量,目前默认定义大小是1016个字节(1M-8K)。这个其实也就解释了我们平常开发过程中遇到的一个问题,就是通过Intent传递过大的对象时会报TransactionTooLargeException异常的原因。open_driver动作具体代码如下:

/framework/native/libs/binder/ProcessState.cpp/

#define DEFAULT_MAX_BINDER_THREADS 15

static int open_driver(const char *driver)

{

int fd = open(driver, O_RDWR | O_CLOEXEC);// 1.通过open系统调用打开了dev/binder设备

if (fd >= 0) {

int vers = 0;

// 2.通过ioctl获取Binder实现的版本号,并检查是否匹配

status_t result = ioctl(fd, BINDER_VERSION, &vers);

if (result == -1) {

ALOGE(“Binder ioctl to obtain version failed: %s”, strerror(errno));

close(fd);

fd = -1;

}

if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {

ALOGE(“Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d”,

vers, BINDER_CURRENT_PROTOCOL_VERSION, result);

close(fd);

fd = -1;

}

size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;

// 3.通过ioctl设置进程支持的最大Binder线程数

result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);

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;

}

open_driver函数中除了实现通过open系统调用打开了dev/binder设备外。还会通过ioctl调用的BINDER_SET_MAX_THREADS命令设置进程支持的最大Binder线程数,目前定义的一般进程默认最大Binder线程数是15。这个也解释了我们在开发过程中遇到一类性能问题:就是进程的Binder线程耗尽,此时进程中已经开启了15个Binder线程用于处理binder请求且都处于工作或阻塞状态,此时如果有新的binder请求时就无法继续处理了,从而导致阻塞卡主;

4.3 与Binder驱动的通信(IPCThreadState)


IPCThreadState是一个单例类,进程中的每个Binder线程存在一个实例,负责了与驱动通信的细节处理。这个类中的关键几个方法说明如下:

IPCThreadState.PNG

以其中最主要的是transact方法为例,该方法会完成一次Binder数据传输动作,代码如下:

/framework/native/libs/binder/IPCThreadState.cpp/

status_t IPCThreadState::transact(int32_t handle,

uint32_t code, const Parcel& data,

Parcel* reply, uint32_t flags)

{

// 1.通过writeTransactionData完成数据组装写入

err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, nullptr);

// 2.判断是否为TF_ONE_WAY异步单向请求,如果是线程不需要阻塞等待返回值,直接返回即可

if ((flags & TF_ONE_WAY) == 0) {

// 3.通过waitForResponse将数据传输到驱动并等待返回结果

if (reply) {

err = waitForResponse(reply);

} else {

Parcel fakeReply;

err = waitForResponse(&fakeReply);

}

} else {

err = waitForResponse(nullptr, nullptr);

}

return err;

}

从代码可以看出,通过Binder驱动完成一次Binder数据传输的主要流程是

  1. 先通过writeTransactionData函数完成传输数据binder_transaction_data的组装填充;

  2. 通过waitForResponse函数真正完成传输数据写入驱动并等待返回数据,具体实现会调用talkWithDriver函数,通过ioctl命令BINDER_WRITE_READ与Binder驱动通信,完成数据的传输写入,并读取返回数据。

详细代码如下:

/framework/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

binder_transaction_data tr;

tr.target.ptr = 0; /* Don’t pass uninitialized stack data to a remote process */

tr.target.handle = handle;

tr.code = code;

tr.flags = binderFlags;

tr.cookie = 0;

tr.sender_pid = 0;

tr.sender_euid = 0;

const status_t err = data.errorCheck();

if (err == NO_ERROR) {

tr.data_size = data.ipcDataSize();

tr.data.ptr.buffer = data.ipcData();

tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);

tr.data.ptr.offsets = data.ipcObjects();

} else if (statusBuffer) {

tr.flags |= TF_STATUS_CODE;

*statusBuffer = err;

tr.data_size = sizeof(status_t);

tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);

tr.offsets_size = 0;

tr.data.ptr.offsets = 0;

} else {

return (mLastError = err);

}

mOut.writeInt32(cmd);

mOut.write(&tr, sizeof(tr));

return NO_ERROR;

}

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

{

uint32_t cmd;

int32_t err;

while (1) {

// 调用talkWithDriver真正完成传输数据写入驱动并等待返回数据

if ((err=talkWithDriver()) < NO_ERROR) break;

}

}

status_t IPCThreadState::talkWithDriver(bool doReceive)

{

do {

IF_LOG_COMMANDS() {

alog << "About to read/write, write size = " << mOut.dataSize() << endl;

}

#if defined(ANDROID)

// 通过ioctl命令BINDER_WRITE_READ与Binder驱动通信

if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

err = NO_ERROR;

else

err = -errno;

} while (err == -EINTR);

}

4.4 C++ Binder服务实现举例


上面的理论知识可能比较枯燥和抽象,下面以Android系统中的一个系统Binder服务为例,结合上面的理论具体分析一下。 我们以实现系统图像合成显示的核心进程服务SurfaceFlinger为例,来分析C++的Binder服务如何实现。下图是SurfaceFlinger C++ Binder服务实现类图:

ISurfaceComposer定义了SurfaceFlinger对外提供的功能接口,其子类都继承了这些接口。

  • BpSurfaceComposer是提供给客户端调用的远程接口,先通过remote方法获取到指向服务实现方的句柄,然后通过transact方法发送具体请求。

  • BnSurfaceComposer中只有一个onTransact方法,该方法根据请求的code来对接每个请求,并直接调用SurfaceFlinger中对应的方法。

  • SurfaceFlinger是服务接口功能真正的实现。

4.4.1 服务端实现

主要包括BnSurfaceComposer和SurfaceFlinger两个类,SurfaceFlinger是BnSurfaceComposer的子类,因此在BnSurfaceComposer中调用自身的virtual方法其实都是在子类SurfaceFlinger类中实现的。BnSurfaceComposer类要做的就是复写onTransact方法,这个方法的职责是:根据请求的code区分具体调用的是那个接口,然后按顺序从Parcel中读出打包好的参数,接着调用留待子类实现的虚函数。我们看一下BnSurfaceComposer::onTransact中的代码片段:

/framework/native/libs/gui/ISurfaceComposer.cpp/

status_t BnSurfaceComposer::onTransact(

uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

switch(code) {

case CREATE_DISPLAY: {

CHECK_INTERFACE(ISurfaceComposer, data, reply);

String8 displayName = data.readString8();

bool secure = bool(data.readInt32());

sp display(createDisplay(displayName, secure));

reply->writeStrongBinder(display);

return NO_ERROR;

}

}

}

这段代码中我们看到了实现中是如何根据code区分接口,并通过Parcel读出调用参数,然后调用具体服务方的。而SurfaceFlinger这个类中的createDisplay方法才是真正实现创建显示设备的逻辑,详细代码如下:

/framework/native/services/surfaceflinger/SurfaceFlinger.cpp/

sp SurfaceFlinger::createDisplay(const String8& displayName, bool secure) {

class DisplayToken : public BBinder {

sp flinger;

virtual ~DisplayToken() {

// no more references, this display must be terminated

Mutex::Autolock _l(flinger->mStateLock);

flinger->mCurrentState.displays.removeItem(this);

flinger->setTransactionFlags(eDisplayTransactionNeeded);

}

public:

explicit DisplayToken(const sp& flinger)
flinger(flinger) {

}

};

sp token = new DisplayToken(this);

Mutex::Autolock _l(mStateLock);

// Display ID is assigned when virtual display is allocated by HWC.

DisplayDeviceState state;

state.isSecure = secure;

state.displayName = displayName;

mCurrentState.displays.add(token, state);

mInterceptor->saveDisplayCreation(state);

return token;

}

4.4.2 注册服务

服务实现完成之后,并不是立即就能让别人使用的。而是需要先通过Binder驱动向ServiceManager中注册服务,表明可以对外提供服务。下面我们看看SurfaceFlinger服务注册发布的具体代码实现:

/frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp/

int main(int, char**) {

// When SF is launched in its own process, limit the number of

// binder threads to 4.

ProcessState::self()->setThreadPoolMaxThreadCount(4);

// 1.启动Binder线程池

// start the thread pool

sp ps(ProcessState::self());

ps->startThreadPool();

// instantiate surfaceflinger

sp flinger = surfaceflinger::createSurfaceFlinger();

setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);

set_sched_policy(0, SP_FOREGROUND);

// initialize before clients can connect

flinger->init();

// 2.通过IServiceManager::addService在ServiceManager中注册服务,此处服务的名称为“SurfaceFlinger”

// publish surface flinger

sp sm(defaultServiceManager());

sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,

IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);

// run surface flinger in this thread

flinger->run();

return 0;

}

由此可见,发布Binder服务需要先启动Binder线程池,然后通过IServiceManager::addService在ServiceManager中进行服务的注册。

4.4.3 客户端实现

BpSurfaceComposer需要实现ISurfaceComposer中的所有接口。我们以上文提到的createDisplay接口为例,来看看BpSurfaceComposer::createDisplay方法代码是如何实现的:

/frameworks/native/libs/gui/ISurfaceComposer.cpp/

virtual sp createDisplay(const String8& displayName, bool secure)

{

Parcel data, reply;

// 1.填写服务接口的唯一描述信息descriptor

data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());

// 2.通过Parcel写入发送参数

data.writeString8(displayName);

data.writeInt32(secure ? 1 : 0);

// 3. 调用remote()->transact将请求发送出去

remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply);

return reply.readStrongBinder();

}

这段代码很简单,逻辑就是:通过Parcel写入调用参数进行打包,然后调用remote()->transact将请求发送出去。

4.4.4 获取服务

客户端在使用服务端的服务之前,需要先根据服务的名称向ServiceManager中查询获取到访问服务的句柄,才能通过BpSurfaceComposer::remote()函数拿到远端服务的代理后调用transact方法完成一次Binder数据发送。详细代码如下:

/framworks/native/libs/gui/SurfaceComposerClient.cpp/

void ComposerService::connectLocked() {

// 1. 需要查找的远端Binder服务名称为"SurfaceFlinger"

const String16 name(“SurfaceFlinger”);

// 2 .调用IServiceManager::getService封装接口获取远端服务代理并封装到BpSurfaceComposer中

while (getService(name, &mComposerService) != NO_ERROR) {

usleep(250000);

}

}

/frameworks/native/include/binder/IServiceManager.h/

template

status_t getService(const String16& name, sp* outService)

{ // 1. 获取到ServiceManager的代理以访问ServiceManager

const sp sm = defaultServiceManager();

if (sm != nullptr) {

// 2. 通过getService获取具体的服务句柄,并使用interface_cast转换获取到BpSurfaceComposer对象

*outService = interface_cast(sm->getService(name));

if ((*outService) != nullptr) return NO_ERROR;

}

return NAME_NOT_FOUND;

}

/frameworks/native/libs/binder/IServiceManager.cpp/

sp defaultServiceManager()

{

std::call_once(gSmOnce, {

sp sm = nullptr;

while (sm == nullptr) {

//ProcessState::getContextObject(nullptr)获取到ServiceManager的访问句柄

sm = interface_cast(ProcessState::self()->getContextObject(nullptr));

}

}

});

return gDefaultServiceManager;

}

/frameworks/native/libs/binder/ProcessState.cpp/

sp ProcessState::getContextObject(const sp& /caller/)

{

// 此处的参数“0”代表查找的是ServiceManager服务,这个是个特殊的地址,用于标识ServiceManager

sp context = getStrongProxyForHandle(0);

return context;

}

通过interface_cast这个方法来获取服务的接口对象,由这个方法本身根据是否是在同一个进程,来自动确定返回一个本地Binder还是远程Binder。interface_cast是一个模板方法,其源码如下:

/frameworks/native/libs/binder/include/binder/IInterface.h/

template

inline sp interface_cast(const sp& obj)

{

return INTERFACE::asInterface(obj);

}

对于ISurfaceComposer来说,其实就是ISurfaceComposer::asInterface(obj),其定义如下:

/frameworks/native/libs/binder/include/binder/IInterface.h/

::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \

const ::android::sp<::android::IBinder>& obj) \

{ \

::android::sp<I##INTERFACE> intr; \

if (obj != nullptr) { \

//1.先使用queryLocalInterface尝试获取本地同一进程中的Binder对象

intr = static_cast<I##INTERFACE*>( \

obj->queryLocalInterface( \

I##INTERFACE::descriptor).get()); \

//2.失败则创建并返回远端Binder对象,封装成BpSurfaceComposer

if (intr == nullptr) { \

intr = new Bp##INTERFACE(obj); \

} \

} \

return intr; \

}

由于都是模板定义,所以在理解的时候,将“##”替换成SurfaceComposer即可。

5 ServiceManager——Binder服务“大管家”

=============================================================================================

ServerManager(简称SM)的功能可以类比互联网中的DNS服务器,“IP地址”为0。每一个Binder服务都需要有一个唯一的名称,由SM来管理这些服务的注册和查找。另外,和DNS服务器本身也是服务器一样,SM也是一个标准的Binder Server,且Binder驱动中,也会通过特定handle = 0这个位置来访问ServiceManager。因此,分析SM我们可以完整的看到一个上层应用如何通过Binder驱动来构建一个Binder Server的过程。下面我们分析SM模块的的具体实现。

5.1 ServiceManager进程的启动


SM作为Binder服务的“大管家”,必须要保证在系统所有Binder服务启动之前就已经启动并处于正常工作状态。所以SM是在开机时由init程序解析init.rc加载时直接启动。如下所示:

/framework/native/cmds/servicemanager/servicemanager.rc/

service servicemanager /system/bin/servicemanager// 1.servicemanager是一个独立可执行文件

class core animation

user system

group system readproc

critical

onrestart restart healthd

onrestart restart zygote// 2.servicemanager重启后会导致zygote重启

onrestart restart audioserver

onrestart restart media

onrestart restart surfaceflinger// 3.servicemanager重启后会导致surfaceflinger重启

onrestart restart inputflinger

onrestart restart drm

onrestart restart cameraserver

onrestart restart keystore

onrestart restart gatekeeperd

onrestart restart thermalservice

writepid /dev/cpuset/system-background/tasks

shutdown critical

由上可见,servicemanager是一个独立可执行文件,由C++编写,是一个独立运行的native进程,在系统开机时有init进程直接拉起。源码路径如下:

/framework/native/cmds/servicemanager/*

其main函数主要逻辑如下:

/framework/native/cmds/servicemanager/main.cpp/

int main(int argc, char** argv) {

if (argc > 2) {

LOG(FATAL) << “usage: " << argv[0] << " [binder driver]”;

}

const char* driver = argc == 2 ? argv[1] : “/dev/binder”;

//1.通过initWithDriver会完成打开/dev/binder下的binder驱动设备和mmap内存映射动作

sp ps = ProcessState::initWithDriver(driver);

ps->setThreadPoolMaxThreadCount(0);

ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);

//2.创建ServiceManager对象,并通过addService接口将自己注册为名为“manager”的Binder Server

sp manager = new ServiceManager(std::make_unique());

if (!manager->addService(“manager”, manager, false /allowIsolated/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {

LOG(ERROR) << “Could not self register servicemanager”;

}

IPCThreadState::self()->setTheContextObject(manager);

ps->becomeContextManager(nullptr, nullptr);// 3.通知binder驱动将自己注册为binder服务大管家

sp looper = Looper::prepare(false /allowNonCallbacks/);

BinderCallback::setupTo(looper);

ClientCallbackCallback::setupTo(looper, manager);

while(true) {

looper->pollAll(-1);// 4.循环等待其它模块请求服务

}

// should not be reached

return EXIT_FAILURE;

}

主要做了如下四件事情:

  1. 通过调用ProcessState的initWithDriver静态接口,这其中会创建SM进程的ProcessState对象并完成进程的Binder初始化:先调用open_driver动作打开Binder驱动,然后调用mmap动作通知Binder驱动完成binder_mmap物理内存分配和虚拟内存映射的操作;

  2. 创建ServiceManager对象,并通过addService接口将SM自身注册为名为“manager”的Binder Server服务,也进一步说明SM自身本质上也是一个C++写的Binder Server服务进程

  3. 调用ProcessState的becomeContextManager接口,通过binder_ioctl协议命令BINDER_SET_CONTEXT_MGR通知Binder设备驱动,将SM注册为binder服务“大管家”

  4. 进入循环等待Binder Client进程的客户端进程访问请求。

5.2 ServiceManager进程Binder服务接口


我们看看ServiceManager.h接口文件的代码定义:

/framework/native/cmds/servicemanager/ServiceManager.h/

class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {

public:

ServiceManager(std::unique_ptr&& access);

~ServiceManager();

// getService will try to start any services it cannot find

// 1.根据名称查询Binder Server服务

binder::Status getService(const std::string& name, sp* outBinder) override;

binder::Status checkService(const std::string& name, sp* outBinder) override;

// 2.注册Binder Server服务

binder::Status addService(const std::string& name, const sp& binder,

bool allowIsolated, int32_t dumpPriority) override;

// 3.遍历列出所有已注册的Binder Server服务

binder::Status listServices(int32_t dumpPriority, std::vectorstd::string* outList) override;

};

根据上一节对Binder framework C++层的分析我们可以看到:ServiceManager继承自BnServiceManager,是一个Binder Server服务端实现。对客户端提供getService查询Service服务、addService注册Service服务以及listServices遍历所有已注册服务等核心功能接口。 其内部具体的实现是通过一个全局的名为mNameToService的ServiceMap类型(本质上还是一个map类型的集合)的变量记录维护着服务名称和具体的服务代理之间映射关系;以addService注册服务的实现代码为例:

/framework/native/cmds/servicemanager/ServiceManager.cpp/

Status ServiceManager::addService(const std::string& name, const sp& binder, bool allowIsolated, int32_t dumpPriority) {

auto entry = mNameToService.emplace(name, Service {

.binder = binder,

.allowIsolated = allowIsolated,

.dumpPriority = dumpPriority,

.debugPid = ctx.debugPid,

});

return Status::ok();

}

5.3 libbinder中对访问ServiceManager接口的封装


源码路径:

frameworks/native/include/binder/IServiceManager.h frameworks/native/libs/binder/IServiceManager.cpp

这块逻辑相当于是Binder framework C++层的libbinder库作为Binder客户端对ServiceManager进程所提供的Binder服务端接口访问的封装。

IServiceManager的C++接口定义如下:

/frameworks/native/include/binder/IServiceManager.h/

class IServiceManager : public IInterface

{

public:

// 1.根据名称查询Binder Server服务

/**

  • Retrieve an existing service, blocking for a few seconds

  • if it doesn’t yet exist.

*/

virtual sp getService( const String16& name) const = 0;

/**

  • Retrieve an existing service, non-blocking.

*/

virtual sp checkService( const String16& name) const = 0;

// 2.注册Binder Server服务

/**

  • Register a service.

*/

// NOLINTNEXTLINE(google-default-arguments)

virtual status_t addService(const String16& name, const sp& service,

bool allowIsolated = false,

int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) = 0;

// 3.遍历列出所有已注册的Binder Server服务

/**

  • Return list of all existing services.

*/

// NOLINTNEXTLINE(google-default-arguments)

virtual Vector listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0;

/**

  • Efficiently wait for a service.

  • Returns nullptr only for permission problem or fatal error.

*/

virtual sp waitForService(const String16& name) = 0;

};

普通的Binder服务我们需要通过ServiceManager来获取接口才能调用,那么ServiceManager的接口又该如何获得呢?在libbinder中,提供了一个defaultServiceManager方法来获取ServiceManager的代理,并且这个方法不需要传入参数。原因我们在驱动篇中也已经讲过了:Binder的实现中,为ServiceManager留了一个特殊的位置,不需要像普通服务那样通过标识去查找。defaultServiceManager代码如下:

/frameworks/native/libs/binder/IServiceManager.cpp/

sp defaultServiceManager()

{

std::call_once(gSmOnce, {

sp sm = nullptr;

while (sm == nullptr) {

sm = interface_cast(ProcessState::self()->getContextObject(nullptr));

if (sm == nullptr) {

ALOGE(“Waiting 1s on context object on %s.”, ProcessState::self()->getDriverName().c_str());

sleep(1);

}

}

gDefaultServiceManager = new ServiceManagerShim(sm);

});

return gDefaultServiceManager;

}

6 Binder Framework Java层

======================================================================================

6.1 主要结构


Android应用使用Java语言开发,Binder Framework框架自然也是提供了相关Java接口。前面我们已经分析了Binder Framework C++层的完整实现。因此Java层完全不用重复实现,而是通过虚拟机提供的JNI机制,直接调用而复用C++层的实现。下面借用一张图描述Binder Framework Java层到C++层的调用关系。

6.2 JNI衔接调用


JNI全称是Java Native Interface,这个是由Java虚拟机提供的机制。这个机制使得native代码可以和Java代码互相通讯。简单来说就是:我们可以在C/C++端调用Java代码,也可以在Java端调用C/C++代码。实际上,在Android中很多的服务或者机制都是在C/C++层实现的,想要将这些实现复用到Java层,就必须通过JNI进行衔接。其实这里面就是解决两个方向的调用问题:

  1. Java端的代码是如何调用的libbinder中的C++方法的? 以BinderProxy.java中的transact方法为例,其功能是实现Java层的Binder客户端使用服务端的远端代理完成一次Binder数据发送传输,简化代码实现如下:

/frameworks/base/core/java/android/os/BinderProxy.java/

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

try {

return transactNative(code, data, reply, flags);

} finally {

}

}

/**

  • Native implementation of transact() for proxies

*/

public native boolean transactNative(int code, Parcel data, Parcel reply,

int flags) throws RemoteException;

总结

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后如何才能让我们在面试中对答如流呢?

答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:

这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
JNI全称是Java Native Interface,这个是由Java虚拟机提供的机制。这个机制使得native代码可以和Java代码互相通讯。简单来说就是:我们可以在C/C++端调用Java代码,也可以在Java端调用C/C++代码。实际上,在Android中很多的服务或者机制都是在C/C++层实现的,想要将这些实现复用到Java层,就必须通过JNI进行衔接。其实这里面就是解决两个方向的调用问题:

  1. Java端的代码是如何调用的libbinder中的C++方法的? 以BinderProxy.java中的transact方法为例,其功能是实现Java层的Binder客户端使用服务端的远端代理完成一次Binder数据发送传输,简化代码实现如下:

/frameworks/base/core/java/android/os/BinderProxy.java/

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {

try {

return transactNative(code, data, reply, flags);

} finally {

}

}

/**

  • Native implementation of transact() for proxies

*/

public native boolean transactNative(int code, Parcel data, Parcel reply,

int flags) throws RemoteException;

总结

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后如何才能让我们在面试中对答如流呢?

答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:

[外链图片转存中…(img-Kdd4X4Eo-1714482923090)]

这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。

[外链图片转存中…(img-5tgEmS1k-1714482923091)]

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值