Binder框架 -- 用户空间和驱动的交互

2017-02-20  本文已影响181人  赤兔欢

Binder框架 -- 用户空间和驱动的交互

MediaPlayerServic 启动的过程中,代码可以简化为:

  1. sp<IServiceManager> sm = new BpServiceManager(BpBinder(0));
  2. sm->addService(String16("media.player"), new MediaPlayerService());

针对上面的两步,看下用户态程序和内核驱动是怎么交互的。

Parcel

在用户程序和内核的交互中,Android提供了Parcel 类帮助我们对需要传递的数据进行封装,其实就是数据的序列化,按照类型和先后顺序写入到内存中。Parcel 内部的存储区域主要有两个,mData和mObjects, mData 存储基本数据类型,mObjects存储Binder 数据类型。Parcel 提供了针对各种数据写入和读取的操作函数。这两块区域都是使用 malloc 分配出来。

uint8_t*            mData;
size_t              mDataSize;
size_t              mDataCapacity;
mutable size_t      mDataPos;

binder_size_t*      mObjects;
size_t              mObjectsSize;
size_t              mObjectsCapacity;
mutable size_t      mNextObjectHint;

flat_binder_object

在Parcel 的序列化中,Binder 对象使用flat_binder_object 结构体保存。同时提供了flatten_binder 和unflatten_binder 函数用于序列化和反序列化。

struct flat_binder_object {
  __u32 type;
  __u32 flags;
  union {
    binder_uintptr_t binder;
    __u32 handle;
  };
  binder_uintptr_t cookie;
};
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const wp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        sp<IBinder> real = binder.promote();
        if (real != NULL) {
            IBinder *local = real->localBinder();
            if (!local) {
                BpBinder *proxy = real->remoteBinder();
                if (proxy == NULL) {
                    ALOGE("null proxy");
                }
                const int32_t handle = proxy ? proxy->handle() : 0;
                obj.type = BINDER_TYPE_WEAK_HANDLE;
                obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
                obj.handle = handle;
                obj.cookie = 0;
            } else {
                obj.type = BINDER_TYPE_WEAK_BINDER;
                obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs());
                obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get());
            }
            return finish_flatten_binder(real, obj, out);
        }

        // XXX How to deal?  In order to flatten the given binder,
        // we need to probe it for information, which requires a primary
        // reference...  but we don't have one.
        //
        // The OpenBinder implementation uses a dynamic_cast<> here,
        // but we can't do that with the different reference counting
        // implementation we are using.
        ALOGE("Unable to unflatten Binder weak reference!");
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
        return finish_flatten_binder(NULL, obj, out);

    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
        return finish_flatten_binder(NULL, obj, out);
    }
}

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, wp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_WEAK_BINDER:
                if (flat->binder != 0) {
                    out->set_object_and_refs(
                        reinterpret_cast<IBinder*>(flat->cookie),
                        reinterpret_cast<RefBase::weakref_type*>(flat->binder));
                } else {
                    *out = NULL;
                }
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
            case BINDER_TYPE_WEAK_HANDLE:
                *out = proc->getWeakProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->unsafe_get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

new BpServiceManager(BpBinder(0))

BpServiceManager 的构造函数以BpBinder 为参数,在BpBinder 的onFirstRef 中完成了驱动强引用计数加1,构造函数中完成了弱引用计数的+1,这个都比较简单。

BpBinder::BpBinder(int32_t handle)
    : mHandle(handle)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);

    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    IPCThreadState::self()->incWeakHandle(handle);
}

void BpBinder::onFirstRef()
{
    ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
    IPCThreadState* ipc = IPCThreadState::self();
    if (ipc) ipc->incStrongHandle(mHandle);
}
void IPCThreadState::incStrongHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle);
    mOut.writeInt32(BC_ACQUIRE);
    mOut.writeInt32(handle);
}

addService

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

localBinder 和remoteBinder

android 提供了三个API用于方位本进程的binder, 根据服务端和代理端的不同,这三个API可以做不同的区分,实现了多态。 这是三个虚函数函数定义在IBinder中,BBinder 和BpBinder 根据需要,BBinder 实现了localBinder, BpBinder 实现了remoteBinder,函数的名字与其所代表的含义也恰如其分,BBinder 在服务端,所以是localBinder, BpBinde 在代理端, 对服务端来说,是remote。

function IBinder BBinder BpBinder
queryLocalInterface NULL this
localBinder NULL this
remoteBinder NULL this

writeStrongBinder

Parcel 提供的 writeStrongBinder 是对binder类型的数据进行序列化操作,这一点从内部调用的函数flatten_binder名字上也可以看出来。序列化的时候首先判断是代理端还是服务端,用的是localBinder 函数进行判断。为空,表明是代理端,用remoteBinder 获取BpBinder.不为空则为服务端,这里不为空。序列化以后的Binder 存储在 flat_binder_object结构体中。注意flat_binder_object 的赋值。BBinder 序列化:

  1. type = BINDER_TYPE_BINDER
  2. binder = reinterpret_cast<uintptr_t>(local->getWeakRefs()); // 弱引用对象
  3. cookie = reinterpret_cast<uintptr_t>(local); //this 对象,BBinder本身
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            ......
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
        ......
    }

    return finish_flatten_binder(binder, obj, out);
}

inline static status_t finish_flatten_binder(
    const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}

status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
    const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
    const bool enoughObjects = mObjectsSize < mObjectsCapacity;
    if (enoughData && enoughObjects) {
restart_write:
        // 通过类型转换,对Parcle 的对象存储区域赋值保存, 好隐晦
        *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;  

        // remember if it's a file descriptor
        if (val.type == BINDER_TYPE_FD) {
            if (!mAllowFds) {
                return FDS_NOT_ALLOWED;
            }
            mHasFds = mFdsKnown = true;
        }

        // Need to write meta-data?
        // nullMetaDAta == false  val.binder != 0, 参考上面的序列化赋值过程
        if (nullMetaData || val.binder != 0) {
            mObjects[mObjectsSize] = mDataPos;
            // 强弱引用计数加减
            acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
            mObjectsSize++;
        }

        return finishWrite(sizeof(flat_binder_object));
    }

    // 存储区域的内存分配过程
    if (!enoughData) {
        const status_t err = growData(sizeof(val));
        if (err != NO_ERROR) return err;
    }
    if (!enoughObjects) {
        size_t newSize = ((mObjectsSize+2)*3)/2;
        if (newSize < mObjectsSize) return NO_MEMORY;   // overflow
        binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
        if (objects == NULL) return NO_MEMORY;
        mObjects = objects;
        mObjectsCapacity = newSize;
    }

    goto restart_write;
}

transact 过程

Parcel 数据的序列化完成以后,就需要把Parcel 数据传递给服务端,transact 出马了。具体的条用流程根据前面的分析是:BpBinder#transact => IPCThreadState#transact. BpBinder 的调用传递了三个参数,第一个是cmd, 第二个是传入的Parcel, 第三个保存返回值。

status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

BpBinder::transact

BpBinder::transact 中参数增加了一个mHandler, 在BpServiceManager 的构造中,mHandler 被赋值为0。在这里代表要传递的服务。后边会讨论这个hander 的来源和代表的意义。

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::transact

  1. writeTransactionData
  2. waitForResponse
  3. talkWithDriver
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 (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }
    
    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }
    
    return err;
}
writeTransactionData

在IPCThreadState::transact 中最重要的函数调用是writeTransactionData,在这里 传入的Parcel 数据 以及handle code 被封装为能和kernel 识别的数据类型:struct binder_transaction_data。数据类型被标记为 BC_TRANSACTION。 然后也被写入IPCThreadState 的Parcel mOut的内存区域中。这时候已经有三个Parcel ,两个参数data 和 reply, 还有一个IPCThreadState 的内部成员mOut, mOut 保存了binder_transaction_data.

struct binder_transaction_data {
  union {
    __u32 handle;
    binder_uintptr_t ptr;
  } target;               // 传递给那个Binder 
  binder_uintptr_t cookie;
  __u32 code;             // 服务内部的命令字
  __u32 flags;
  pid_t sender_pid;
  uid_t sender_euid;
  binder_size_t data_size;       // 数据区域的大小
  binder_size_t offsets_size;    // 对象区域的大小, 名字好怪 是不是objs_size 更合适
  union {
    struct {
      binder_uintptr_t buffer;     // 数据区域地址
      binder_uintptr_t offsets;    // 对象区域的地址 变量命名值得商议
    } ptr;
    __u8 buf[8];
  } data;                // 描述的传递数据
};

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; /* Don't pass uninitialized stack data to a remote process */
    tr.target.handle = handle;   // 不适用ptr 指针, 使用handle 查找binder.
    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); // 对象区域的大小,名字好怪 是不是objs_size 更合适
        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;
}
waitForResponse

