Android中的binder机制分析二:以精简的语句来提炼binder

一、 个人心得:
网上很多讲binder的书籍和博客,喜欢一上来就从智能指针开始,我个人觉得,对于binder机制的研究,没有必要去深入钻研智能指针,把binder中所有的sp和wp都当成普通的指针就行了,其次,另一个就是一上来开始分析源码, 然后大家就会被各种名词给混淆:
BnService, BpService, BBinder, Service Manager, binder driver, ProcessState,IPCThreadState等等,这些概念再混入到各种文件例如
IxxxService.h,IxxxService.cpp,总之,我刚开始是被完全晕了。。。
所以,我觉得一个不错的方式是先给出一个最简单的demo,至少,这个demo让大家知道,binder是基于C/S的通信架构,脑子里面有一个client端和service端的概念,那么去分析代码,会有一个大致的方向。

binder相关源码路径(Android4.4):
frameworks\native\include\binder
frameworks\native\libs\binder\

二、 用精简的话总结binder:
1. binder机制的核心是基于mmap的binder driver的运转;
这里的binder driver指的是Linux kernel中的binder驱动,说这个之前会抛出一个用户态和内核态的概念,理论上,不管是native层运行的程序也好,java层运行的程序也罢,都是在用户态运行的,以32位系统为例,每个程序(进程)都认为自己独享了3个G的用户空间,所以,两个进程A和B不能直接通信:
AB进程都认为自己在独享3GB的内存空间
但是,大于等于虚拟地址0XC000000之后的虚拟地址对于每个进程而言,都是一样的,唯一的缺陷就是用户态的程序不能直接访问这段内核地址,必须要以特定的方式,最常用的就是copy_from_user和copy_to_user。
而binder的实现,则是直接在Linux kernel中虚拟成了一个设备,借助操作驱动文件的方式操作binder,并且,binder没有完全使用copy_from_user和copy_to_user这种效率不高的字符操作函数,而是直接使用了更高效的mmap来实现用户态与内核态的互访;
好吧,说了这么说,好像又绕又废话,总之,就是不管我们上面java层,native层怎么折腾,怎么封装,最终,都是在binder driver中实现的;

2. Android系统只有一个service manager,并且,它是所有service的大管家;

天地浑沌如鸡子。盘古生在其中。万八千岁。天地开辟。阳清为天。阴浊为地。

好像扯得有点远,插入这句话是想说我们的service manager(SM)如同盘古一样在天地初始(系统启动)的时候,用一把斧头开天辟地(第一个打开binder驱动),世界才拥有了千秋万物(Android才拥有了各种service)。
虽然盘古是虚构的,可是我们的SM可是有实体的哦(一个可执行文件),就在这里:

frameworks\native\cmds\servicemanager\service_manager.c(Android4.4)
frameworks\native\cmds\servicemanager\binder.c

我们来具体分析一下这个代码。

int main(int argc, char **argv)
{
    struct binder_state *bs;
    /* svcmgr就是一个handle,值为0 */
    void *svcmgr = BINDER_SERVICE_MANAGER;

	/* 1.打开binder驱动 */
    bs = binder_open(128*1024);

	/* 2.把自己变成大管家,统领所有service */
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    svcmgr_handle = svcmgr;
	/* 3.开启循环,开始管事了 */
    binder_loop(bs, svcmgr_handler);
    return 0;
}

main函数中首先调用binder_open去驱动打开binder:

struct binder_state *binder_open(unsigned mapsize)
{
	****省略部分代码****
	
    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }

    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    
    ****省略部分代码**** 
}

可以看到这个函数其实就干了两件事,一个是打开设备文件,另一个是mmap出一段可以直接在用户态能够操作的内核态内存,大小是128k;
回到main函数 :

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

因为是按照驱动模式操作binder,自然ioctl是必不可少的了,这里直接告诉驱动把此进程设置成大管家;
最后看看大管家的循环:

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    unsigned readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    
    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));

    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;
		
		/* 不停地调用驱动中的ioctl */
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
		/* 解析指令 */
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

