Binder总结

本文深入探讨Android中的Binder机制,解释其如何实现跨进程通信,尤其是只进行一次数据拷贝的原因。通过分析服务添加到servicemanager的过程,阐述Binder如何通过句柄找到服务并传输数据。总结了Binder通信模型、数据拷贝过程以及servicemanager的角色。

Binder机制在android中无处不在,系统服务管理应用,系统服务之间,应用之间都使用了Binder完成跨进程通信。所以Binder到底是怎么实现跨进程通信的呢?为什么说Binder只有一次数据拷贝呢?为什么客户端通过句柄handle创建BpBinder就可以找到服务端,然后将数据传输给服务端呢?这是我学习Binder前的疑问。
这篇文章会跳过一些Binder的基础学习,主要解答以上一些疑问。
我们从Service添加到servicemanager中说起:
1、首先是客户端调用BpServiceManager(servicemanager的代理)的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;
    }

其中会将service名字和service分别写入Parcel中,我们来看下writeStrongBinder的实现:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

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) {
                ......
            } 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);
        }

        ......
}

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:
        *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;

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

        // Need to write meta-data?
        if (nullMetaData || val.binder != 0) {
            mObjects[mObjectsSize] = mDataPos;
            acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
            mObjectsSize++;
        }

        return finishWrite(sizeof(flat_binder_object));
    }

这里创建了一个结构体flat_binder_object,将成员type,binder,cookie分别赋值,注意这个结构体在内核中也用到了。之后我们会把这个结构体写入Parcel中,这里需要注意有一个偏移数组mObjects,通过mObjectsSize找到flat_binder_object在数组mData数组中的索引,我们在内核中也会用到它来查找flat_binder_object。数据准备好了,接下来就是通过BpBinder,再通过IPCThreadState调用transact方法,这里又对数据有一层封装:

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;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
    } else {
        return (mLastError = err);
    }

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

    return NO_ERROR;
}

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }

    binder_write_read bwr;

    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();

    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    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;
    }

    IF_LOG_COMMANDS() {
        TextOutput::Bundle _b(alog);
        if (outAvail != 0) {
            alog << "Sending commands to driver: " << indent;
            const void* cmds = (const void*)bwr.write_buffer;
            const 
<think>我们正在总结关于Binder和锁竞争的内容。根据用户提供的引用,特别是引用[1]和引用[2]中关于Systrace中Binder锁信息的解读,以及引用[3]中关于Binder机制的稳定性讨论,我们可以进行如下总结: 核心内容: 1. Binder机制Android中用于跨进程通信(IPC),其性能仅次于共享内存,但稳定性优于共享内存(因为共享内存需要处理并发同步,容易死锁和资源竞争)[^3]。 2. 在Systrace中,我们可以观察到Binder锁竞争的情况,这有助于分析卡顿和响应问题。 - 当有多个线程或进程等待同一个Binder锁时,Systrace中会显示`waiters=N`,表示有N个操作在等待锁释放[^2]。 3. 锁竞争导致的性能问题:如果多个线程需要同一个Binder锁,它们会排队等待,从而造成延迟。 因此,总结如下: ### Binder与锁竞争总结 1. **Binder机制优势** BinderAndroid主要的跨进程通信机制,相比共享内存和Socket具有以下特点: - **性能**:仅次于共享内存,但避免了共享内存的复杂同步问题; - **稳定性**:基于C/S架构,服务端与客户端解耦,减少资源竞争和死锁风险[^3]。 2. **锁竞争问题分析** 在Systrace中分析Binder锁竞争的关键点: - **`waiters`值**:表示等待同一Binder锁的操作数量(例如`waiters=2`表示有2个操作在等待); - **锁持有时间**:观察Binder事务(如`Binder:1605_B`)的执行时长,长时间持有锁会阻塞后续请求; - **卡顿溯源**:当UI线程因等待Binder锁而阻塞时,会导致应用卡顿或响应延迟[^1]。 3. **优化建议** - **减少跨进程调用**:合并冗余的Binder请求; - **异步通信**:使用`oneway`接口或异步Binder调用避免主线程阻塞; - **锁粒度优化**:拆分高并发场景下的全局锁,采用细粒度锁。 --- §§ 相关问题 §§ 1. 如何通过Systrace定位Binder锁竞争的具体代码位置? 2. Binder通信中`oneway`关键字如何避免线程阻塞? 3. Android中有哪些替代Binder的跨进程通信方案? > 参考文献: > : Systrace系列10 —— Binder 和锁竞争解读 > : Systrace 之 binder 锁竞争 > : Binder相关面试总结(三):Binder机制是如何跨进程的 </think> ### B集:Binder与锁竞争总结 Android系统中,Binder作为核心IPC(进程间通信)机制,其锁竞争问题直接影响系统性能与稳定性。以下为关键要点总结: #### 一、Binder锁竞争原理 1. **竞争场景** 当多个进程/线程同时请求同一Binder服务时,会进入锁等待队列: - **Systrace标识**:`waiters=N`(N表示等待线程数),例如`Binder:1605_B (4667)`的`waiters=2`表示2个线程在等待锁释放。 - **锁持有时间**:Binder事务执行期间持有锁,若操作耗时过长(如复杂计算或I/O),会阻塞后续请求。 2. **性能影响** - **卡顿根源**:UI线程因等待Binder锁释放而阻塞,导致帧渲染超时(>16ms)[^1]。 - **稳定性风险**:高并发场景下可能引发死锁或资源枯竭(如线程池耗尽)。 #### 二、Systrace分析指南 ```mermaid graph TD A[发现卡顿帧] --> B[定位Binder事务区块] B --> C{检查waiters值} C -->|waiters>0| D[确认锁持有者] D --> E[分析持有者执行耗时] E --> F[优化锁持有时间] ``` - **关键步骤**: 1. 定位`Binder transaction`区块及对应的`waiters`值[^1]。 2. 追踪锁持有者(如`Binder:1605_B`)的执行逻辑。 3. 检查锁持有时间是否异常(通常应<5ms)。 #### 三、优化策略 1. **减少锁争用**: - **异步调用**:使用`oneway`接口避免阻塞等待(如`startService`)。 - **拆分事务**:将大事务拆分为小粒度操作(例:分页加载数据)。 2. **缩短锁持有时间**: - 避免在Binder服务中执行耗时操作(如数据库读写),改用`Handler`异步处理。 - 优化数据结构,减少序列化/反序列化开销。 3. **架构设计**: - **服务隔离**:高频调用的Binder服务部署到独立进程。 - **连接池管理**:限制并发连接数,避免资源争抢(如`ContentFetcher`设计)。 > **典型对比**:Binder vs 其他IPC > | 机制 | 性能 | 稳定性 | 适用场景 | > |------------|---------|----------|------------------| > | **Binder** | ★★★★☆ | ★★★★★ | 系统级跨进程通信 | > | 共享内存 | ★★★★★ | ★★☆☆☆ | 大数据量低延迟 | > | Socket | ★★☆☆☆ | ★★★☆☆ | 网络通信 | > *Binder平衡性能与稳定性,规避共享内存的死锁风险[^3]* ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值