waitForResponse 中调用talkWithDriver

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

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        
        ......
        
     }
}
talkWithDriver

talkWithDriver 要和驱动通信了,在这里所有的数据又被一个新的结构体标记了,struct binder_write_read.为什么是标记而不是取代,binder_write_read 仅仅描述了IPCThreadState 的两个Parcel 大小,已经内部内存的地址。ioctl 调用BINDER_WRITE_READ 命令进入内核, 传递的参数是 struct binder_write_read。mIn 用来存放从内核的返回数据。

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }
    
    binder_write_read bwr;
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
#if defined(__ANDROID__)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
    } while (err == -EINTR);

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
     
        return NO_ERROR;
    }
    
    return err;
}

数据的转移和封装

在transact 过程中,需要传递的数据由最初的基本数据类型和Binder 类型被层层封装。最后有struct binder_write_read 结构体描述,struct binder_write_read 作为ioctl 的参数传递到内核中。下面的图展示了数据的封装过程。

binder_ioctl

binder_ioctl_write_read

进入内核以后,binder_ioctl 根据cmd 选择binder_ioctl_write_read,binder_ioctl_write_read 调用copy_from_user 从用户空间copy binder_write_read 结构体,第一次copy。 和用户空间封装数据的顺序相反,然后根据写数据的大小和读数据空间的大小进行读写操作。

static int binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg,
                struct binder_thread *thread)
{
    int ret = 0;
    struct binder_proc *proc = filp->private_data;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;

    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }

    if (bwr.write_size > 0) {
        ret = binder_thread_write(proc, thread,
                      bwr.write_buffer,
                      bwr.write_size,
                      &bwr.write_consumed);
        trace_binder_write_done(ret);
        if (ret < 0) {
            bwr.read_consumed = 0;
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }
    
    if (bwr.read_size > 0) {
        ret = binder_thread_read(proc, thread, bwr.read_buffer,
                     bwr.read_size,
                     &bwr.read_consumed,
                     filp->f_flags & O_NONBLOCK);
        trace_binder_read_done(ret);
        if (!list_empty(&proc->todo))
            wake_up_interruptible(&proc->wait);
        if (ret < 0) {
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }
    
    ......
}

binder_thread_write

get_user 第二次copy, 读出binder_transaction_data 数据的命令字,这里为BC_TRANSACTION,仅仅一个int 大小。第三次copy_from_user copy 读出binder_transaction_data 结构体

static int binder_thread_write(struct binder_proc *proc,
            struct binder_thread *thread,
            binder_uintptr_t binder_buffer, size_t size,
            binder_size_t *consumed)
{
    uint32_t cmd;
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    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);
        trace_binder_command(cmd);
        if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
            binder_stats.bc[_IOC_NR(cmd)]++;
            proc->stats.bc[_IOC_NR(cmd)]++;
            thread->stats.bc[_IOC_NR(cmd)]++;
        }
        switch (cmd) {
        ......
        
        case BC_TRANSACTION:
        case BC_REPLY: {
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
            break;
        }
       ......
       
        default:
            return -EINVAL;
        }
        *consumed = ptr - buffer;
    }
    return 0;
}

