Binder基础-认识Parcel

263 阅读9分钟

Binder 主要作用就是进程间通信,而通信离不开数据传递,android.os.Parcel 则是用于在不同的进程或线程之间传递数据的核心基础类。它主要用于在进程间通信(IPC)将数据序列化和反序列化。Parcel 提供了六大基础类型写入读取,还支持读写这些基础类型的数组类型。不仅如此还支持复杂数据类型的读写,如Parcelable(Android 特有)和 Serializable(Java 标准接口)对象。

类型名读写占用的内存字节
六种基本类型及其数组类型boolean/boolean[]1(数组额外多4字节写数组长度)
int/int[]4
char/char[]2
long/long[]8
float/float[]4
double/double[]8
Parcelable实现Parcelable接口的类按实际读写的内存大小计算
Serializable实现Serializable接口的类按实际读写的内存大小计算
BundlesAndroid默认提供一种Key-Value形式打包数据的方法按实际读写的内存大小计算
Binder继承自IBinder接口的类对象24(64位系统)
FileDescriptor文件描述符fd

详细参考

developer.android.com/reference/a…

1、主要功能

  1. 序列化和反序列化: Parcel 允许将对象序列化到一个流中,并在需要时将其反序列化回来。这对于在进程之间传递对象是非常重要的。
  2. 数据传输: Parcel 可用于在不同的组件(如 Activity、Service 或其他组件)间传递数据。

2、主要方法

  • 写入数据:

    • writeInt(int value): 将一个整数写入 Parcel
    • writeString(String value): 将一个字符串写入 Parcel
    • writeParcelable(Parcelable value, int flags): 将一个实现了 Parcelable 接口的对象写入 Parcel
  • 读取数据:

    • readInt(): 从 Parcel 中读取一个整数。
    • readString(): 从 Parcel 中读取一个字符串。
    • readParcelable(ClassLoader loader): 从 Parcel 中读取一个实现了 Parcelable 接口的对象。

3、核心原理

3.1、Parcel 构造过程

使用基础 obtain 获取一个 Parcel 对象。

Parcel.obtain()
private Parcel(long nativePtr) {
    if (DEBUG_RECYCLE) {
        mStack = new RuntimeException();
    }
    //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
    init(nativePtr);
}

private void init(long nativePtr) {
    if (nativePtr != 0) {
        mNativePtr = nativePtr;
        mOwnsNativeParcelObject = false;
    } else {
        mNativePtr = nativeCreate();
        mOwnsNativeParcelObject = true;
    }
}
Parcel::Parcel()
{
    LOG_ALLOC("Parcel %p: constructing", this);
    initState();
}
void Parcel::initState()
{
    LOG_ALLOC("Parcel %p: initState", this);
    mError = NO_ERROR;
    mData = nullptr;
    mDataSize = 0;
    mDataCapacity = 0;
    mDataPos = 0;
    ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
    ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
    mVariantFields.emplace<KernelFields>();
    mAllowFds = true;
    mDeallocZero = false;
    mOwner = nullptr;
    mEnforceNoDataAvail = true;
    mServiceFuzzing = false;
}

较重要的参数进行解释:

参数意义
mData表示一块内存,用来存储 IPC 数据,记录的是这块内存缓冲区的内存起始地址
mDataSize当前 Parcel 中 mData 已存储的数据大小
mDataCapacity当前 Parcel mData 指示的内存总大小
mDataPos当前 Parcel 数据写入或读取的内存位置
mVariantFieldsBinder 中 Parcel 的优化,通过使用 variant 数据结构来优化空间利用
mVariantFields.mObjects表示一块内存(该对象主要用作Binder记录相关对象的引用)
mVariantFields.mObjectsSize当前Parcel中mObjects已存储的数据大小
mVariantFields.mObjectsCapacity当前Parcel mObjects指示的内存的大小

3.2、Parcel 写入过程

3.2.1、writeInt

以写一个Int值为例

android.os.Parcel data = android.os.Parcel.obtain();
data.writeInt(1);
frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}
3.2.2、writeAligned
frameworks/native/libs/binder/Parcel.cpp
template<class T>
status_t Parcel::writeAligned(T val) {
    static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));//函数参数的一些基本检查,确保类型 T 的大小不需要额外的填充
    static_assert(std::is_trivially_copyable_v<T>);//确保类型 T 是平凡可拷贝的(trivially copyable)

    //检查 Parcel 的当前写入位置 mDataPos 是否有足够的空间来存储 val
    //mDataCapacity 是 Parcel 数据缓冲区的总容量
    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
        //如果有足够的空间,使用 memcpy 将 val 的内存拷贝到 mData 缓冲区中的当前位置 mDataPos
        memcpy(mData + mDataPos, &val, sizeof(val));
        //调用 finishWrite 函数来完成写入操作,并传递写入的字节数(sizeof(val))
        return finishWrite(sizeof(val));
    }

    //如果空间不足,调用 growData 函数来扩展数据缓冲区的大小
    //growData 的参数是需要额外的字节数(sizeof(val))
    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
}
3.2.3、finishWrite
status_t Parcel::finishWrite(size_t len)
{
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    //printf("Finish write of %d\n", len);
    mDataPos += len;
    ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos);
    if (mDataPos > mDataSize) {
        mDataSize = mDataPos;
        ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize);
    }
    //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
    return NO_ERROR;
}

