Binder 通信流程之详细分解

2016-5-22 chenhui Binder

阅读本文前,请先阅读:Binder 通信流程 否则陷进去反倒理不清楚。


下面对 Binder 通信流程的每一个步骤的代码进行解释,力求读者能弄懂 Binder 是怎么进行通信的。


=== 第一次通信开始

第一步、得到或创建 SM 的代理对象,这样才能和 SM 通信。


在这里要先搞清楚一点:我们向 Binder 驱动发送 BC_TRANSACTION 命令时,实际上是要和一个 Server 通信,而和 Server 通信需要获得该 Server 的代理对象,SM 本身就是一个特殊的 Server,所以这里要从获取 Server 的代理对象开始。


SM 代理对象是通过 defaultServiceManager() 方法来获取的,在第一次调用他并获取代理对象后,他就把代理对象保存在一个成员变量内,以后再调用就直接返回:


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

这里的 gDefaultServiceManager 就是之前说的保存代理对象的成员变量。IServiceManager 是 SM 代理对象的方法表,该函数的返回值 sp<IServiceManager> 是一个指向 SM 代理对象方法表的智能指针。


注意,IServiceManager 不是代理对象,而是代理对象的方法表。这个 IServiceManager 由 SM 提供,我们通过他来执行对代理对象的一些操作。


获取 SM 代理对象的关键方法是 ProcessState::self() 这个方法以及其返回值的 getContextObject 方法,最后调用 interface_cast 把最终的返回值封装成 SM 代理对象。


ProcessState::self() 这个方法不需要深入了解,我们只需要知道每个进程都有一个唯一的 ProcessState 对象,这个方法就创建(已创建就返回)了这么一个对象。

我们需要了解的是,ProcessState 的构造方法都干了些什么,实际上很简单,就是先打开 Binder 创建 Binder 对象并返回句柄,再映射这个句柄,这样就相当于为这个句柄对应的 Binder 对象创建了数据缓冲区,有了数据缓冲区,就能保存数据,也就能和其他进程进行通信了。


ProcessState::ProcessState()
    : mDriverFD(open_driver()) // mDriverFD = open_driver(),即打开 Binder
    ...
{
    ...
    mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
    ...
}

至于 open_driver() 的实现,相比接触过驱动的同学都能了解,就是打开 /dev/ 目录下的某个文件呗,没错,打开的就是 /dev/binder。


static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);

	...
	
    return fd;
}

由此可见,defaultServiceManager() 干的第一件事情就是和 Binder 驱动进行连接。



和 Binder 驱动进行连接后,就要开始获取 SM 的代理对象了,这就是通过 getContextObject() 方法来实现的。


sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
    ...
    return getStrongProxyForHandle(0);
}

这里调用的 getStrongProxyForHandle() 方法就是用来获得一个 Server 的代理对象的方法,调用他时传入的值就是代理对象的句柄,SM 比较特殊,所以他的句柄永远是 0。因为我们和 Server 通信时,他的代理对象是一直要保存着的,不可能每次通信都获取一次,所以得到代理对象后,都要把代理对象的某个值作为句柄,并保存下来,这样就能重复利用代理对象。


Android 在保存句柄时,通过 handle_entry 对象封装了他,并保存在一个哈希表内。在我们用句柄来查找哈希表时,如果不存在对应的 handle_entry,那么系统就会自动创建一个对应指定句柄的 handle_entry 对象。handle_entry 的 iBinder 成员指向代理对象, 在 handle_entry 刚创建时,其为空。


那么 getStrongProxyForHandle() 又应该怎么做呢?很简单,先查找哈希表,他肯定会返回一个 handle_entry 对象,如果这个对象的 iBinder 成员不为空,就说明有代理对象了,直接返回。如果没有,那就要创建一个新的代理对象。


但是!这里新创建的代理对象,不能直接拿来使用,因为这里的代理对象是一个空的代理对象!


sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);
	
	// 得到 handle_entry 对象
    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
		
        IBinder* b = e->binder;
        // 没有代理对象,则新建一个
		if (b == NULL || !e->refs->attemptIncWeak(this)) {
			
			// BpBinder 就是代理对象
            b = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // 已有代理对象,直接返回他
			
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

由此可见,defaultServiceManager() 干的第二件事也就是 getContextObject(),他的目的就是创建一个新的代理对象。如果代理对象已经有了,则直接返回他。


现在,按照我们的流程下来,现在我们已经得到了一个空的代理对象,这个空的代理对象起不到啥作用,接下来还要把他封装成一个 SM 代理对象,封装过程就是通过 interface_cast<IServiceManager>() 实现的。


怎么创建呢?很简单,通过这个空的代理对象,封装一个 BpServiceManager 对象,这个就是 SM 代理对象了。怎么封装呢?这就要请出我们的 IServiceManager 这个 SM 代理对象方法表了。


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


interface_cast() 是一个模版方法,INTERFACE 会被替换成 IServiceManager,所以这里就是调用 IServiceManager 的 asInterface() 方法来把 BpBinder 代理对象封装成 SM 代理对象。



或许是为了方便程序员进行拓展,asInterface() 方法并不是直接定义的,而是通过 IMPLEMENT_META_INTERFACE 宏定义的,下面来看看这个宏和 IServiceManager::asInterface() 的定义。



#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() { }                                   \

// 下面是 asInterface 

IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");

// 下面是展开后的代码

const android::String16 IServiceManager::descriptor("android.os.IServiceManager"); 
const android::String16&                                            
        IServiceManager::getInterfaceDescriptor() const {           
    return IServiceManager::descriptor;                             
}                                                                   
android::sp<IServiceManager> IServiceManager::asInterface(             
        const android::sp<android::IBinder>& obj)                   
{                                                                   
    android::sp<IServiceManager> intr;                              
    if (obj != NULL) {                                              
        intr = static_cast<IServiceManager*>(                       
            obj->queryLocalInterface(                               
                    IServiceManager::descriptor).get());            
        if (intr == NULL) {                                         
            intr = new BpServiceManager(obj);                       
        }                                                           
    }                                                               
    return intr;                                                    
}                                                                   
IServiceManager::IServiceManager() { }                              
IServiceManager::~IServiceManager() { }         


现在就清楚了,其实就是把 BpBinder 对象作为参数来创建一个 BpServiceManager 对象,这个也就是 SM 代理对象。

那么 BpServiceManager 的构造函数里又干了些啥呢?呵呵,什么都不干,那是个空方法。


第二步、Client 把数据封装到 Parcel 对象内,并和 Parcel 对象和 binder_transaction_data 对象进行关联,然后把 BTD 对象写到输出缓冲区。再把 BTD 在输出缓冲区内的开始地址和大小封装到 binder_write_read 内,使用 ioctl 把 BC_TRANSACTION 命令和 BWR 发送给 Binder 驱动。


既然我们已经和 Binder 建立的连接,也有了自己的数据缓冲区,更有了 SM 代理对象,接下来就要和 SM 进行通信了。


那么,他应该如何向 Binder 驱动发起通信呢?我们知道:Binder 进程通信的数据保存在一个 binder_transaction_data 结构体内;而进程和 Binder 驱动进行通信则通过 binder_write_read 结构体。也就是说,进程和进程之间的 Binder 通信通过 BTD 对象来实现;而进程和 Binder 驱动的通信则通过 BWR 对象来实现。


这样这一步的步骤就简单了:

  1. 把 Parcel 对象里的数据的开始地址和大小保存到一个 binder_transaction_data 结构体内,这个结构体作为进程通信的一次数据传输
  2. 把 binder_transaction_data 这个结构体写到 binder_write_read 结构体内,BWR 结构体的作用是把 BTD 对象写入 Binder 驱动。



这个步骤我拿注册 Server 组件时调用的 defaultServiceManager()->addService() 方法来举例。



virtual status_t addService(const String16& name, const sp<IBinder>& service)
{
    Parcel data, reply;
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    data.writeString16(name);
    data.writeStrongBinder(service);
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}


这里有两个 Parcel 对象:data 和 reply。其中 data 保存发送出去的数据,reply 则用来接收返回值。

4~6 这三行代码,就是把注册 Service 所需要的信息写进去:

第一个在第一步里提到过,是 "android.os.IServiceManager" 这个字符串

第二个是要注册的 Service 名

第三个是要注册的 Service 的本地对象(代理对象是对 BpBinder 的封装,本地对象是对 BBinder 的封装)。writeStrongBinder() 这个方法会把本地对象解析成一个 flat_binder_object 对象,再写入到 Parcel 对象。


把这些数据写到 Parcel 对象里后,就调用 IServiceManager 的 remote()->transact() 方法向 SM 发送一个 ADD_SERVICE_TRANSACTION 命令,其中 data 保存了发送的数据所以直接传参,而 reply 由于要接收返回值,所以只是传一个地址过去,这样 Binder 驱动得到 Server 返回的数据后,就把数据写到 reply 上了。


可能你会觉得奇怪,不是要发 BC_TRANSACTION 命令吗?怎么成了 ADD_S... 呢?很简单,在前文提到过,和进程之间通信要用 BTD 对象,ADD_S.. 这个命令就是用来标识 BTD 对象的,对方进程(Service)得到得到 BTD 对象并取出标识后,就知道他要干些什么事了(SM得到ADD_S, 就知道要注册)。


而 BC_TRANSACTION 命令,则是标识 BWR 对象的,他的作用是告诉 Binder 驱动我要干什么,这就和 ADD_S.. 分开了。

别急,我们继续往下看。


remote() 可忽略,他得到的就是 SM 代理对象。



status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}


这里又涉及到 IPCThreadState::self() 这个方法以及后面的 transact() 方法。这个 IPCThreadState 和上面提到的 ProcssState 一样,都是一个进程里只有一个的对象,他的 self() 方法就创建(已创建则返回)这个对象出来,所以关键的是他的 transact() 方法。


transact() 方法做了些什么呢?其实就是开头说的,通过 BTD 对象和 BWR 对象来进行通信。


这里还有一点要注意的是,每个进程都有两个缓冲区,分别是输出缓冲区和输入缓冲区。输出缓冲区的作用是当进程需要发送命令到 Binder 驱动内时,就要先把该命令和附带的数据写到这个缓冲区内,一般来说这个附带数据就是一个 BTD 对象;输入缓冲区的作用是 Binder 驱动从 Server 得到返回值后,把返回值写到输入缓冲区,再通知 Client,最后 Client 从输入缓冲区中得到数据


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

    ...
	
	
    if (err == NO_ERROR){
			
	... 
	// 把 Parcel 对象里的内容写入 BTD 对象内,并把该对象和命令写入输出缓冲区
	err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
	
    if ((flags & TF_ONE_WAY) == 0) {
       
		...
		
    // 开始等待 Binder 的返回值
    if (reply) {
	// 需要返回值
        err = waitForResponse(reply);
    } else {
	// 不需要返回值
        Parcel fakeReply;
        err = waitForResponse(&fakeReply);
    }
		
    ...
    } else {
        err = waitForResponse(NULL, NULL);
    }
    
    return err;
}


