免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1745 | 回复: 0
打印 上一主题 下一主题

Android IPC机制详解 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-08-07 10:17 |只看该作者 |倒序浏览

                       
Android IPC机制详解
                       
转载时请注明出处和作者联系方式
文章出处:
http://www.limodev.cn/blog
作者联系方式:李先静
                       
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 >>> 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 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 defaultServiceManager()
    {
        if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
        {
            AutoMutex _l(gDefaultServiceManagerLock);
            if (gDefaultServiceManager == NULL) {
                gDefaultServiceManager = interface_cast(
                    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 ProcessState::getStrongProxyForHandle(int32_t handle)
    {
        sp 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
    这个函数在循环中重复执行下列动作:
  • talkWithDriver 通过ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)读取请求和写回结果。
  • executeCommand 执行相应的请求
    在IPCThreadState::executeCommand(int32_t cmd)函数中:
  • 对于控制对象生命周期的请求,像BR_ACQUIRE/BR_RELEASE直接做了处理。
  • 对于BR_TRANSACTION请求,它调用被请求对象的transact函数。
    按下列方式调用实际的对象:
    if (tr.target.ptr) {
        sp b((BBinder*)tr.cookie);
        const status_t error = b->transact(tr.code, buffer, &reply, 0);
        if (error transact(tr.code, buffer, &reply, 0);
        if (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域用于区分不同的请求:
  • binder_thread_write用于发送请求或返回结果。
  • binder_thread_read用于读取结果。
    从binder_thread_write中调用binder_transaction中转请求和返回结果,binder_transaction的实现如下:
    对请求的处理:
  • 通过对象的handle找到对象所在的进程,如果handle为空就认为对象是context_mgr,把请求发给context_mgr所在的进程。
  • 把请求中所有的binder对象全部放到一个RB树中。
  • 把请求放到目标进程的队列中,等待目标进程读取。
    如何成为context_mgr呢?内核模块提供了BINDER_SET_CONTEXT_MGR调用:
    static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
            ...
            case BINDER_SET_CONTEXT_MGR:
                    if (binder_context_mgr_node != NULL) {
                            printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
                            ret = -EBUSY;
                            goto err;
                    }
                    if (binder_context_mgr_uid != -1) {
                            if (binder_context_mgr_uid != current->euid) {
                                    printk(KERN_ERR "binder: BINDER_SET_"
                                           "CONTEXT_MGR bad uid %d != %d\n",
                                           current->euid,
                                           binder_context_mgr_uid);
                                    ret = -EPERM;
                                    goto err;
                            }
                    } else
                            binder_context_mgr_uid = current->euid;
                    binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
                    if (binder_context_mgr_node == NULL) {
                            ret = -ENOMEM;
                            goto err;
                    }
                    binder_context_mgr_node->local_weak_refs++;
                    binder_context_mgr_node->local_strong_refs++;
                    binder_context_mgr_node->has_strong_ref = 1;
                    binder_context_mgr_node->has_weak_ref = 1;
                    break;
    ServiceManager(frameworks/base/cmds/servicemanager)通过下列方式成为了context_mgr进程:
    int binder_become_context_manager(struct binder_state *bs)
    {
        return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
    }
    int main(int argc, char **argv)
    {
        struct binder_state *bs;
        void *svcmgr = BINDER_SERVICE_MANAGER;
        bs = binder_open(128*1024);
        if (binder_become_context_manager(bs)) {
            LOGE("cannot become context manager (%s)\n", strerror(errno));
            return -1;
        }
        svcmgr_handle = svcmgr;
        binder_loop(bs, svcmgr_handler);
        return 0;
    }
    o 如何得到服务对象的handle
  • 服务提供者通过defaultServiceManager得到ServiceManager对象,然后调用addService向服务管理器注册。
  • 服务使用者通过defaultServiceManager得到ServiceManager对象,然后调用getService通过服务名称查找到服务对象的handle。
    o 如何通过服务对象的handle找到服务所在的进程
    0表示服务管理器的handle,getService可以查找到系统服务的handle。这个handle只是代表了服务对象,内核模块是如何通过handle找到服务所在的进程的呢?
  • 对于ServiceManager: ServiceManager调用了binder_become_context_manager使用自己成为context_mgr,所有handle为0的请求都会被转发给ServiceManager。
  • 对于系统服务和应用程序的Listener,在第一次请求内核模块时(比如调用addService),内核模块在一个RB树中建立了服务对象和进程的对应关系。
            off_end = (void *)offp + tr->offsets_size;
            for (; offp  t->buffer->data_size - sizeof(*fp)) {
                            binder_user_error("binder: %d:%d got transaction with "
                                    "invalid offset, %d\n",
                                    proc->pid, thread->pid, *offp);
                            return_error = BR_FAILED_REPLY;
                            goto err_bad_offset;
                    }
                    fp = (struct flat_binder_object *)(t->buffer->data + *offp);
                    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);
                            }
                            if (fp->cookie != node->cookie) {
                                    binder_user_error("binder: %d:%d sending u%p "
                                            "node %d, cookie mismatch %p != %p\n",
                                            proc->pid, thread->pid,
                                            fp->binder, node->debug_id,
                                            fp->cookie, node->cookie);
                                    goto err_binder_get_ref_for_node_failed;
                            }
                            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->handle = ref->desc;
                            binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo);
                            if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
                                    printk(KERN_INFO "        node %d u%p -> ref %d desc %d\n",
                                           node->debug_id, node->ptr, ref->debug_id, ref->desc);
                    } break;
  • 请求服务时,内核先通过handle找到对应的进程,然后把请求放到服务进程的队列中。
    o C调用JAVA
    前面我们分析的是C代码的处理。对于JAVA代码,JAVA调用C的函数通过JNI调用即可。从内核时读取请求是在C代码(executeCommand)里进行了,那如何在C代码中调用那些用JAVA实现的服务呢?
    android_os_Binder_init里的JavaBBinder对Java里的Binder对象进行包装。
    JavaBBinder::onTransact调用Java里的execTransact函数:
            jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
                code, (int32_t)&data, (int32_t)reply, flags);
            jthrowable excep = env->ExceptionOccurred();
            if (excep) {
                report_exception(env, excep,
                    "*** Uncaught remote exception!  "
                    "(Exceptions are not yet supported across processes.)");
                res = JNI_FALSE;
                /* clean up JNI local ref -- we don't return to Java code */
                env->DeleteLocalRef(excep);
            }
    o 广播消息
    binder不提供广播消息,不过可以ActivityManagerService服务来实现广播。
    (frameworks/base/core/java/android/app/ActivityManagerNative.java)
    接收广播消息需要实现接口BroadcastReceiver,然后调用ActivityManagerProxy::registerReceiver注册。
    触发广播调用ActivityManagerProxy::broadcastIntent。(应用程序并不直接调用它,而是调用Context对它的包装)
                                                                                                    Category:
    Android
                           
                   
                   
                   
                   

    本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/102957/showart_2020448.html
  • 您需要登录后才可以回帖 登录 | 注册

    本版积分规则 发表回复

      

    北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年举报专区
    中国互联网协会会员  联系我们:huangweiwei@itpub.net
    感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP