扣丁书屋

Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析

在前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析中,我们系统地介绍了Android系统匿名共享内存的实现原理,其中着重介绍了它...

在前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析中,我们系统地介绍了Android系统匿名共享内存的实现原理,其中着重介绍了它是如何辅助内存管理系统来有效地管理内存的,在再前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划中,我们还提到,Android系统匿名共享内存的另外一特点是通过Binder进程间通信机制来实现进程间共享的,本文中,将详细介绍Android系统匿名共享内存是如何使用Binder进程间通信机制来实现进程间共享的。

由于Android系统匿名共享内存在进程间共享的原理涉及到Binder进程间通信机制的相关知识,所以希望读者在继续阅读本文之前,最好对Android系统的Binder进程间通信机制有一定的了解,具体可以参考Android进程间通信(IPC)机制Binder简要介绍和学习计划这篇文章。

Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划这篇文章中,我们举了一个例子来简要地介绍了Android系统的匿名共享内存机制及其使用方法,在这篇文章中,我们继续以这个实例来说明Android系统的匿名共享内存是如何使用Binder进程间通信机制来实现进程间共享的。为了方便描述,结合前面的Binder进程间通信机制知识,我们通过下面这个序列图来总结这个实例中的匿名共享内存文件的文件描述符在进程间传输的过程:

这里, 我们需要关注的便是虚线框部分了,它在Binder驱动程序中实现了在两个进程中共享同一个打开文件的方法。我们知道,在Linux系统中,文件描述符其实就是一个整数。每一个进程在内核空间都有一个打开文件的数组,这个文件描述符的整数值就是用来索引这个数组的,而且,这个文件描述符只是在本进程内有效,也就是说,在不同的进程中,相同的文件描述符的值,代表的可能是不同的打开文件。因此,在进程间传输文件描述符时,不能简要地把一个文件描述符从一个进程传给另外一个进程,中间必须做一过转换,使得这个文件描述在目标进程中是有效的,并且它和源进程的文件描述符所对应的打开文件是一致的,这样才能保证共享。

浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路一文中,我们介绍了用来传输的Binder对象的数据结构struct flat_binder_object,它定义在kernel/common/drivers/staging/android/binder.h 文件中:

/*
     * This is the flattened representation of a Binder object for transfer
     * between processes.  The 'offsets' supplied as part of a binder transaction
     * contains offsets into the data where these structures occur.  The Binder
     * driver takes care of re-writing the structure type and data as it moves
     * between processes.
     */
    struct flat_binder_object {
        /* 8 bytes for large_flat_header. */
        unsigned long       type;
        unsigned long       flags;

        /* 8 bytes of data. */
        union {
            void        *binder;    /* local object */
            signed long handle;     /* remote object */
        };

        /* extra data associated with local object */
        void            *cookie;
    };

域type是一个枚举类型,它的取值范围是:

enum {
        BINDER_TYPE_BINDER  = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
        BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
        BINDER_TYPE_HANDLE  = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
        BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
        BINDER_TYPE_FD      = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
    };

这里我们要介绍的Binder对象的type便是BINDER_TYPE_FD了,要传输的文件描述符的值保存在handle域中。

Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析一文中,我们详细介绍了Binder对象在进程间通信传输的完整过程,这里就不再详述了,有兴趣的读都可以回过头去参考一下。这里,我们只关注文件描述符类型的Binder对象在Binder驱动程序中的相关处理逻辑。