writeTransactionData() 这个方法创建了一个 BTD 对象,并把 ADD_S... 命令作为该 BTD 对象的行动代码(即该 BTD 对象的作用是注册 Service),再记录 Parcel 对象保存的数据的开始地址和大小,最后把 BC_TRANSACTION 命令和 BTD 对象一同写入输出缓冲区。


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;

    // 把代理对象句柄和 ADD_S.. 写入 BTD 对象
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
	// BTD 对象记录 Parcel 对象保存的内容的开始地址和大小
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        ...
    } else {
        return (mLastError = err);
    }
    
    // 把 BC_TRANSACTION 和 BTD 对象写入输出缓冲区
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    
    return NO_ERROR;
}


写入缓冲区后,接下来要干的就是通过  BWR 对象写入 Binder 驱动并等待返回值,这个就是 waitForResponse() 实现的。



status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
		
	// 在这里和 Binder 驱动进行通信
        if ((err=talkWithDriver()) < NO_ERROR) break;
		
	// 到了这里就说明一次通信完毕,Binder 返回了某个值
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        
	// 第一个值必然是一个命令
        cmd = mIn.readInt32();
        
        ...
		
        switch (cmd) {
			
	// Server 接受通信
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        
		...
		
	// 通信结束,获取返回值
        case BR_REPLY:
            ....
            goto finish;

        default:
            ...
            break;
        }
    }

finish:
    ...
    return err;
}


这个函数的主体是一个 while(1) 循环,循环开头就调用 talkWithDriver() 和 Binder 进行通信,怎么通信呢?很简单,就是使用 BWR 对象把输出缓冲区里的开始地址和大小记录下来,然后把 BWR 对象写到 Binder 驱动内,Binder 驱动根据开始地址和大小从用户空间读出数据后,就开始执行相应的操作了,对于 BC_TRANSACTION 而言,就是要和 Server 进行通信。这些是第三步的内容。


循环后头是处理 Binder 返回值的部分,对于本文介绍的通信流程而言,Binder 一共会返回两次值。

  1. Binder 要和 Server 建立通信后,向 Client 发送的 BR_TRANSACTION_COMPLETE 命令,这个命令表示对方已接受通信。对这个命令的处理方法很简单,就是看是否需要接收 Server 返回的返回值,如果不需要,那数据已经发送给 Server 了,这里就直接退出。
  2. 在需要接收返回值的情况下,第一次返回值的处理方式就是跳出 switch,又回到 while 循环的开头继续等待 Binder 返回的返回值,然后再读出命令,这次读出的命令一般就是 BR_REPLY 命令。


关于这两个返回值的处理,我会在第三步和第六步再次涉及到。


那么 talkWithDriver() 是怎么和 Binder 驱动通信的呢?我们依然可以猜测一下。首先,BWR 对象是跑不掉的,然后,在之前提到过,BWR 对象既可以从 Binder 中读取数据,也可以发送数据到 Binder 驱动内,毫无疑问,我们这次是要把输出缓冲区的 BC_TRANSACTION 命令和 BTD 对象写到 Binder 驱动内,但是,BWR 对象怎么知道我是要读,还是要写?


很简单,只要给他传个参数指定是否需要读或写即可,事实上,还真有这么一个参数,这个参数不是指定写,而是指定是否读,他默认为 true 也就是读,但只有他还不够,输入缓冲区也必须为空,这意味着上一条命令已处理完。在指定读操作之后,就无法再进行写操作;


那么怎么知道是否需要写呢?很简单,只要下面两个条件满足其中一条,并输出缓冲区不为空即可:

  1. 没有指定读操作,理由同上,指定了读操作,就不能再进行写操作,即使读操作的要求不满足;
  2. 输入缓冲区为空,这意味着上一条命令已处理完毕,既然已处理完毕,那他的内存不能白放着,用了算了



 

status_t IPCThreadState::talkWithDriver(bool doReceive)
{ 

    binder_write_read bwr;
    
    // 输入缓冲区是否为空
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    
    // 需要写到 Binder 驱动里的数据长度,如果为 0,则不需要写。故读时为0,缓冲区不为空也为0
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    bwr.write_size = outAvail;
    bwr.write_buffer = (long unsigned int)mOut.data();

    // This is what we'll read.
    if (doReceive && needRead) {
	// 需要读,则记录读
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
    }
    
    ... 
    
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do { 
	// 向 Binder 发送 BINDER_WRITE_READ 命令,表示有 BWR 对象需要处理
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
 
    } while (err == -EINTR);
     

    ...
	
    return err;
}


经过上面的解释,这个函数就很容易看懂了。不过这里要注意的是,读写都可以进行操作。只要输入缓冲区为空,那么即使他只允许接受返回命令,他也一定会把输出缓冲区里的命令发出去;输入缓冲区不为空但没有指定只接收返回命令(doReceive=false),那他也一定会写。

 

而对于读来说,在只允许接收返回命令(默认值为true)和输入缓冲区为空的情况下,他也一定为空,在本情景中,他一定会读。


自此,代码的执行进入内核空间。


=== 第二次通信开始

第三步、Binder 驱动从用户空间读入 BWR 对象,并把 BWR 的行为(读or写)交给 Client 线程池中的一个线程进行操作。


对于写,他会从用户空间读出 BC_TRANSACTION  命令和 BTD 对象,根据命令的含义,他从 BTD 对象中读出对端(Service)信息,得到对端的引用对象。


根据对端的引用对象,得到其实体对象,并从其线程池中找出合适线程来处理这次消息。根据 BTD 对象把数据从用户空间的 Parcel 对象中读到 binder_transtion 对象内,最后 BT 对象会被插入线程的工作队列,并唤醒该线程来处理这个 BT 对象。


最后,构造一个 binder_work 给源线程(当前线程自身),表示数据已发送。当前线程处理到这个消息时,就会把 BR_TRANSACTION_COMPLETE 写回用户空间,最终进程会根据是否需要接收返回值来决定是否需要再进行一次第三步,只不过这一次是 BWR 行为是读,读的就是返回值。


