Android Binder机制 -- libbinder

Binder简介

Binder是android系统提供的一种IPC机制。Android为什么选择binder?而不使用linux系统已有的IPC机制。具体原因不清楚,但是,看一下Binder机制的优点,我们可能能了解几分。

  1. 高效

    linux 的大多数IPC方式都需要至少两次内存拷贝。而Binder只需要一次内存拷贝。尽管共享内存的方式不需要多余的内存拷贝,但是对于多进程而言,要构建复杂的同步机制,这也会抵消共享内存零拷贝带来性能优势。

  2. 稳定

  3. 安全

libbinder

在native层,通过libbinder封装的类,我们能够很轻松(真的轻松吗?)的使用Binder机制来获取系统或应用提供的服务。

继承关系

其继承关系比较复杂,如果我们要实现自己的服务或代理类,我们需要实现的就是图中红色的部分。等了解了每个类的功能和知道如何实现binder service 和 binder client 后,再回过头来看这个类图,就会比较清晰了。

Android Binder机制 -- libbinder

类介绍

IBinder、BBinder 和 BpBinder

IBinder本质上就是一个接口类。提供了,BBinderBpBinder需要用到的通用函数及class。

BBinder对应的是Binder实体对象,简单的理解就是它表示 IPC通信过程中的 服务提供者。也就是C/S模式中的Server。

BpBinder对应的时Binder代理对象,代理的就是BBinder,通过BpBinder我们就能和BBinder对象建立联系。

IBinder中有一个比较重要的函数transact

// code 就是我们定义的协议码
// data 就是我们要发送的数据
// reply 就是接收到的数据
virtual status_t transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) = 0;

对于BBinder,作为Server,他只是接受来自BpBinder的请求,处理后回复,所以,BBinder::transact的实现就是处理数据并返回处理结果。

对于BpBinder,作为Client,他发送请求给BBinder并等待其处理结果。所以,BpBinder::transact的实现就是将数据发送给BBinder并等待其返回。

  1. BpBinder::transact
    Android Binder机制 -- libbinder

    BpBinder::mHandler, 这个handler对应这一个BBinder,通过它,我们就能将数据发送到BBinder所在的进程。并交由其transact函数处理。

    当我们要发送数据时,直接调用BpBinder::transact发送即可,其就会将数据交由我们代理的BBinder对象处理。数据传输工作由IPCThreadState::self()->transact完成,这里我们暂且不谈其实现细节。

  2. BBinder::transact

    Android Binder机制 -- libbinder
    Android Binder机制 -- libbinder

    BpBinder::transact执行后,数据到达BBinder所在的进程时,首先会执行BBinder::transact函数,该函数内部又调用了BBinder::onTransact函数来处理,该函数原型如下:

        virtual status_t    onTransact( uint32_t code,
                                        const Parcel& data,
                                        Parcel* reply,
                                        uint32_t flags = 0);
    

    是一个虚函数,这意味着,我们可以重写该函数来实现我们自己的协议和处理逻辑,这是我们自定义Binder service 的关键。

Interface、BnInterface 和 BpInterface

? 前面的BBinderBpBinder本质上是对代理对象和被代理对象直接数据传递方式的封装。BnInterfaceBpInterface则是对两者的数据协议的封装。

Android Binder机制 -- libbinder
Android Binder机制 -- libbinder

  1. DECLARE_META_INTERFACEIMPLEMENT_META_INTERFACE

    这两个宏的作用就是让我们少写了很多代码!!!其用法我们后面会提到。前者定义了一个成员变量和4个方法,后者就是实现了这4个方法。

  2. interface_cast

    这个函数的作用就是将IBinder对象转换为IInterface对象(实际类型需要运行时才知道)。

  3. BpRefBase

    前面的类图,可以看到BpRefBaseBpBinder之间是聚合关系。BpInterface继承了BpRefBase,而BpRefBase持有了BpBinder
    Android Binder机制 -- libbinder

    通过remote函数,我们就能拿到其持有的BpBinder,然后在BpInterface就能发送数据到BBinder对象了。

    为甚么是聚合关系,而不是BpInterface直接继承BpBinder??。我想,大概是,这样做,整个进程只会存在一个对象,便于Binder进行生命周期管理。