binder_transaction

  1. binder_transaction 中首先根据handler 查找目标的binder的target_node, handler == 0 .
  2. 构造binder_transaction 结构体,这个结构体仅仅驱动内部使用,用来在不同的进程中传递数据。调用 binder_alloc_buf,为数据复制准备空间。binder_alloc_buf 除了分配空间,还需要把内核空间地址计算出 target_node 所代表的进程用户空间地址。
  3. copy_from_user, 复制 数据域数据。第四次copy
  4. copy_from_user, 复制 对象域数据。第五次copy
  5. while 循环,将对象域的所有binder 数据解析出来, 还是用 表示
  6. 前边 writeStrongBinder 的时候binder type == BINDER_TYPE_BINDER。
  7. binder_get_node 获取 自身的binder_node 节点,这个时候还没有,所以new一个。
  8. binder_get_ref_for_node, 找到对target_node 节点的引用 binder_ref * ref
  9. 对解析出来的结果处理,我们传递进来的IBinder 为MediaPlayerService, 实质上是一个BnMediaPlayerService, 那 BnMediaPlayerService 的hanler, 在这里被赋值 fp->handle = ref->desc ref 为binder_get_ref_for_node 对target_node 的引用。这个值得大小及意义后边分析。在这里,找到了Binder 对象的handle 的出生之处。
  10. 将binder_transaction 加入到target_node 所代表的进程的target_list 队列中,实际上是todo 队列。
  11. 唤醒target_node 进程。到这里完成了进程的切换。
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;
    binder_size_t *offp, *off_end;
    binder_size_t off_min;
    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) {
        
    } else {
        if (tr->target.handle) {      // target.handle= 0, 走else 分支
        
        } else {
            target_node = binder_context_mgr_node;
            if (target_node == NULL) {
                return_error = BR_DEAD_REPLY;
                goto err_no_context_mgr_node;
            }
        }
        target_proc = target_node->proc;
    
        if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
            struct binder_transaction *tmp;

            tmp = thread->transaction_stack;
            if (tmp->to_thread != thread) {
                    proc->pid, thread->pid, tmp->debug_id,
                    tmp->to_proc ? tmp->to_proc->pid : 0,
                    tmp->to_thread ?
                    tmp->to_thread->pid : 0);
                return_error = BR_FAILED_REPLY;
                goto err_bad_call_stack;
            }
            while (tmp) {
                if (tmp->from && tmp->from->proc == target_proc)
                    target_thread = tmp->from;
                tmp = tmp->from_parent;
            }
        }
    }
    
    if (target_thread) {
        e->to_thread = target_thread->pid;
        target_list = &target_thread->todo;
        target_wait = &target_thread->wait;
    } else {   // target_thread 为空
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }

    /* TODO: reuse incoming transaction for reply */
    // t 为binder_transaction, 构造 binder_transaction结构体
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    if (t == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_alloc_t_failed;
    }
    binder_stats_created(BINDER_STAT_TRANSACTION);

    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    if (tcomplete == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_alloc_tcomplete_failed;
    }
    binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);

    t->debug_id = ++binder_last_id;  // 计数,全局变量

    if (!reply && !(tr->flags & TF_ONE_WAY))
        t->from = thread;
    else
        t->from = NULL;
    t->sender_euid = task_euid(proc->tsk);
    t->to_proc = target_proc;
    t->to_thread = target_thread;
    t->code = tr->code;
    t->flags = tr->flags;
    t->priority = task_nice(current);

    trace_binder_transaction(reply, t, target_node);

    t->buffer = binder_alloc_buf(target_proc, tr->data_size,
        tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    if (t->buffer == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_binder_alloc_buf_failed;
    }
    t->buffer->allow_user_free = 0;
    t->buffer->debug_id = t->debug_id;
    t->buffer->transaction = t;
    t->buffer->target_node = target_node;
    trace_binder_transaction_alloc_buf(t->buffer);
    if (target_node)
        binder_inc_node(target_node, 1, 0, NULL);

    offp = (binder_size_t *)(t->buffer->data +
                 ALIGN(tr->data_size, sizeof(void *)));

    if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
               tr->data.ptr.buffer, tr->data_size)) {
        binder_user_error("%d:%d got transaction with invalid data ptr\n",
                proc->pid, thread->pid);
        return_error = BR_FAILED_REPLY;
        goto err_copy_data_failed;
    }
    if (copy_from_user(offp, (const void __user *)(uintptr_t)
               tr->data.ptr.offsets, tr->offsets_size)) {
        binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
                proc->pid, thread->pid);
        return_error = BR_FAILED_REPLY;
        goto err_copy_data_failed;
    }

    off_end = (void *)offp + tr->offsets_size;
    off_min = 0;
    for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;

        fp = (struct flat_binder_object *)(t->buffer->data + *offp);
        off_min = *offp + sizeof(struct flat_binder_object);
        switch (fp->type) {
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {
            struct binder_ref *ref;
            struct binder_node *node = binder_get_node(proc, fp->binder);

            if (node == NULL) {
                node = binder_new_node(proc, fp->binder, fp->cookie);
                if (node == NULL) {
                    return_error = BR_FAILED_REPLY;
                    goto err_binder_new_node_failed;
                }
                node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
                node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
            }

            ref = binder_get_ref_for_node(target_proc, node);
            if (ref == NULL) {
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_for_node_failed;
            }
            if (fp->type == BINDER_TYPE_BINDER)
                fp->type = BINDER_TYPE_HANDLE;
            else
                fp->type = BINDER_TYPE_WEAK_HANDLE;
            fp->binder = 0;
            fp->handle = ref->desc;
            fp->cookie = 0;
            binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
                       &thread->todo);

            trace_binder_transaction_node_to_ref(t, node, ref);
            binder_debug(BINDER_DEBUG_TRANSACTION,
                     "        node %d u%016llx -> ref %d desc %d\n",
                     node->debug_id, (u64)node->ptr,
                     ref->debug_id, ref->desc);
        } break;
        default:
            return_error = BR_FAILED_REPLY;
            goto err_bad_object_type;
        }
    }
    
    if (reply) {
        BUG_ON(t->buffer->async_transaction != 0);
        binder_pop_transaction(target_thread, in_reply_to);
    } else if (!(t->flags & TF_ONE_WAY)) {
        BUG_ON(t->buffer->async_transaction != 0);
        t->need_reply = 1;
        t->from_parent = thread->transaction_stack;
        thread->transaction_stack = t;
    } else {
        BUG_ON(target_node == NULL);
        BUG_ON(t->buffer->async_transaction != 1);
        if (target_node->has_async_transaction) {
            target_list = &target_node->async_todo;
            target_wait = NULL;
        } else
            target_node->has_async_transaction = 1;
    }
    
    t->work.type = BINDER_WORK_TRANSACTION;
    list_add_tail(&t->work.entry, target_list);
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    list_add_tail(&tcomplete->entry, &thread->todo);
    
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;
   
   ......
}