第二步调用 ioctl 把 BWR 对象的用户空间地址传入内核空间后,最后就会调用到 binder 驱动的 file_operations 结构体的 ioctl 函数,这个函数就是 binder_ioctl()。


和之前一样,先猜测一下 binder_ioctl() 都会干些啥。


首先,必须得知道进程调用 ioctl() 要干些什么事,其实很明了了,就是 BINDER_WRITE_READ,也就是读或写。


然后,我们知道,进程和 Binder 驱动通过 BWR 对象来进行通信(读写),而进程在用户空间调用 ioctl 时传入了 BWR 对象的地址,这个地址是用户空间的地址,所以 binder_ioctl() 紧接着要做的就是把这个 BWR 对象从用户空间读到内核空间。


再然后就是重中之重了,那就是处理读写,怎么处理呢?我们知道,每一个进程在 Binder 驱动里都有一个线程池,每一个线程用 binder_thread 结构体来描述,这里就要取出一个 binder_thread 也就是从线程池里取出一个线程出来,当然我们并没有往线程池里注册线程,那么他就会把当前线程(也就是当前进程)申请一个 binder_thread 结构体,并存入线程池,再返回。


从线程池中得到一个线程后,就用这个线程来执行读写操作,完毕后,把 BWR 对象写回用户空间。



static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret;
	struct binder_proc *proc = filp->private_data;
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	
	// 这个 arg 就是用户空间的 BWR 对象的地址
	void __user *ubuf = (void __user *)arg;
	
	// 从线程池中取出线程
	mutex_lock(&binder_lock);
	thread = binder_get_thread(proc);
	
	...

	switch (cmd) {
	case BINDER_WRITE_READ: {
		
		struct binder_write_read bwr;
		...
		
		// 把用户空间的 BWR 对象读入内核空间
		if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
			ret = -EFAULT;
			goto err;
		}
		
		// 有数据需要写
		if (bwr.write_size > 0) {
			ret = binder_thread_write(proc, thread, 
				(void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
				
			if (ret < 0) {
				bwr.read_consumed = 0;
				if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
					ret = -EFAULT;
				goto err;
			}
		}
		
		// 需要读数据,暂时忽略
		if (bwr.read_size > 0) {
			...
		}
		
		...
		
		if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
			ret = -EFAULT;
			goto err;
		}
		break;
	}
	...
	default:
		ret = -EINVAL;
		goto err;
	}
	ret = 0;
err:
	...
	mutex_unlock(&binder_lock);
	...
	return ret;
}


这样再看上面这段代码,就轻松多了,最后他就会调用 binder_thread_write() 来执行写操作。


写操作会怎么被执行呢?简单啊,用户空间的输出缓冲区地址就保存在 BWR 对象的 write_buffer 成员里,只要从这个用户空间输出缓冲区中读出命令和 BTD 对象,然后根据命令(也就是 BC_TRANSACTION)来操作 BTD 对象。