这个loop循环就很亲民化了,binder_write_read 是与驱动中binder通信的数据格式,不停地驱动ioctl看是否有指令,如果有的话就去解析;
好了,Android中的“盘古”SM代码就分析完了,它的使用自然是要与启动脚本init.rc联系在一起;

out\target\product\xxx\root

这里面有一段如下代码:

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system

3. client端和service端各有一个唯一ProcessState对象来协助BpxxxService和BnxxxService;
我们从之前的demo大概明白了这些:BpxxxService是client的代理服务,BnxxxService连接服务端与binder驱动的管道,程序实际中,client并不知道有这个叫BpxxxService的东西存在,以为自己是直接调用BnxxxService来获取所需的,那么,这个BpxxxService是怎么出现的呢?
我们先来看service端的main函数:

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

    /* 1. 打开binder driver并且进行内核地址的mmap */
    sp<ProcessState> proc(ProcessState::self());

    /* 2.获取Android的SM并将我们的服务添加进去 */
    sp<IServiceManager> sm = defaultServiceManager();
    sm->addService(String16(MyService::getServiceName()), new MyService());

    /* 3.启动子线程,一般的server都不需要再启动这样一个子线程来协助主线程工作 */
    ProcessState::self()->startThreadPool();

    /* 4.loop循环,从binder driver中获取指令,并由Bn的transact转为onTransact
     * 最终回调到Bn端,进而执行xxxService里面的代码 */
    IPCThreadState::self()->joinThreadPool();

    return 0;
}

分析第一步,看一下ProcessState::self():

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}

这里面是使用的单例设计模式,gProcess是一个全局变量,所以每个进程调用此函数只会有一个ProcessState对象,继续看下ProcessState构造函数:

ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
    	......
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
		......
}

截取关键代码如上,参数列表中mDriverFD是binder驱动的文件描述符,是通过
open_driver()来获取的,这个函数是不是很熟悉,跟SM应用程序里面打开binder操作如出一辙,跟进这个函数:

static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);
    if (fd >= 0) {
        fcntl(fd, F_SETFD, FD_CLOEXEC);
        int vers;
        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 does not match user space protocol!");
            close(fd);
            fd = -1;
        }
        size_t maxThreads = 15;
        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 '/dev/binder' failed: %s\n", strerror(errno));
    }
    return fd;
}

很简单,打开binder驱动,调用ioctl查询binder版本和设置最大子线程数(15);
继续回到构造函数,接下来就是调用mmap让系统在内核里面找一段内存,能让用户态直接访问的,内存大小为

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

返回的即这段内存的始地址;

第二步,调用defaultServiceManager获取SM,并添加我们的服务:

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    
    return gDefaultServiceManager;
}

这个也是单例设计模式,我们看一下interface_cast是什么:

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

这是一个模板类,返回的是上面的一个语句,那么这个语句跟我们的binder有什么关系?答案在供client端调用的IMyService.h和IMyService.cpp的两个关键宏上面:
IMyService.h有这个语句:

    /* 固定写法:此处必须继承自IInterface类 */
    class IMyService  : public IInterface   
    {
    public:
        DECLARE_META_INTERFACE(MyService);
        virtual int setNum(int a) = 0;
        virtual int getNum() = 0;
    };

DECLARE_META_INTERFACE是什么:

#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();

看得眼花,把参数带进来替换:

/* 定义descriptor */
static const android::String16 descriptor; 
/* 声明一个asInterface方法,很重要,预警 */
static android::sp<IMyService> asInterface(                      
	const android::sp<android::IBinder>& obj);     
/* 就是一个getter方法,没啥好说的 */             
virtual const android::String16& getInterfaceDescriptor() const;
/* 跟进我们传进来的参数,自动帮我们声明构造与析构,很贴心嘛 */
IMYSERVICE();                                                     
virtual ~IMYSERVICE();

c/c++中“##”表示宏参数连接,上述代码带进来,实际上就三个事:
a.descriptor()和getInterfaceDescriptor()是一对的,获取描述符;
b.声明构造和析构;
c.声明了一个asInterface函数;
看来,在这个头文件中声明这个宏很方便啊,一句话帮我们完善了很多内容~