3.3、Parcel 内存扩容

3.3.1、growData
status_t Parcel::growData(size_t len)
{
    if (len > INT32_MAX) {
        // don't accept size_t values which may have come from an
        // inadvertent conversion from a negative int.
        return BAD_VALUE;
    }

    //几种内存溢出的检查
    if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow
    if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow
    //进行扩容获取一个新的内存大小值,新的缓冲区大小是当前大小加上新增长度后总和的1.5倍
    size_t newSize = ((mDataSize+len)*3)/2;
    return (newSize <= mDataSize)
            ? (status_t) NO_MEMORY
            : continueWrite(std::max(newSize, (size_t) 128));
}
3.3.1、continueWrite
status_t Parcel::continueWrite(size_t desired)
{
    // 检查请求的大小是否超出 INT32_MAX如果超出,返回 BAD_VALUE 错误
    if (desired > INT32_MAX) {
        return BAD_VALUE;
    }

    // 获取与 kernelFields 或 RPC 相关的字段
    auto* kernelFields = maybeKernelFields();
    auto* rpcFields = maybeRpcFields();

    // 如果目标大小小于当前数据大小,则需要调整对象大小,进行内存裁剪
    size_t objectsSize =
            kernelFields ? kernelFields->mObjectsSize : rpcFields->mObjectPositions.size();
    if (desired < mDataSize) {
        if (desired == 0) {
            // 如果目标大小为 0,所有对象都将被丢弃
            objectsSize = 0;
        } else {
            // 如果有内核字段,调整对象大小以适应新数据大小
            if (kernelFields) {
                while (objectsSize > 0) {
                    // 检查每个对象的位置是否小于目标大小如果是,则停止调整
                    if (kernelFields->mObjects[objectsSize - 1] < desired) break;
                    objectsSize--;
                }
            } else {
                // 如果是 RPC 字段,调整对象大小以适应新数据大小
                while (objectsSize > 0) {
                    if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break;
                    objectsSize--;
                }
            }
        }
    }

    // 如果存在数据所有者(mOwner)有效
    if (mOwner) {
        // 如果目标大小为 0,释放当前数据
        if (desired == 0) {
            freeData();
            return NO_ERROR;
        }

        // 如果存在所有者,需要接管数据的所有权
        uint8_t* data = (uint8_t*)malloc(desired); // 使用 malloc 分配新的数据缓冲区
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }
        binder_size_t* objects = nullptr;

        // 如果有内核字段且需要保留对象,分配新的对象数组并拷贝对象引用
        if (kernelFields && objectsSize) {
            objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t));
            if (!objects) {
                free(data); // 如果对象数组分配失败,释放已分配的数据缓冲区
                mError = NO_MEMORY;
                return NO_MEMORY;
            }

            // 保留将要保留的对象的引用
            size_t oldObjectsSize = kernelFields->mObjectsSize;
            kernelFields->mObjectsSize = objectsSize;
            acquireObjects(); // 获取对象的引用
            kernelFields->mObjectsSize = oldObjectsSize;
        }
        // 如果是 RPC 字段,截断不需要的 RPC 对象
        if (rpcFields) {
            if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
                free(data);
                return status;
            }
        }

        // 将已有的数据复制到新分配的缓冲区中
        if (mData) {
            memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
        }
        if (objects && kernelFields && kernelFields->mObjects) {
            memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t));
        }
        // 关闭文件描述符(TODO: 可能需要进一步确认)
        if (kernelFields) {
            closeFileDescriptors();
        }
        // 释放所有者的数据,并将所有权转移到当前对象
        mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
               kernelFields ? kernelFields->mObjectsSize : 0);
        mOwner = nullptr;

        // 更新全局统计信息,并设置新的数据缓冲区及其容量
        LOG_ALLOC("Parcel %p: taking ownership of %zu capacity", this, desired);
        gParcelGlobalAllocSize += desired;
        gParcelGlobalAllocCount++;

        mData = data;
        mDataSize = (mDataSize < desired) ? mDataSize : desired; // 设定数据实际大小
        ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
        mDataCapacity = desired; // 更新数据缓冲区容量
        if (kernelFields) {
            kernelFields->mObjects = objects;
            kernelFields->mObjectsSize = kernelFields->mObjectsCapacity = objectsSize;
            kernelFields->mNextObjectHint = 0;
            kernelFields->mObjectsSorted = false;
        }

    } else if (mData) {
        // 如果没有所有者但已有数据,处理缓冲区的重新分配
        if (kernelFields && objectsSize < kernelFields->mObjectsSize) {
#ifdef BINDER_WITH_KERNEL_IPC
            // 释放丢弃的对象引用
            const sp<ProcessState> proc(ProcessState::self());
            for (size_t i = objectsSize; i < kernelFields->mObjectsSize; i++) {
                const flat_binder_object* flat =
                        reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
                if (flat->hdr.type == BINDER_TYPE_FD) {
                    kernelFields->mFdsKnown = false;
                }
                release_object(proc, *flat, this);
            }

            // 重新分配对象数组
            if (objectsSize == 0) {
                free(kernelFields->mObjects);
                kernelFields->mObjects = nullptr;
                kernelFields->mObjectsCapacity = 0;
            } else {
                binder_size_t* objects =
                        (binder_size_t*)realloc(kernelFields->mObjects,
                                                objectsSize * sizeof(binder_size_t));
                if (objects) {
                    kernelFields->mObjects = objects;
                    kernelFields->mObjectsCapacity = objectsSize;
                }
            }
            kernelFields->mObjectsSize = objectsSize;
            kernelFields->mNextObjectHint = 0;
            kernelFields->mObjectsSorted = false;
#else  // BINDER_WITH_KERNEL_IPC
            LOG_ALWAYS_FATAL("Non-zero numObjects for RPC Parcel");
#endif // BINDER_WITH_KERNEL_IPC
        }
        // 如果是 RPC 字段,截断 RPC 对象
        if (rpcFields) {
            if (status_t status = truncateRpcObjects(objectsSize); status != OK) {
                return status;
            }
        }

        // 如果已有数据,使用 realloc 调整缓冲区大小
        if (desired > mDataCapacity) {
            uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
            if (data) {
                LOG_ALLOC("Parcel %p: continue from %zu to %zu capacity", this, mDataCapacity,
                        desired);
                gParcelGlobalAllocSize += desired;
                gParcelGlobalAllocSize -= mDataCapacity;
                mData = data;
                mDataCapacity = desired;
            } else {
                mError = NO_MEMORY;
                return NO_MEMORY;
            }
        } else {
            // 如果新大小小于当前大小,则调整数据大小和位置
            if (mDataSize > desired) {
                mDataSize = desired;
                ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
            }
            if (mDataPos > desired) {
                mDataPos = desired;
                ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos);
            }
        }

    } else {
        // 如果没有数据和所有者,分配并初始化数据缓冲区
        uint8_t* data = (uint8_t*)malloc(desired);
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }

        // 记录当前状态,并初始化数据
        if (!(mDataCapacity == 0 &&
              (kernelFields == nullptr ||
               (kernelFields->mObjects == nullptr && kernelFields->mObjectsCapacity == 0)))) {
            ALOGE("continueWrite: %zu/%p/%zu/%zu", mDataCapacity,
                  kernelFields ? kernelFields->mObjects : nullptr,
                  kernelFields ? kernelFields->mObjectsCapacity : 0, desired);
        }

        // 更新全局统计信息,分配新的数据缓冲区
        LOG_ALLOC("Parcel %p: allocating with %zu capacity", this, desired);
        gParcelGlobalAllocSize += desired;
        gParcelGlobalAllocCount++;

        mData = data;
        mDataSize = mDataPos = 0; // 初始化数据大小和位置
        ALOGV("continueWrite Setting data size of %p to %zu", this, mDataSize);
        ALOGV("continueWrite Setting data pos of %p to %zu", this, mDataPos);
        mDataCapacity = desired; // 更新数据缓冲区的容量
    }

    return NO_ERROR;
}