binder_thread_read

binder_thread_read 返回,binder_thread_read 开始了,由于无数据,被wait_event_freezable_exclusive 阻塞。

handler 的身世

在binder 通信中handler 代表了一个Binder 对象,那么这个hander 的真是意义到底是什么呢。上边我们看到了handler出处,看下这个handler 到底从哪里来,代表什么含义。fp 为flat_binder_object 对象, handler 被赋值为binder_refs 对象的desc. ServiceManger服务一定是0,其他的服务在这个基础上+1, 看下这个desc 的计算过程,本进程中的代理对象的 binder_ref 最大值 计数+1, 也就是说本进程的服务计数+1。所以不同的服务,可能有相同的handler。这个handler 为什么不交给ServiceManager 管理呢,所有的服务统一引用计数

ref = binder_get_ref_for_node(target_proc, node);
     
fp->binder = 0;
fp->handle = ref->desc;
fp->cookie = 0;

static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
                          struct binder_node *node)
{
    struct rb_node *n;
    struct rb_node **p = &proc->refs_by_node.rb_node;
    struct rb_node *parent = NULL;
    struct binder_ref *ref, *new_ref;

   ......

    new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
    for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
        ref = rb_entry(n, struct binder_ref, rb_node_desc);
        if (ref->desc > new_ref->desc)
            break;
        new_ref->desc = ref->desc + 1;
    }

   ......
    
    return new_ref;
}

ServiceManger 进程的binder_thread_read

ServiceManagere 对应的驱动的队列被唤醒,binder_thread_read 开始工作。

  1. binder_thread_read 首先从TODO 队列中取出需要完成的事物,注意是两个,write 的时候写添加了两个。首先取出 BINDER_WORK_TRANSACTION, binder_transaction。
  2. 然后将binder_transaction 结构体转化为 binder_transaction_data 结构体,这样用户态程序可以访问,cmd 转换。
  3. 将buffer 内核的地址转化为用户空间地址。依次调用put_user copy_to_user 将相关数据传递到用户空间。
  4. 取出 BINDER_WORK_TRANSACTION_COMPLETE binder_transaction。将数据传递到用户空间