int
binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
		    void __user *buffer, int size, signed long *consumed)
{
	uint32_t cmd;
	
	// 输出缓冲区的用户空间地址
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	while (ptr < end && thread->return_error == BR_OK) {
		
		// 读出命令
		if (get_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		
		...
		
		switch (cmd) {
 
		case BC_TRANSACTION:
		case BC_REPLY: {
			
			struct binder_transaction_data tr;
				
			// 读出 BTD 对象
			if (copy_from_user(&tr, ptr, sizeof(tr)))
				return -EFAULT;
			ptr += sizeof(tr);
			
			// 开始操作 BTD 对象
			binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
			break;
		}
		 
		...
		*consumed = ptr - buffer;
	}
	return 0;
}


可以看出的是,不管是 BC_TRANSACTION 还是 BC_REPLY,都是通过 binder_transaction() 函数来操作的,这两个命令的区别是第四个参数,如果是 BC_REPLY 就传入 1,否则就传入 0。


那么 binder_transaction() 又是怎么处理的呢?

我们知道,BC_TRANSACTION 和 BC_REPLY 两个命令都是通过这个函数来操作的,所以首先要判断的就是这是哪个命令,怎么判断呢?就看传入的最后一个参数,如果是1就是 REPLY,否则就是 TRANSACTION。


这里先介绍 TRANSACTION,REPLY 下面再说。对于 TRANSACTION 而言,接着要做的就是找到目标 Server,因为 Binder 要和目标 Server 进行通信,那么怎么找到呢?我们知道,Client 在引用一个实体对象时,他会有一个引用对象,所以寻找目标 Server 的过程就是寻找这个引用对象,然后引用对象的 node 成员就保存了目标 Server 的实体对象,那么怎么寻找呢?这就要看我们的 BTD 对象了。


在之前已经从用户空间读出了 BTD 对象,而 BTD 对象的 target.handle 这个成员就保存了引用对象的句柄。这个句柄值分为两种:0和其他。如果他是0,就说明目标 Server 是 SM,那么他的实体对象是固定的,他的变量名为 binder_context_mgr_node,为什么要这么做?这是因为,一个引用对象的获取是要通过 SM 来连接 Server 后才能得到的,而 SM 本身就没有办法去获取他的引用对象,所以干脆就直接获取他的实体对象,反正就这一个还是固定的,所以没有任何问题。如果他不是 0,那么就从引用对象哈希表中取出引用对象,再取出实体对象。


那么,引用对象又是怎么获取的呢?这个在本文不会说到。


得到 Server 的实体对象后,就能再得到 Server 的 binder_proc 对象,然后就可以给他发送 BR_TRANSACTION 返回协议来要求他返回一个值。那么,怎么发送呢?很简单,从他的线程池里挑出一个线程来,然后让这个线程执行即可。这里涉及到线程优化的问题,本文不涉及到这方面,你只需要知道他会选出一个合适的线程即可。


Binder 线程都有两个链表节点,一个 todo,另一个 wait。wait 用来挂入一个等待队列;todo 是一个链表头,保存了线程待处理的工作项。当我们把工作项插入 todo 的同时,会通过他的 wait 节点唤醒线程。


由上可知,我们找到线程后,就要把 BTD 对象再包装成工作项对象,然后把他插入到目标线程的 todo 队列里,最后唤醒他。

既然把数据发给了 Server,接下来就是要通知 Client 了,所以需要再弄出一个工作项,并把他插入到源线程的 todo 队列里,但不需要唤醒,因为现在正在执行内核代码的就是源进程。


由于发给目标线程的工作项和源线程的工作项不同,后者只需要起到通知作用,用的是 binder_work 结构体;而前者还需要携带数据,所以他是用一个 binder_transaction 结构体包含了 binder_work 结构体,最后把 binder_work 成员插入 todo 队列,最后再通过 binder_work 成员取回 binder_transaction 结构体。


经过一番解释,代码也就容易看懂了,下面是代码:



static void
binder_transaction(struct binder_proc *proc, struct binder_thread *thread,
	struct binder_transaction_data *tr, int reply)
{
	struct binder_transaction *t;
	struct binder_work *tcomplete;
	size_t *offp, *off_end;
	struct binder_proc *target_proc;
	struct binder_thread *target_thread = NULL;
	struct binder_node *target_node = NULL;
	struct list_head *target_list;
	wait_queue_head_t *target_wait;
	struct binder_transaction *in_reply_to = NULL;
	struct binder_transaction_log_entry *e;
	uint32_t return_error;

	...

	if (reply) {
		// BC_REPLY
		...
	} else {
		// BC_TRANSACTION
		
		if (tr->target.handle) {
			// 引用对象的句柄
			
			// 得到引用对象
			struct binder_ref *ref;
			ref = binder_get_ref(proc, tr->target.handle);
			
			if (ref == NULL) {
				// 没有就说明还没有连接 Server 
				binder_user_error("binder: %d:%d got "
					"transaction to invalid handle\n",
					proc->pid, thread->pid);
				return_error = BR_FAILED_REPLY;
				goto err_invalid_target_handle;
			}
			
			// 取出实体对象
			target_node = ref->node;
		} else {
			// 引用对象句柄 = 0,说明目标 Server 是 SM
			
			// 固定的实体对象
			target_node = binder_context_mgr_node;
			if (target_node == NULL) {
				return_error = BR_DEAD_REPLY;
				goto err_no_context_mgr_node;
			}
		}
		
		...
		
		// 得到 binder_proc 对象
		target_proc = target_node->proc;
		if (target_proc == NULL) {
			return_error = BR_DEAD_REPLY;
			goto err_dead_binder;
		}
		
		// 找到合适的线程
		if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
			...
		}
	}
	
	// 得到处理线程后,得到其所在的 todo 队列和 wait 队列
	// 一会,我们就会把他从 wait 队列中唤醒,并把 BR_TRANSACTION 包装成工作项插入 todo
	if (target_thread) {
		e->to_thread = target_thread->pid;
		target_list = &target_thread->todo;
		target_wait = &target_thread->wait;
	} else {
		// 没有线程,那就直接扔给对方进程
		target_list = &target_proc->todo;
		target_wait = &target_proc->wait;
	}
	
	e->to_proc = target_proc->pid;
 
	// 申请工作项对象(binder_transaction)。
	// 他会被发送给目标线程(Server)
	t = kzalloc(sizeof(*t), GFP_KERNEL);

	// 申请工作项对象(binder_work)
	// 他会被发送给源线程(Client)
	tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
	
	...
 
	// 初始化特殊工作项
	if (!reply && !(tr->flags & TF_ONE_WAY))
		t->from = thread;
	else
		t->from = NULL;
	t->sender_euid = proc->tsk->cred->euid;
	t->to_proc = target_proc;
	t->to_thread = target_thread;
	t->code = tr->code;
	t->flags = tr->flags;
	t->priority = task_nice(current);
	
	// 为特殊工作项申请缓冲区
	t->buffer = binder_alloc_buf(target_proc, tr->data_size,
		tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
 
	...
	t->buffer->transaction = t;
	t->buffer->target_node = target_node;
	...
	
	offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
	
	// 根据 BTD 对象把保存在 Parcel 对象里的数据复制到内核空间
	if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
		...
	}
	if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
		...
	}
	
	// 遍历通信数据里的 Binder 对象,执行相应操作,比如创建相应的实体对象和引用对象
	off_end = (void *)offp + tr->offsets_size;
	for (; offp < off_end; offp++) {
		struct flat_binder_object *fp;
		...
		fp = (struct flat_binder_object *)(t->buffer->data + *offp);
		switch (fp->type) {
		case BINDER_TYPE_BINDER:
		case BINDER_TYPE_WEAK_BINDER: ...break;
		case BINDER_TYPE_HANDLE:
		case BINDER_TYPE_WEAK_HANDLE: ...break;
		case BINDER_TYPE_FD:...break;

		default:
			...
		}
	}
	
	...
	
	// 把工作项插入目标线程的 todo 队列
	t->work.type = BINDER_WORK_TRANSACTION;
	list_add_tail(&t->work.entry, target_list);
	
	// 把工作项插入源线程的 todo 队列
	tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
	list_add_tail(&tcomplete->entry, &thread->todo);
	
	// 唤醒目标线程
	if (target_wait)
		wake_up_interruptible(target_wait);
	
	return;

	...
}