要彻底理解这几个类和宏的意义,还是要结合实例来理解,接下来我们就编写一个demo展示一下libbinder的用法。

使用方式

假定一个这样的场景,我们的程序运行在普通用户下,但是有时需要执行一些需要root权限的命令。我们就能创建一个运行在root权限下的service,程序通过Binder接口,将要执行的命令发送给service,并等待service 命令输出结果。

定义协议和接口

首先,我们需要继承IInterface类,并在定义好我们接口函数,这些接口函数在服务端和客户端都会被用到。

#include <binder/IInterface.h>
#include <string>

using namespace android;

class IExecService : public IInterface {
public:
    // 首先定义 协议码。协议码起始值必须大于等于  IBinder::FIRST_CALL_TRANSACTION
    enum {
        EXEC_COMMAND = IBinder::FIRST_CALL_TRANSACTION,
    };

    //定义元接口,
    DECLARE_META_INTERFACE(ExecService)

    // 定义我们的接口
    virtual std::string exec(const std::string &cmd) = 0;
};

首先DECLARE_META_INTERFACE,我们先看一下展开后的样子:

static const android::String16 descriptor;                          
static android::sp<IExecService> asInterface(                       
            const android::sp<android::IBinder>& obj);                  
virtual const android::String16& getInterfaceDescriptor() const;    
IExecService();                                                     
virtual ~IExecService();                                            

额,这里,比较重要的就是 asInterface方法。该方法主要是给Client端使用,将一个IBinder转换成IInterface对象。

Service 端实现

首先继承BnInterface

// 定义 服务接口
class BnExecService : public BnInterface<IExecService> {
protected:
    status_t onTransact( uint32_t code,
                         const Parcel& data,
                         Parcel* reply,
                         uint32_t flags = 0) override;
};

BnExecService类实际上继承了 BBinderIExecServiceIInterface。此时BnExecService还是一个抽象类,因为我们还没实现IExecService中的函数。前面我们提到过,当数据到来时,会执行到IBinder::transact--> BBInder::onTransact,那先来看看onTransact的实现。

status_t BnExecService::onTransact(uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
    switch (code) {
        case EXEC_COMMAND: {
            CHECK_INTERFACE(IExecService, data, reply);	
            std::string cmd(data.readCString());
            auto res = exec(cmd);
            reply->writeCString(res.c_str());
            return NO_ERROR;
        }
        default:
            return BBinder::onTransact(code, data, reply, flags);
    }
}

这也算是固定的写法吧,如果code不是我们自定义的,就调用BBinder::onTransact来处理(处理Android预定义的Code)。

此外,我们使用了CHECK_INTERFACE宏,那么在Client端发送数据时,首先就应该发送descriptor(DECLARE_META_INTERFACE宏帮我们的定义的)。

下一步就是实现exec方法,这里我们在BnExecService的子类中实现,当然也可以直接在BnExecService中实现。

class ExecService : public BnExecService {
public:

    // 实现咯
    std::string exec(const std::string &cmd) override {

        FILE * filp = popen(cmd.c_str(), "r");
        if (!filp) {
            return "Failed to popen";
        }

        std::string result;
        char line[1024]{};
        while (fgets(line, sizeof(line), filp) != nullptr) {
            result.append(line);
        }

        if (pclose(filp) != 0) {
            return "Failed to pclose";
        }
        return  result;
    }
};

最后就是将ExecService注册到ServiceMananger,这样Client就能通过ServiceManager引用到它。

ProcessState::self()->startThreadPool(); 
// 注册到 Service Manager
defaultServiceManager()->addService(String16("exec_service"), new ExecService());
IPCThreadState::self()->joinThreadPool();

