• (转)Android IPC机制详解


    o IBinder接口

    IBinder接口是对跨进程的对象的抽象。普通对象在当前进程可以访问,如果希望对象能被其它进程访问,那就必须实现IBinder接口。IBinder接口可以指向本地对象,也可以指向远程对象,调用者不需要关心指向的对象是本地的还是远程。

    transact是IBinder接口中一个比较重要的函数,它的函数原型如下:

    virtual status_t transact(
    uint32_t code,
     const
     Parcel&
     data,
     Parcel*
     reply,
     uint32_t flags =
     0
    )
     =
     0
    ;
    

    android中的IPC的基本模型是基于客户/服务器(C/S)架构的。

    客户端 请求通过内核模块中转 服务端

    如果IBinder指向的是一个客户端代理,那transact只是把请求发送给服务器。服务端的IBinder的transact则提供了实际的服务。

    o 客户端

    BpBinder是远程对象在当前进程的代理,它实现了IBinder接口。它的transact函数实现如下:

    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;
    
    }
    

    参数说明:

    • code 是请求的ID号。
    • data 是请求的参数。
    • reply 是返回的结果。
    • flags 一些额外的标识,如FLAG_ONEWAY。通常为0。

    transact只是简单的调用了IPCThreadState::self()的transact,在IPCThreadState::transact中:

    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_LOG_TRANSACTIONS(
    )
     {
    
            TextOutput::
    Bundle
     _b(
    alog)
    ;
    
            alog <<
     "BC_TRANSACTION thr "
     <<
     (
    void
    *
    )
    pthread_self(
    )
     <<
     " / hand "
    
                <<
     handle <<
     " / code "
     <<
     TypeCode(
    code)
     <<
     ": "
    
                <<
     indent <<
     data <<
     dedent <<
     endl;
    
        }
    
     
        if
     (
    err ==
     NO_ERROR)
     {
    
            LOG_ONEWAY(
    ">>>> SEND from pid %d uid %d %s"
    ,
     getpid(
    )
    ,
     getuid(
    )
    ,
    
                (
    flags &
     TF_ONE_WAY)
     ==
     0
     ?
     "READ REPLY"
     :
     "ONE WAY"
    )
    ;
    
            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)
    ;
    
            }
    
     
            IF_LOG_TRANSACTIONS(
    )
     {
    
                TextOutput::
    Bundle
     _b(
    alog)
    ;
    
                alog <<
     "BR_REPLY thr "
     <<
     (
    void
    *
    )
    pthread_self(
    )
     <<
     " / hand "
    
                    <<
     handle <<
     ": "
    ;
    
                if
     (
    reply)
     alog <<
     indent <<
     *
    reply <<
     dedent <<
     endl;
    
                else
     alog <<
     "(none requested)"
     <<
     endl;
    
            }
    
        }
     else
     {
    
            err =
     waitForResponse(
    NULL,
     NULL)
    ;
    
        }
    
     
        return
     err;
    
    }
    
     
    status_t IPCThreadState::
    waitForResponse
    (
    Parcel *
    reply,
     status_t *
    acquireResult)
    
    {
    
        int32_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
    ;
    
     
            cmd =
     mIn.readInt32
    (
    )
    ;
    
     
            IF_LOG_COMMANDS(
    )
     {
    
                alog <<
     "Processing waitForResponse Command: "
    
                    <<
     getReturnString(
    cmd)
     <<
     endl;
    
            }
    
     
            switch
     (
    cmd)
     {
    
            case
     BR_TRANSACTION_COMPLETE:
    
                if
     (
    !
    reply &&
     !
    acquireResult)
     goto
     finish;
    
                break
    ;
    
     
            case
     BR_DEAD_REPLY:
    
                err =
     DEAD_OBJECT;
    
                goto
     finish;
    
     
            case
     BR_FAILED_REPLY:
    
                err =
     FAILED_TRANSACTION;
    
                goto
     finish;
    
     
            case
     BR_ACQUIRE_RESULT:
    
                {
    
                    LOG_ASSERT(
    acquireResult !=
     NULL,
     "Unexpected brACQUIRE_RESULT"
    )
    ;
    
                    const
     int32_t result =
     mIn.readInt32
    (
    )
    ;
    
                    if
     (
    !
    acquireResult)
     continue
    ;
    
                    *
    acquireResult =
     result ?
     NO_ERROR :
     INVALID_OPERATION;
    
                }
    
                goto
     finish;
    
     
            case
     BR_REPLY:
    
                {
    
                    binder_transaction_data tr;
    
                    err =
     mIn.read
    (
    &
    tr,
     sizeof
    (
    tr)
    )
    ;
    
                    LOG_ASSERT(
    err ==
     NO_ERROR,
     "Not enough command data for brREPLY"
    )
    ;
    
                    if
     (
    err !=
     NO_ERROR)
     goto
     finish;
    
     
                    if
     (
    reply)
     {
    
                        if
     (
    (
    tr.flags
     &
     TF_STATUS_CODE)
     ==
     0
    )
     {
    
                            reply->
    ipcSetDataReference(
    
                                reinterpret_cast(
    tr.data
    .ptr
    .buffer
    )
    ,
    
                                tr.data_size
    ,
    
                                reinterpret_cast(
    tr.data
    .ptr
    .offsets
    )
    ,
    
                                tr.offsets_size
    /
    sizeof
    (
    size_t)
    ,
    
                                freeBuffer,
     this)
    ;
    
                        }
     else
     {
    
                            err =
     *
    static_cast(
    tr.data
    .ptr
    .buffer
    )
    ;
    
                            freeBuffer(
    NULL,
    
                                reinterpret_cast(
    tr.data
    .ptr
    .buffer
    )
    ,
    
                                tr.data_size
    ,
    
                                reinterpret_cast(
    tr.data
    .ptr
    .offsets
    )
    ,
    
                                tr.offsets_size
    /
    sizeof
    (
    size_t)
    ,
     this)
    ;
    
                        }
    
                    }
     else
     {
    
                        freeBuffer(
    NULL,
    
                            reinterpret_cast(
    tr.data
    .ptr
    .buffer
    )
    ,
    
                            tr.data_size
    ,
    
                            reinterpret_cast(
    tr.data
    .ptr
    .offsets
    )
    ,
    
                            tr.offsets_size
    /
    sizeof
    (
    size_t)
    ,
     this)
    ;
    
                        continue
    ;
    
                    }
    
                }
    
                goto
     finish;
    
     
            default
    :
    
                err =
     executeCommand(
    cmd)
    ;
    
                if
     (
    err !=
     NO_ERROR)
     goto
     finish;
    
                break
    ;
    
            }
    
        }
    
     
    finish:
    
        if
     (
    err !=
     NO_ERROR)
     {
    
            if
     (
    acquireResult)
     *
    acquireResult =
     err;
    
            if
     (
    reply)
     reply->
    setError(
    err)
    ;
    
            mLastError =
     err;
    
        }
    
     
        return
     err;
    
    }
    

    这里transact把请求经内核模块发送了给服务端,服务端处理完请求之后,沿原路返回结果给调用者。这里也可以看出请求是同步操作,它会等待直到结果返回为止。

    在BpBinder之上进行简单包装,我们可以得到与服务对象相同的接口,调用者无需要关心调用的对象是远程的还是本地的。拿ServiceManager来说:
    (frameworks/base/libs/utils/IServiceManager.cpp)

    class BpServiceManager :
     public BpInterface
    {
    
    public:
    
        BpServiceManager(
    const
     sp&
     impl)
    
            :
     BpInterface(
    impl)
    
        {
    
        }
    
    ...
        virtual
     status_t addService(
    const
     String16&
     name,
     const
     sp&
     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.readInt32
    (
    )
     :
     err;
    
        }
    
    ...
    }
    ;
    

    BpServiceManager实现了 IServiceManager和IBinder两个接口,调用者可以把BpServiceManager的对象看作是一个 IServiceManager对象或者IBinder对象。当调用者把BpServiceManager对象当作IServiceManager对象使 用时,所有的请求只是对BpBinder::transact的封装。这样的封装使得调用者不需要关心IServiceManager对象是本地的还是远 程的了。

    客户通过defaultServiceManager函数来创建BpServiceManager对象:
    (frameworks/base/libs/utils/IServiceManager.cpp)

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

    先通过ProcessState::self()->getContextObject(NULL)创建一个Binder对象,然后通过 interface_cast和IMPLEMENT_META_INTERFACE(ServiceManager, “android.os.IServiceManager”)把Binder对象包装成 IServiceManager对象。原理上等同于创建了一个BpServiceManager对象。

    ProcessState::self()->getContextObject调用ProcessState::getStrongProxyForHandle创建代理对象:

    sp<
    IBinder>
     ProcessState::
    getStrongProxyForHandle
    (
    int32_t handle)
    
    {
    
        sp<
    IBinder>
     result;
     
     
        AutoMutex _l(
    mLock)
    ;
     
     
        handle_entry*
     e =
     lookupHandleLocked(
    handle)
    ;
     
     
        if
     (
    e !=
     NULL)
     {
    
            // We need to create a new BpBinder if there isn't currently one, OR we
    
            // are unable to acquire a weak reference on this current one.  See comment
    
            // in getWeakProxyForHandle() for more info about this.
    
            IBinder*
     b =
     e->
    binder;
    
            if
     (
    b ==
     NULL ||
     !
    e->
    refs->
    attemptIncWeak(
    this)
    )
     {
    
                b =
     new BpBinder(
    handle)
    ;
    
                e->
    binder =
     b;
    
                if
     (
    b)
     e->
    refs =
     b->
    getWeakRefs(
    )
    ;
    
                result =
     b;
    
            }
     else
     {
    
                // This little bit of nastyness is to allow us to add a primary
    
                // reference to the remote proxy when this team doesn't have one
    
                // but another team is sending the handle to us.
    
                result.force_set
    (
    b)
    ;
    
                e->
    refs->
    decWeak(
    this)
    ;
    
            }
    
        }
     
     
        return
     result;
    
    }
    

    如果handle为空,默认为context_manager对象,context_manager实际上就是ServiceManager。
    o 服务端 
    服务端也要实现IBinder接口,BBinder类对IBinder接口提供了部分默认实现,其中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;
    
    }
    

    PING_TRANSACTION请求用来检查对象是否还存在,这里简单的把 pingBinder的返回值返回给调用者。其它的请求交给onTransact处理。onTransact是BBinder里声明的一个 protected类型的虚函数,这个要求它的子类去实现。比如CameraService里的实现如下:

    status_t CameraService::
    onTransact
    (
    
        uint32_t code,
     const
     Parcel&
     data,
     Parcel*
     reply,
     uint32_t flags)
    
    {
    
        // permission checks...
    
        switch
     (
    code)
     {
    
            case
     BnCameraService::
    CONNECT
    :
    
                IPCThreadState*
     ipc =
     IPCThreadState::
    self
    (
    )
    ;
    
                const
     int
     pid =
     ipc->
    getCallingPid(
    )
    ;
    
                const
     int
     self_pid =
     getpid(
    )
    ;
    
                if
     (
    pid !=
     self_pid)
     {
    
                    // we're called from a different process, do the real check
    
                    if
     (
    !
    checkCallingPermission(
    
                            String16(
    "android.permission.CAMERA"
    )
    )
    )
    
                    {
    
                        const
     int
     uid =
     ipc->
    getCallingUid(
    )
    ;
    
                        LOGE(
    "Permission Denial: "
    
                                "can't use the camera pid=%d, uid=%d"
    ,
     pid,
     uid)
    ;
    
                        return
     PERMISSION_DENIED;
    
                    }
    
                }
    
                break
    ;
    
        }
    
     
        status_t err =
     BnCameraService::
    onTransact
    (
    code,
     data,
     reply,
     flags)
    ;
    
     
        LOGD(
    "+++ onTransact err %d code %d"
    ,
     err,
     code)
    ;
    
     
        if
     (
    err ==
     UNKNOWN_TRANSACTION ||
     err ==
     PERMISSION_DENIED)
     {
    
            // the 'service' command interrogates this binder for its name, and then supplies it
    
            // even for the debugging commands.  that means we need to check for it here, using
    
            // ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to
    
            // BnSurfaceComposer before falling through to this code).
    
     
            LOGD(
    "+++ onTransact code %d"
    ,
     code)
    ;
    
     
            CHECK_INTERFACE(
    ICameraService,
     data,
     reply)
    ;
    
     
            switch
    (
    code)
     {
    
            case
     1000
    :
    
            {
    
                if
     (
    gWeakHeap !=
     0
    )
     {
    
                    sp h =
     gWeakHeap.promote
    (
    )
    ;
    
                    IMemoryHeap *
    p =
     gWeakHeap.unsafe_get
    (
    )
    ;
    
                    LOGD(
    "CHECKING WEAK REFERENCE %p (%p)"
    ,
     h.get
    (
    )
    ,
     p)
    ;
    
                    if
     (
    h !=
     0
    )
    
                        h->
    printRefs(
    )
    ;
    
                    bool attempt_to_delete =
     data.readInt32
    (
    )
     ==
     1
    ;
    
                    if
     (
    attempt_to_delete)
     {
    
                        // NOT SAFE!
    
                        LOGD(
    "DELETING WEAK REFERENCE %p (%p)"
    ,
     h.get
    (
    )
    ,
     p)
    ;
    
                        if
     (
    p)
     delete p;
    
                    }
    
                    return
     NO_ERROR;
    
                }
    
            }
    
            break
    ;
    
            default
    :
    
                break
    ;
    
            }
    
        }
    
        return
     err;
    
    }
    

    由此可见,服务端的onTransact是一个请求分发函数,它根据请求码(code)做相应的处理。

    o 消息循环

    服务端(任何进程都可以作为服务端)有一个线程监听来自客户端的请求,并循环处理这些请求。

    如果在主线程中处理请求,可以直接调用下面的函数:

    IPCThreadState::
    self
    (
    )
    ->
    joinThreadPool(
    mIsMain)
    ;
    

    如果想在非主线程中处理请求,可以按下列方式:

            sp
     proc =
     ProcessState::
    self
    (
    )
    ;
    
            if
     (
    proc->
    supportsProcesses(
    )
    )
     {
    
                LOGV(
    "App process: starting thread pool.\n
    "
    )
    ;
    
                proc->
    startThreadPool(
    )
    ;
    
            }
    

    startThreadPool的实现原理:

    void
     ProcessState::
    startThreadPool
    (
    )
    
    {
    
        AutoMutex _l(
    mLock)
    ;
    
        if
     (
    !
    mThreadPoolStarted)
     {
    
            mThreadPoolStarted =
     true
    ;
    
            spawnPooledThread(
    true
    )
    ;
    
        }
    
    }
     
     
    void
     ProcessState::
    spawnPooledThread
    (
    bool isMain)
    
    {
    
        if
     (
    mThreadPoolStarted)
     {
    
            int32_t s =
     android_atomic_add(
    1
    ,
     &
    mThreadPoolSeq)
    ;
    
            char
     buf[
    32
    ]
    ;
    
            sprintf(
    buf,
     "Binder Thread #%d"
    ,
     s)
    ;
    
            LOGV(
    "Spawning new pooled thread, name=%s\n
    "
    ,
     buf)
    ;
    
            sp
     t =
     new PoolThread(
    isMain)
    ;
    
            t->
    run(
    buf)
    ;
    
        }
    
    }
    

    这里创建了PoolThread的对象,实现上就是创建了一个线程。所有的线程类都要实现threadLoop虚函数。PoolThread的threadLoop的实现如下:

        virtual bool threadLoop(
    )
    
        {
    
            IPCThreadState::
    self
    (
    )
    ->
    joinThreadPool(
    mIsMain)
    ;
    
            return
     false
    ;
    
        }
    

    上述代码,简而言之就是创建了一个线程,然后在线程里调用 IPCThreadState::self()->joinThreadPool函数。

    下面再看joinThreadPool的实现:

    do
    
    {
    
    ...
            result
     =
     talkWithDriver(
    )
    ;
    
            if
     (
    result >=
     NO_ERROR)
     {
    
                size_t IN =
     mIn.dataAvail
    (
    )
    ;
    
                if
     (
    IN <
     sizeof
    (
    int32_t)
    )
     continue
    ;
    
                cmd =
     mIn.readInt32
    (
    )
    ;
    
                IF_LOG_COMMANDS(
    )
     {
    
                    alog <<
     "Processing top-level Command: "
    
                        <<
     getReturnString(
    cmd)
     <<
     endl;
    
                }
    
                result =
     executeCommand(
    cmd)
    ;
    
            }
    
    ...
    while
    (
    ...)
    ;
    

    这个函数在循环中重复执行下列动作:

    1. talkWithDriver 通过ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)读取请求和写回结果。
    2. executeCommand 执行相应的请求

    在IPCThreadState::executeCommand(int32_t cmd)函数中:

    1. 对于控制对象生命周期的请求,像BR_ACQUIRE/BR_RELEASE直接做了处理。
    2. 对于BR_TRANSACTION请求,它调用被请求对象的transact函数。

    按下列方式调用实际的对象:

    if
     (
    tr.target
    .ptr
    )
     {
    
        sp<
    BBinder>
     b(
    (
    BBinder*
    )
    tr.cookie
    )
    ;
    
        const
     status_t error =
     b->
    transact(
    tr.code
    ,
     buffer,
     &
    reply,
     0
    )
    ;
    
        if
     (
    error <
     NO_ERROR)
     reply.setError
    (
    error)
    ;
     
     
    }
     else
     {
    
        const
     status_t error =
     the_context_object->
    transact(
    tr.code
    ,
     buffer,
     &
    reply,
     0
    )
    ;
    
        if
     (
    error <
     NO_ERROR)
     reply.setError
    (
    error)
    ;
    
    }
    

    如果tr.target.ptr不为空,就把tr.cookie转换成一个Binder对象,并调用它的transact函数。如果没有目标对象, 就调用 the_context_object对象的transact函数。奇怪的是,根本没有谁对the_context_object进行初始 化,the_context_object是空指针。原因是context_mgr的请求发给了ServiceManager,所以根本不会走到else 语句里来。

    o 内核模块

    android使用了一个内核模块binder来中转各个进程之间的消息。模块源代码放在binder.c里,它是一个字符驱动程序,主要通过 binder_ioctl与用户空间的进程交换数据。其中BINDER_WRITE_READ用来读写数据,数据包中有一个cmd域用于区分不同的请求:

    1. binder_thread_write用于发送请求或返回结果。
    2. binder_thread_read用于读取结果。

    从binder_thread_write中调用binder_transaction中转请求和返回结果,binder_transaction的实现如下:

    对请求的处理:

    1. 通过对象的handle找到对象所在的进程,如果handle为空就认为对象是context_mgr,把请求发给context_mgr所在的进程。
    2. 把请求中所有的binder对象全部放到一个RB树中。
    3. 把请求放到目标进程的队列
  • 相关阅读:
    c语言:猴子吃桃问题
    c语言:输入任意数求该数的阶乘
    (整理三)高并发架构思路,附十万定时任务执行解决方案
    (整理4)RPC服务和HTTP服务简单说明
    .NET Core和Swagger 生成 Api 文档转
    (整理二)读取大日志文件
    (整理一)理解分布式事务,高并发下分布式事务的解决方案-附索引的利弊
    2016年结
    2013年结
    2017年结
  • 原文地址:https://www.cnblogs.com/greywolf/p/3031194.html
Copyright © 2020-2023  润新知