继续看IMyService.cpp中下面这句话:

    /* 接口:这里面会去new        BpxxxSerivce*/
    IMPLEMENT_META_INTERFACE(MyService, "jztech.binder.IMyService");

跟踪一下,还是在IInterface.h中,这个略微复杂,但是跟头文件中的声明是对应的,这里是实现:

#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \

依旧看的头疼,但是还是要带入参数

/* 这两个函数是头文件中实现 */
    const android::String16  
    		IMYSERVICE::descriptor("jztech.binder.IMyService");             \
    const android::String16& 
    		IMYSERVICE::getInterfaceDescriptor()const {
	return IMYSERVICE::descriptor; 
}
	/* asInterface的实现 */
    android::sp<IMYSERVICE> IMYSERVICE::asInterface( 
            const android::sp<android::IBinder>& obj)                   
    {                                                                   \
        android::sp<IMYSERVICE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<IMYSERVICE*>(                          \
                obj->queryLocalInterface(                               \
                        IMYSERVICE::descriptor).get());               \
            if (intr == NULL) {     
            /* 实例化了BpxxxService */          
                intr = new BpMYSERVICE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    /* 构造和析构什么事都不用做 */
    IMYSERVICE::IMYSERVICE() { }                                    \
    IMYSERVICE::~IMYSERVICE() { }

我们重点关注的就是asInterface的实现,天啊,在这里面居然实例化了BpxxxService,再回想一下,IxxxService是client端直接调用的,简直就是在眼皮子底下实例化的,难怪说对client端,Bp是透明的,这两个宏真的是好一招瞒天过海。
我们往上回两级捋捋,现在我们在分析service端代码,然后分析到了一个return语句,返回的是asInterface函数,通过分析宏定义,我们知道了核心是创建了用于client的代码Bp,嗯…似乎有什么不对?等一下,我们现在不是在说service端吗?怎么莫名其妙地变成了client端,还去分析Bp啥时候创建的?其实,你如能这么想那就对了,这就是binder的C/S美妙架构,我们现在想往SM申请添加一个服务端,对于SM而言,我们不正是一个client?所以,从整个Android系统来看,就是SM,service,client三者之间的剪不清,理还乱的关系;
还有点东西需要提一下,到这里,我们获得的,就是SM的binder实例了,看一下返回类型:

sp<IServiceManager> sm = defaultServiceManager();

这个IServiceManager模式,似乎很熟悉,但是又说不上来,其实,他就是SM的接口,等价于我们这个MyService的接口,你点进去看IServiceManager类,有四个纯虚函数:

virtual sp<IBinder>         getService( const String16& name) const = 0;
virtual sp<IBinder>         checkService( const String16& name) const = 0;
virtual status_t            addService( const String16& name,
                                            const sp<IBinder>& service,
                                            bool allowIsolated = false) = 0;
virtual Vector<String16>    listServices() = 0;

我们的demo就只有两个纯虚函数嘛,所以,我想说,SM本身就是一个标准的binder例子,这也是为什么很多书籍和博客喜欢以SM来入手讲解binder的原因;
回到main函数的第三步,这里会去创建一个子线程,用于协助主线程来处理该进程注册的所有服务,这条语句可以省略,其本身也是继承自Thread类,就不详细分析了;
继续分析第四步:

    /* 4.loop循环,从binder driver中获取指令,并由Bn的transact转为onTransact
     * 最终回调到Bn端,进而执行xxxService里面的代码 */
    IPCThreadState::self()->joinThreadPool();

跟踪IPCThreadState::self()

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;
    }
    
    if (gShutdown) return NULL;
    
    pthread_mutex_lock(&gTLSMutex);
    if (!gHaveTLS) {
        if (pthread_key_create(&gTLS, threadDestructor) != 0) {
            pthread_mutex_unlock(&gTLSMutex);
            return NULL;
        }
        gHaveTLS = true;
    }
    pthread_mutex_unlock(&gTLSMutex);
    goto restart;
}

这里面重点关注实例化IPCThreadState,先看一下这个构造:

IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()),
      mMyThreadId(androidGetTid()),
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0)
{
    pthread_setspecific(gTLS, this);
    clearCaller();
    mIn.setDataCapacity(256);
    mOut.setDataCapacity(256);
}

前面已经实例化过ProcessState,所以,这里直接就能获取,mIn和mOut是用于打包Parcel数据的,这个Parcel之前似乎没有提过,是binder通信中一种封装好的用于用户态和binder驱动交互的数据格式;
构造比较简单,我们再去看一下引用的函数joinThreadPool:

void IPCThreadState::joinThreadPool(bool isMain)
{
	......
	
    do {
		......
		/* 查询client端是否有指令 */
        result = getAndExecuteCommand();
		......
        }
    } while (result != -ECONNREFUSED && result != -EBADF);
    
	......
}

整个函数被我精简了,只需要关注这个点:
既然service端要不停查询client端给过来的指令,那么,线程中的while循环自然是少不了了,分析一下getAndExecuteCommand:

status_t IPCThreadState::getAndExecuteCommand()
{
    status_t result;
    int32_t cmd;
	
	/* 1. 与binder驱动交互之前的准备 */
    result = talkWithDriver();
    if (result >= NO_ERROR) {
        size_t IN = mIn.dataAvail();
        if (IN < sizeof(int32_t)) return result;
        cmd = mIn.readInt32();
        IF_LOG_COMMANDS() {
            alog << "Processing top-level Command: "
                 << getReturnString(cmd) << endl;
        }

		/* 2.执行cmd */
        result = executeCommand(cmd);
        ......
    }

    return result;
}

talkWithDriver这个函数名取得有点名不副实,并没有与binder driver进行实质性的操作,那么executeCommand呢?

	.......
    case BR_TRANSACTION:
    .......
    else {
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
                if (error < NO_ERROR) reply.setError(error);
            }
    ......

我们在进行client端往service端调用的时候,执行的cmd是BR_TRANSACTION,这里有一个the_context_object,看一下出处:

sp<BBinder> the_context_object;

void setTheContextObject(sp<BBinder> obj)
{
    the_context_object = obj;
}

它其实就是BBinder,这个BBinder又是什么鬼?
我们需要捋一下继承关系:

xxxService->BnxxxService->BnInterface->BBinder

前面我们已经分析了,service端目前已经把MyService的服务添加到SM中了,与此同时,service端的线程也不停转起来了,并且从driver驱动里面拿到了client发过来的消息,所以,我们再看一下BBinder中transact干了什么:

status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    data.setDataPosition(0);

    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            reply->writeInt32(pingBinder());
            break;
        default:
            err = onTransact(code, data, reply, flags);
            break;
    }

    if (reply != NULL) {
        reply->setDataPosition(0);
    }

    return err;
}

它其实去调用了onTransact,这个函数我们又眼熟了,这不就是我们在IMyService.cpp中里面实现的

    status_t BnMyService::onTransact (uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
    {
        int ret = -1;
        switch (code) {
            case SET_NUM: 
                {
                    int num = -1;
                    ALOGD("BnMyService::onTransact  SET_NUM ");
                    num = data.readInt32();
                    ret = setNum(num);
                    reply->writeInt32(ret);
                    return NO_ERROR;
                }
            case GET_NUM:
                {
                    ALOGD("BnMyService::onTransact  GET_NUM ");
                    ret = getNum();
                    reply->writeInt32(ret);
                    return NO_ERROR;
                }
            default:
                return BBinder::onTransact(code, data, reply, flags);
        }
    }

这里就调用的MyService.cpp中的具体函数实现了;

总结一下binder的service端:
1.创建service进程唯一的ProcessState;
2. 以自己为client端,获取SM的binder实例,添加服务;
3. 创建一个IPCThreadState实例,进入线程循环;
4. 假定收到client端消息,由继承关系调用Bn的onTransact函数,并去调用xxxService中的函数实现,将结果写入binder驱动中返回给client;

client代码也是通过调用defaultServiceManager从而去创建client进程唯一的ProcessState实例,所以service端和client端都只有一个唯一的ProcessState对象。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值