那么,Server 的目标线程被唤醒后又会怎么处理呢?源线程(也就是现在正在执行代码的这个)又会怎么处理呢?

Server 的目标线程是怎么的我们先不管,我们先看看源线程(当前线程)是怎么处理这个工作项的。


当前线程调用完 binder_transaction() 后,回到 binder_ioctl(),然后再来看看他接下来会执行什么:



// 有数据需要写
if (bwr.write_size > 0) {
	
	...
}

// 需要读数据,暂时忽略
if (bwr.read_size > 0) {
	ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
	if (!list_empty(&proc->todo))
		wake_up_interruptible(&proc->wait);
	if (ret < 0) {
		if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
			ret = -EFAULT;
		goto err;
	}
}


可以看出,他接下来要做的就是判断进程是否还要继续读数据,那么这个 read_size 的值是多少呢?前文我们说过【在只允许接收返回命令(默认值为true)和输入缓冲区为空的情况下,他也一定为空,在本情景中,他一定会读】,他一定会读表现为 read_size = 输入缓冲区大小,所以这里的意思是,只要输入缓冲区还有空间,那我就要进入了。


那么这里又干了些什么呢?其实很简单,在 binder_thread_read() 里,他从 todo 队列里取出之前放进去的工作项,由于这个工作项的代码是 BINDER_WORK_TRANSACTION_COMPLETE,所以他会把 BR_TRANSACTION_COMPLETE 命令写到进程的输入缓冲区内。


命令被写到输入缓冲区后,binder_ioctl() 也就结束了,进程返回了用户空间,然后会开始处理 BR_TRANSACTION_COMPLETE 命令。

还是先来看看 binder_thread_read() 是怎么做这些工作的吧:


static int
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
	void  __user *buffer, int size, signed long *consumed, int non_block)
{
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	int ret = 0;
	int wait_for_proc_work;

	...

	while (1) {
		uint32_t cmd;
		struct binder_transaction_data tr;
		struct binder_work *w;
		struct binder_transaction *t = NULL;
		
		// 从 todo 队列里取出工作项
		if (!list_empty(&thread->todo))
			w = list_first_entry(&thread->todo, struct binder_work, entry);
		else if (!list_empty(&proc->todo) && wait_for_proc_work)
			w = list_first_entry(&proc->todo, struct binder_work, entry);
		else {
			...
			break;
		}

		if (end - ptr < sizeof(tr) + 4)
			break;

		switch (w->type) {
		case BINDER_WORK_TRANSACTION: {...} break;
		case BINDER_WORK_TRANSACTION_COMPLETE: {
			
			// 把 BR_TRANSACTION_COMPLETE 命令写到用户空间
			cmd = BR_TRANSACTION_COMPLETE;
			if (put_user(cmd, (uint32_t __user *)ptr))
				return -EFAULT;
			ptr += sizeof(uint32_t);

			...
			
			// 释放工作项
			list_del(&w->entry);
			kfree(w);
			
			...
			
		} break;
		case BINDER_WORK_NODE: ...break;
		case BINDER_WORK_DEAD_BINDER:
		case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
		case BINDER_WORK_CLEAR_DEATH_NOTIFICATION:
		break;
	}

done:

	...
	return 0;
}


好了,现在我们要返回用户空间。

那么,用户空间怎么处理 BR_TRANSACTION_COMPLETE 呢?我想这一点,在我们之前的文中已经涉及过了,不过这里还是需要再讲一遍。



status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
		
	// 之前已发送了请求并接受了第一个返回值
        if ((err=talkWithDriver()) < NO_ERROR) break;
		
		...
        
	// 从输入缓冲区内读出了 BR_TRANSACTION_COMPLETE
        cmd = mIn.readInt32();
        
        ...
		
        switch (cmd) {
			 
        case BR_TRANSACTION_COMPLETE:
	    // 什么意思呢?就是如果你还想要返回值,就跳到循环开头继续等
	    // 如果你不想要返回值,就直接结束函数。
            if (!reply && !acquireResult) goto finish;
            break;
        
		...
		 
        case BR_REPLY:
            ....
            goto finish;

        default:
            ...
            break;
        }
    }

finish:
    ...
    return err;
}



你看吧?他啥都不干,就是又跳到循环开头调用 talkWithDriver() 重新来了一回。

我们知道,talkWithDriver() 是要把一个 BWR 写到 Binder 驱动里的,那么这个时候我们还需要写什么命令吗?当然不需要了!我们只需要读就可以了!然而又因为读不到工作项,所以他就会在这里被阻塞,直到 Server 把数据返回给 Binder,Binder 再把数据交给 Client。


第四步、对端线程被唤醒后,创建一个 BTD 对象,并读出 binder_transtion 对象,把 BT 对象中的数据信息保存到 BTD 对象中,最后把 BTD 对象写到用户空间的输入缓冲区,最终在用户空间解析数据并进行处理,具体怎么处理,不同的 Service 都不一样。总之,一次数据通信就完成了。


那Server 是怎么处理的呢?

首先我们可以知道的是,每一个 Binder 对象都有一个线程池,每一个线程池里的线程最后都会调用 binder_thread_read() 并因为 todo 队列里没有工作项而睡眠,而在第三步我们已经唤醒了一个空闲线程,所以他会继续执行并出去 todo 并进行处理。


binder_thread_read() 的代码在第三步的最后已经贴过了,基本上就是那么一回事,根据第三步又可得知,Binder 发过来的工作项的代码是 BINDER_WORK_TRANSACTION,那么他会怎么处理呢?


在这里我们要注意一点,现在正在执行的是 Server 的线程,也就是目标线程,并且现在 Server 线程执行的依然是 Binder 驱动的代码,所以说这里要做的低一点就是先创建一个 BTD 对象,然后把这个 BTD 对象