文件描述符类型的Binder对象在Binder驱动程序中的相关处理逻辑实现在binder_transact函数,这个函数定义在kernel/common/drivers/staging/android/binder.c文件中:

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;
        size_t *offp, *off_end;
        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;

        ......


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

        ......

        off_end = (void *)offp + tr->offsets_size;
        for (; offp < off_end; offp++) {
            struct flat_binder_object *fp;
            ......
            fp = (struct flat_binder_object *)(t->buffer->data + *offp);
            switch (fp->type) {
            ......
            case BINDER_TYPE_FD: {
                int target_fd;
                struct file *file;

                if (reply) {
                    if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
                        binder_user_error("binder: %d:%d got reply with fd, %ld, but target does not allow fds\n",
                                        proc->pid, thread->pid, fp->handle);
                        return_error = BR_FAILED_REPLY;
                        goto err_fd_not_allowed;
                    }
                } else if (!target_node->accept_fds) {
                    binder_user_error("binder: %d:%d got transaction with fd, %ld, but target does not allow fds\n",
                                    proc->pid, thread->pid, fp->handle);
                    return_error = BR_FAILED_REPLY;
                    goto err_fd_not_allowed;
                }

                file = fget(fp->handle);
                if (file == NULL) {
                    binder_user_error("binder: %d:%d got transaction with invalid fd, %ld\n",
                                        proc->pid, thread->pid, fp->handle);
                    return_error = BR_FAILED_REPLY;
                    goto err_fget_failed;
                }
                target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
                if (target_fd < 0) {
                    fput(file);
                    return_error = BR_FAILED_REPLY;
                    goto err_get_unused_fd_failed;
                }
                task_fd_install(target_proc, target_fd, file);
                if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)
                        printk(KERN_INFO "        fd %ld -> %d\n", fp->handle, target_fd);
                /* TODO: fput? */
                fp->handle = target_fd;
            } break;

            ......
            }
        }

        ......

    }

这里,我们先明确一下在Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划这篇文章中所举的例子获取匿名共享内存文件的文件描述符的场景。匿名共享内存文件是在Server进程创建的,Client通过IMemoryService.getFileDescriptor去获取Server进程所创建的匿名共享内存文件的文件描述符,Server进程在返回这个文件描述符的过程中进入到Binder驱动程序,即这里的binder_transact函数。因此,这里的当前执行binder_transact函数的进程是Server进程,即源进程是Server进程,而目标进程是Client进程,就是这里的target_proc所表示的进程了。

函数binder_transaction处理文件描述符类型的Binder对象就在中间的for循环里面。

首先是获得Binder对象,并保存在本地变量fp中:

fp = (struct flat_binder_object *)(t->buffer->data + *offp);

文件描述符的值就保存在fp->handle中,通过fget函数取回这个文件描述符所对应的打开文件结构:

file = fget(fp->handle);

这里的file是一个struct file指针,它表示一个打开文件结构。注间,在Linux系统中,打开文件结构struct file是可以在进程间共享的,它与文件描述符不一样。

接着在目标进程中获得一个空闲的文件描述符:

target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);

现在,在目标进程中,打开文件结构有了,文件描述符也有了,接下来就可以把这个文件描述符和这个打开文件结构关联起来就可以了:

task_fd_install(target_proc, target_fd, file);

由于这个Binder对象最终是要返回给目标进程的,所以还要修改fp->handle的值,它原来表示的是在源进程中的文件描述符,现在要改成目标进程的文件描述符:

fp->handle = target_fd;

这样,对文件描述符类型的Binder对象的处理就完成了。目标进程拿到这个文件描述符后,就可以和源进程一起共享打开文件了。

至此,Android系统匿名共享内存利用Binder进程间通信机制来实现进程间共享的学习就结束了,整个Android系统匿名共享内存机制的学习也完成了,希望对读者有所帮助,重新学习Android系统匿名共享内存机制请回到Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划一文。

Dalvik虚拟机Java堆创建过程分析

发布于:2年以前  |  2834次阅读  |  详细内容 »

Chromium扩展(Extension)的Content Script加载过程分析

Chromium的Extension由Page和Content Script组成。Page有UI和JS,它们加载在自己的Extension Process中渲染和执行。Content Script只有JS,这些JS是注入在宿主网页中执行的...

发布于:2年以前  |  2027次阅读  |  详细内容 »

ART运行时Mark-Compact( MC)GC执行过程分析

除了Semi-Space(SS)GC和Generational Semi-Space(GSS)GC,ART运行时还引入了第三种Compacting GC:Mark-Compact(MC)GC。这三种GC虽然都是Compacting GC,不过它们的...