continueWrite 函数主要用于:

  • 扩展缓冲区以容纳更多数据
  • 缩小缓冲区以释放不再需要的内存
  • 在首次写入数据时进行初始化

分别使用 malloc 和 calloc 的原由

uint8_t* data = (uint8_t*)malloc(desired):用于分配一个指定大小的内存块 desired。这里使用 malloc 是因为我们仅需要一个新的内存块来存储数据,且在分配后会用现有的数据初始化这个内存块(即用 memcpy 进行初始化)。因此,初始内容不重要,直接使用 malloc 即可 uint8_t* data = (uint8_t*)malloc(desired):同样地,用于分配初始数据缓冲区。如果没有现有的数据需要复制和初始化,这里使用 malloc 也是合理的,因为我们会直接用分配的内存初始化数据

objects = (binder_size_t*)calloc(objectsSize, sizeof(binder_size_t)):这里使用 calloc 是因为需要分配并初始化一个对象数组。objects 数组需要被初始化为零,因为在后续操作中,这些零值用于表示未使用的对象位置,或者作为一个起始值以避免未定义行为。使用 calloc 可以确保这些位置初始为零

总结

malloc 用于分配内存时,当你需要的内存块只需要后续初始化(如用 memcpy),且不需要内存块初始为零时使用

calloc 用于分配内存并需要其初始化为零的情况,如需要确保所有的内存位置从一开始就是零值