很简单啊,首先,我们总得返回一个值吧?我们知道 BTD 对象用来进程间通信,所以我们的返回值也要通过这个对象来传递,所以首先就得有一个 BTD 对象,并且把 BR_TRANSACTION 命令和 BTD 对象写到 Server 的输入缓冲区内,写完后再返回用户空间读出命令和 BTD 对象进行处理即可。


我们知道,描述 Server 行为的命令就保存在 BTD 对象的 code 成员中,而本文的示例命令是 ADD_SERVICE_TRANSACTION,这样,BR_TRANSACTION 命令和包含 ADD_SERVICE_TRANSACTION 命令的 BTD 对象就被写到了输入缓冲区内,最后由 Server 在用户空间进行处理。



static int
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
	void  __user *buffer, int size, signed long *consumed, int non_block)
{
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	int ret = 0;
	int wait_for_proc_work;

	...

	while (1) {
		uint32_t cmd;
		
		// 这个 BTD 对象会写回到用户空间,描述 Client 传来的值
		struct binder_transaction_data tr;
		
		struct binder_work *w;
		struct binder_transaction *t = NULL;
		
		// 从 todo 队列里取出工作项
		if (!list_empty(&thread->todo))
			w = list_first_entry(&thread->todo, struct binder_work, entry);
		else if (!list_empty(&proc->todo) && wait_for_proc_work)
			w = list_first_entry(&proc->todo, struct binder_work, entry);
		else {
			...
			break;
		}

		if (end - ptr < sizeof(tr) + 4)
			break;

		switch (w->type) {
			
			case BINDER_WORK_TRANSACTION: {
				// 取出 binder_transaction 对象
				t = container_of(w, struct binder_transaction, work);
			} break;
			
			... 
		}
		
		
		if (!t)
			continue;

		BUG_ON(t->buffer == NULL);
		
		if (t->buffer->target_node) {
			// 就是当前Server的Binder节点,初始化BTD对象
			
			struct binder_node *target_node = t->buffer->target_node;
			tr.target.ptr = target_node->ptr;
			
			...
			
			cmd = BR_TRANSACTION;
		} else {
			tr.target.ptr = NULL;
			tr.cookie = NULL;
			cmd = BR_REPLY;
		}
		
		// 在本文中,这个 code 就是 ADD_SERVICE_TRANSACTION
		// 这个 code 是关键,他告诉 Server 应该怎么做
		tr.code = t->code;
		tr.flags = t->flags;
		tr.sender_euid = t->sender_euid;

		...
		
		// 复制数据
		tr.data_size = t->buffer->data_size;
		tr.offsets_size = t->buffer->offsets_size;
		tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;
		tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));
		
		// 把 BR_TRANSACTION 命令写到输入缓冲区
		if (put_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		
		// 把 BTD 对象写到输入缓冲区
		ptr += sizeof(uint32_t);
		if (copy_to_user(ptr, &tr, sizeof(tr)))
			return -EFAULT;
		ptr += sizeof(tr);
		
		// 删除工作项
		list_del(&t->work.entry);
		
		...
		break;		
	}

done:

	...
	return 0;
}



至于 Server 会怎么处理,那不是本文要涉及的,毕竟不同的 Server 有不同的命令和方法,我们应该关注的是他怎么返回一个返回值。

返回操作是 Server 在用户空间操作的,而每个 Server 的操作方法都不一样,所以这里只介绍内核层。


第三、四次数据通信开始(需要返回值),基本步骤和前两次是一样的。

第五步、对端在用户空间处理完数据后,和第一步一样,也构造一个保存了返回数据的 BWR 及相关对象调用 ioctl 来通知 Binder 驱动,不同的是 Binder 命令从 BC_TRANSACTION 变成了 BC_REPLY。


和之前一样,先得到 Client 的实体对象,把数据封装成 BT 对象,交给线程池中的一个线程。Client 线程得到该 BT 对象后,创建一个 BTD 对象来关联 BT 对象内的数据,并把他和一个 BR_REPLY 命令写回到用户空间,最终用户空间处理。数据通信彻底完成。



用户层处理完数据后,就像 Client 发起通信请求一样,也会调用 ioctl() 来发送一个 BINDER_WRITE_READ 命令和 BWR 对象来把数据发给 Binder,最后也都会调用到 binder_thread_write() 来执行返回操作。


不同的是,Client 发起请求时,其命令为 BC_TRANSACTION,而 Server 返回时用的命令则是 BC_REPLY,然后就像本文开头的 Client 一样,随着 binder_ioctl -> binder_thread_write -> binder_transaction 来处理这个命令。


实际上 binder_transaction 的代码在前面已经解释地差不多了,但这里还是要得再说一遍。实际上 BC_REPLY 和 BC_TRANSACTION 的大多数逻辑都是一样的,唯一不同的地方是获得对方的实体对象的方式不同。后者先得到引用对象再得到实体对象,前者则在自己的事务堆栈中拿出最上面那个,就是刚处理的事物,然后再得到 Client 的实体对象。



static void
binder_transaction(struct binder_proc *proc, struct binder_thread *thread,
	struct binder_transaction_data *tr, int reply)
{
	struct binder_transaction *t;
	struct binder_work *tcomplete;
	size_t *offp, *off_end;
	struct binder_proc *target_proc;
	struct binder_thread *target_thread = NULL;
	struct binder_node *target_node = NULL;
	struct list_head *target_list;
	wait_queue_head_t *target_wait;
	struct binder_transaction *in_reply_to = NULL;
	struct binder_transaction_log_entry *e;
	uint32_t return_error;

	...