ProcessStateIPCThreadState是binder通信的关键,其完成了实际的数据接发操作。后面会详细介绍的。

Client端实现

首先继承BpInterface,这样我们就能通过remote()方法获取到IBinder对象(实际类型是BpBinder)。

// 定义代理接口
class BpExecService : public BpInterface<IExecService> {
public:
    BpExecService(const sp<IBinder> &binder);

    std::string exec(const std::string &cmd) override;
};

对于BnExecService::exec其实现是执行真正的逻辑。而BpExecService::exec其实现就是将code和IPC相关的数据打包发送给Service

BpExecService::BpExecService(const sp<IBinder> &binder) : BpInterface<IExecService>(binder) {

}

std::string BpExecService::exec(const std::string &cmd) {
    Parcel data;
    Parcel reply;
    data.writeInterfaceToken(IExecService::getInterfaceDescriptor());
    data.writeCString(cmd.c_str());
    if (remote()->transact(EXEC_COMMAND, data, &reply, 0)) {
        return "";
    }

    return reply.readCString();
}

构造函数中的IBinder对象就是remote()方法返回的IBinder对象。这个对于对象从何而来???

还记得DECLARE_META_INTERFACE吗?其定义了几个方法。我们还没实现的,现在就要通过IMPLEMENT_META_INTERFACE宏实现了。

IMPLEMENT_META_INTERFACE(ExecService, "com.liutimo.IExecService");

第二个参数就是descriptor。其会在Service Mananger中显示。
Android Binder机制 -- libbinder

该宏展开后如下:

	const android::String16 IExecService::descriptor("com.liutimo.IExecService"); //初始化  descriptor           
	const android::String16&                                            
            IExecService::getInterfaceDescriptor() const {              
        return IExecService::descriptor;                                
    }                            
    android::sp<IExecService> IExecService::asInterface(                
            const android::sp<android::IBinder>& obj)                   
    {                                                                   
        android::sp<IExecService> intr;                                 
        if (obj != NULL) {                                              
            intr = static_cast<IExecService*>(                          
                obj->queryLocalInterface(                               
                        IExecService::descriptor).get());               
            if (intr == NULL) {                                         
                intr = new BpExecService(obj);                          
            }                                                           
        }                                                               
        return intr;                                                    
    }                                                                   
    IExecService::IExecService() { }                                    
    IExecService::~IExecService() { }                                   

这里比较重要的函数就是asInterface

先来看client 如何引用Service

ProcessState::self()->startThreadPool();
auto binder = defaultServiceManager()->getService(String16("exec_service"));
auto es = interface_cast<IExecService>(binder);
std::cout << es->exec(argv[1]) << std::endl;

interface_cast原型如下:

template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}

最终还是调用的IExecService::asInterface(obj)。回到asInterface的定义。

    android::sp<IExecService> IExecService::asInterface(                
            const android::sp<android::IBinder>& obj)                   
    {                                                                   
        android::sp<IExecService> intr;                                 
        if (obj != NULL) {                                              
            intr = static_cast<IExecService*>(                          
                obj->queryLocalInterface(                               
                        IExecService::descriptor).get());               
            if (intr == NULL) {                                         
                intr = new BpExecService(obj);                          
            }                                                           
        }                                                               
        return intr;                                                    
    } 

obj就是我们从service mananger拿到的IBinder对象(通过obj->transact方法就能传输数据到service端)。其实际类型分两种情况:

  1. Client 和 Service 位于同一个进程空间。obj实际类型就是BBinder
  2. Client 和 Service 位于不同的进程空间,obj实际类型就是BpBinder

queryLocalInterface的左右就是clientService是不是在同一个进程空间。是的话,asInterface返回的实际上就是ExecService对象,否则,就创建一个BpExecService对象返回(我们通过BpExecService::exec就能进行IPC调用了)。

libbinder数据是如何传输的

Android Binder机制 -- libbinder

上一篇:phpstudy mysql5.7无法启动成功


下一篇:05手机邮箱导航