发布于:2年以前  |  2244次阅读  |  详细内容 »

Android系统匿名共享内存Ashmem(Anonymous Shared Memory)在进程间共享的原理分析

在前面一篇文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析中,我们系统地介绍了Android系统匿名共享内存的实现原理,其中着重介绍了它...

发布于:2年以前  |  2389次阅读  |  详细内容 »

Android窗口管理服务WindowManagerService的简要介绍和学习计划

在前一个系列文章中,我们从个体的角度来分析了Android应用程序窗口的实现框架。事实上,如果我们从整体的角度来看,Android应用程序窗口的实现要更复杂,因为它们的类型和...

发布于:2年以前  |  3463次阅读  |  详细内容 »

Android应用程序组件Content Provider的共享数据更新通知机制分析

在Android系统中,应用程序组件Content Provider为不同的应用程序实现数据共享提供了基础设施,它主要通过Binder进程间通信机制和匿名共享内存机制来实现的。关于数据共享...

发布于:2年以前  |  2736次阅读  |  详细内容 »

Dalvik虚拟机为新创建对象分配内存的过程分析

在前面一文中,我们分析了Dalvik虚拟机创建Java堆的过程。有了Java堆之后,Dalvik虚拟机就可以在上面为对象分配内存了。在Java堆为对象分配内存需要解决内存碎片和内存不足...

发布于:2年以前  |  1514次阅读  |  详细内容 »

Dalvik虚拟机JNI方法的注册过程分析

在前面一文中,我们分析了Dalvik虚拟机的运行过程。从中可以知道,Dalvik虚拟机在调用一个成员函数的时候,如果发现该成员函数是一个JNI方法,那么就会直接跳到它的地址去...

发布于:2年以前  |  3065次阅读  |  详细内容 »

ART运行时Compacting GC堆创建过程分析

发布于:2年以前  |  2936次阅读  |  详细内容 »

Android日志系统Logcat源代码简要分析

在前面两篇文章Android日志系统驱动程序Logger源代码分析和Android应用程序框架层和系统运行库层日志系统源代码中,介绍了Android内核空间层、系统运行库层和应用程序框架...

发布于:2年以前  |  2131次阅读  |  详细内容 »

从NDK在非Root手机上的调试原理探讨Android的安全机制

最近都在忙着研究Android的安全攻防技术,好长一段时间没有写博客了,准备回归老本行中--Read the funcking android source code。这两天在看NDK文档的时候,看到一句话&quot;Na...

发布于:2年以前  |  5682次阅读  |  详细内容 »

ART运行时Java堆创建过程分析

发布于:2年以前  |  2840次阅读  |  详细内容 »

Android应用程序注册广播接收器(registerReceiver)的过程分析

前面我们介绍了Android系统的广播机制,从本质来说,它是一种消息订阅/发布机制,因此,使用这种消息驱动模型的第一步便是订阅消息;而对Android应用程序来说,订阅消息其...

发布于:2年以前  |  2179次阅读  |  详细内容 »

Android应用程序启动过程源代码分析

前文简要介绍了Android应用程序的Activity的启动过程。在Android系统中,应用程序是由Activity组成的,因此,应用程序的启动过程实际上就是应用程序中的默认Activity的启动...

发布于:2年以前  |  3675次阅读  |  详细内容 »

Android源代码仓库及其管理工具Repo分析

发布于:2年以前  |  1951次阅读  |  详细内容 »

最多阅读

Android包管理框架:APK的打包流程 2年以前  |  4040次阅读
Android应用程序启动过程源代码分析 2年以前  |  3675次阅读
Android应用程序安装过程源代码分析 2年以前  |  3473次阅读
Android包管理框架:APK的安装流程 2年以前  |  3414次阅读
Dalvik虚拟机垃圾收集(GC)过程分析 2年以前  |  3279次阅读
Android应用优化:启动优化 2年以前  |  3184次阅读
Dalvik虚拟机JNI方法的注册过程分析 2年以前  |  3065次阅读

手机扫码阅读