static int binder_thread_read(struct binder_proc *proc,
                  struct binder_thread *thread,
                  binder_uintptr_t binder_buffer, size_t size,
                  binder_size_t *consumed, int non_block)
{
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    int ret = 0;
    int wait_for_proc_work;

    if (*consumed == 0) {
        if (put_user(BR_NOOP, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
    }

retry:
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);


    thread->looper |= BINDER_LOOPER_STATE_WAITING;
    if (wait_for_proc_work)
        proc->ready_threads++;

    binder_unlock(__func__);
    ......
    binder_lock(__func__);

    if (wait_for_proc_work)
        proc->ready_threads--;
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

    if (ret)
        return ret;

    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;

        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 {
            /* no data added */
            if (ptr - buffer == 4 &&
                !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                goto retry;
            break;
        }

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

        switch (w->type) {
        case BINDER_WORK_TRANSACTION: {
            t = container_of(w, struct binder_transaction, work);
        } break;
        case BINDER_WORK_TRANSACTION_COMPLETE: {
            cmd = BR_TRANSACTION_COMPLETE;
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);

            binder_stat_br(proc, thread, cmd);

            list_del(&w->entry);
            kfree(w);
            binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
        } break;

        if (!t)
            continue;

        BUG_ON(t->buffer == NULL);
        if (t->buffer->target_node) {
            struct binder_node *target_node = t->buffer->target_node;

            tr.target.ptr = target_node->ptr;
            tr.cookie =  target_node->cookie;
            t->saved_priority = task_nice(current);
            if (t->priority < target_node->min_priority &&
                !(t->flags & TF_ONE_WAY))
                binder_set_nice(t->priority);
            else if (!(t->flags & TF_ONE_WAY) ||
                 t->saved_priority > target_node->min_priority)
                binder_set_nice(target_node->min_priority);
            cmd = BR_TRANSACTION;
        } else {
            tr.target.ptr = 0;
            tr.cookie = 0;
            cmd = BR_REPLY;
        }
        tr.code = t->code;
        tr.flags = t->flags;
        tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);

        if (t->from) {
            struct task_struct *sender = t->from->proc->tsk;

            tr.sender_pid = task_tgid_nr_ns(sender,
                            task_active_pid_ns(current));
        } else {
            tr.sender_pid = 0;
        }

        tr.data_size = t->buffer->data_size;
        tr.offsets_size = t->buffer->offsets_size;
        tr.data.ptr.buffer = (binder_uintptr_t)(
                    (uintptr_t)t->buffer->data +
                    proc->user_buffer_offset);
        tr.data.ptr.offsets = tr.data.ptr.buffer +
                    ALIGN(t->buffer->data_size,
                        sizeof(void *));

        if (put_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);

        trace_binder_transaction_received(t);
        binder_stat_br(proc, thread, cmd);
                list_del(&t->work.entry);
        t->buffer->allow_user_free = 1;
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
            t->to_parent = thread->transaction_stack;
            t->to_thread = thread;
            thread->transaction_stack = t;
        } else {
            t->buffer->transaction = NULL;
            kfree(t);
            binder_stats_deleted(BINDER_STAT_TRANSACTION);
        }
        break;
    }
    
    return 0;
}

ServiceManager 的ioctl 阻塞返回。


mmap 的设计和binder 效率的讨论

有这么一种说法是 mmap 映射了用户空间和内核空间的地址,减少了一次从内核空间到用户地址空间的copy, 所以提高了效率。从上面的分析看

  1. 从用户态到内核, 发生了4次拷贝
  2. 从内核到用户态, 发生了一共 2 +2 =4 次copy, 仅仅减少了1次。

所以 binder 驱动的效率提升不在这里。为什么要使用mmap, 从binder 的设计上来看,读写操作没有谁用标准的linux 接口read和write, 那么从内核传递数据到用户态如何完成呢,ioctl 的第三个参数只能是从用户到内核传递地址。mmap 的设计是为了解决从内核传递数据到用户这一功能。

那为什么Binder 效率比传统的IPC要高效呢。
内核资源竞争 在传统的IPC中,read 和 write 操作都需要对内核中的 读写的buffer 进行资源竞争,解决并发方位的问题,一方面加锁操作消耗了一定的资源,另一方面,各个操作需要排队等待。

Binder 为了解决内内核中 buffer 的竞争,采用了为每个线程单独分配空间,避免了内核态的等待。这样效率就提高了,但是内核态不需要等待,用户空间的线程不一定能够及时的操作,所以binder 在用户态又引用了多线程支持。从内核态和用户态解决了资源访问等待的问题。这是binder 效率高的真正原因。

上一篇下一篇

猜你喜欢

热点阅读