	if (reply) {
		// BC_REPLY
		
		// 取出事务
		in_reply_to = thread->transaction_stack;
		...
		binder_set_nice(in_reply_to->saved_priority);
		...
		// 指向下一个事务
		thread->transaction_stack = in_reply_to->to_parent;
		
		// 事务的发送者就是 Client,回复他就要把 Client 作为目标
		target_thread = in_reply_to->from;
		if (target_thread == NULL) {
			return_error = BR_DEAD_REPLY;
			goto err_dead_binder;
		}
		...
		// 得到 Client 的 binder_proc
		target_proc = target_thread->proc;
	} else {
		// BC_TRANSACTION
	}
	
	// 得到处理线程后,得到其所在的 todo 队列和 wait 队列
	// 一会,我们就会把他从 wait 队列中唤醒,并把 BR_TRANSACTION 包装成工作项插入 todo
	if (target_thread) {
		e->to_thread = target_thread->pid;
		target_list = &target_thread->todo;
		target_wait = &target_thread->wait;
	} else {
		// 没有线程,那就直接扔给对方进程
		target_list = &target_proc->todo;
		target_wait = &target_proc->wait;
	}
	
	e->to_proc = target_proc->pid;
 
	// 申请两个工作项,一个给目标线程,另一个给源线程
	t = kzalloc(sizeof(*t), GFP_KERNEL);
	tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
	 
	...
	
	offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
	
	// 根据 BTD 对象把保存在 Parcel 对象里的数据复制到内核空间
	if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
		...
	}
	if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
		...
	}
	
	...
	
	// 把工作项插入目标线程的 todo 队列
	t->work.type = BINDER_WORK_TRANSACTION;
	list_add_tail(&t->work.entry, target_list);
	
	// 把工作项插入源线程的 todo 队列
	tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
	list_add_tail(&tcomplete->entry, &thread->todo);
	
	// 唤醒目标线程
	if (target_wait)
		wake_up_interruptible(target_wait);
	
	return;

	...
}


然后 Server 线程会再回到用户空间,又在 talkWithDriver() 中再次进入内核空间,并处理 BINDER_WORK_TRANSACTION_COMPLETE 工作项,处理完后,就继续睡眠等待工作项的来临


Client 线程在发送 BC_TRANSATION 后,和 Server 一样会在 binder_thread_read() 中等待,然后又被 Server 的 BC_REPLY 唤醒,醒来后就开始处理工作项。


这个工作项当然包含了返回值,其处理方式和 Server 处理 BC_TRANSATION 是一样的,先从 todo 队列中取出工作项,然后再通过这个工作项取出 binder_transaction 结构体并用来创建一个 BTD 对象,再把 BR_REPLY 和 BTD 写到输入缓冲区,最后返回用户空间,在 waitForResponse() 里进行处理。


binder_thread_read() 代码如下:



static int
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
	void  __user *buffer, int size, signed long *consumed, int non_block)
{
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	int ret = 0;
	int wait_for_proc_work;

	...

	while (1) {
		uint32_t cmd;
		
		// 这个 BTD 对象会写回到用户空间,描述 Client 传来的值
		struct binder_transaction_data tr;
		
		struct binder_work *w;
		struct binder_transaction *t = NULL;
		
		// 从 todo 队列里取出工作项
		if (!list_empty(&thread->todo))
			w = list_first_entry(&thread->todo, struct binder_work, entry);
		else if (!list_empty(&proc->todo) && wait_for_proc_work)
			w = list_first_entry(&proc->todo, struct binder_work, entry);
		else {
			...
			break;
		}

		if (end - ptr < sizeof(tr) + 4)
			break;

		switch (w->type) {
			
			case BINDER_WORK_TRANSACTION: {
				// 取出 binder_transaction 对象
				t = container_of(w, struct binder_transaction, work);
			} break;
			
			... 
		}
		
		
		if (!t)
			continue;

		BUG_ON(t->buffer == NULL);
		
		if (t->buffer->target_node) {
			
			...
		} else {
			// REPLY 时,target_node = NULL
			
			tr.target.ptr = NULL;
			tr.cookie = NULL;
			cmd = BR_REPLY;
		}
		
		// 在本文中,这个 code 就是 ADD_SERVICE_TRANSACTION
		// 这个 code 是关键,他告诉 Server 应该怎么做
		tr.code = t->code;
		tr.flags = t->flags;
		tr.sender_euid = t->sender_euid;

		...
		
		// 复制数据
		tr.data_size = t->buffer->data_size;
		tr.offsets_size = t->buffer->offsets_size;
		tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;
		tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));
		
		// 把 BR_REPLY 命令写到输入缓冲区
		if (put_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		
		// 把 BTD 对象写到输入缓冲区
		ptr += sizeof(uint32_t);
		if (copy_to_user(ptr, &tr, sizeof(tr)))
			return -EFAULT;
		ptr += sizeof(tr);
		
		// 删除工作项
		list_del(&t->work.entry);
		
		...
		break;		
	}

done:

	...
	return 0;
}


最后返回用户空间怎么处理呢?我们知道,waitForResponse() 是一个 while(1) 循环,只要通信未结束(接收到 BR_REPLY),他就会一直循环下去。现在通信结束了,所以这个循环也就可以退出了,通信也就结束了。



status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
		
		// 第二个返回值
        if ((err=talkWithDriver()) < NO_ERROR) break;
		
		...
        
		// 从输入缓冲区内读出了 BR_REPLY
        cmd = mIn.readInt32();
        
        ...
		
        switch (cmd) {
			 
        case BR_TRANSACTION_COMPLETE: ...break;
        
		...
		 
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
               ...
            }
			
			// 通信完毕
            goto finish;

        default: ... break;
        }
    }

finish:
    ...
    return err;
}











发表评论:

Copyright ©2015-2016 freehui All rights reserved