专栏名称: 开发者全社区
分享和推送Java/Android方向的技术和文章,让你成为这方面的大牛,让你每天都成长一点。同时,我们也会邀请BAT的大牛分享原创!
目录
相关文章推荐
51好读  ›  专栏  ›  开发者全社区

Android存储系统的架构与设计

开发者全社区  · 公众号  · android  · 2016-12-11 08:24

正文

来源:http://gityuan.com/2016/07/23/android-io-arch/

一、概述

本文讲述 Android 存储系统的 架构 与设计,基于Android 6.0的源码,涉及到最为核心的便是MountService和Vold这两个模块以及之间的交互。为了缩减篇幅,只展示部分核心代码。

MountService :Android Binder服务端,运行在system_server进程,用于跟Vold进行消息通信,比如 MountService Vold 发送挂载SD卡的命令,或者接收到来自 Vold 的外设热插拔事件。MountService作为Binder服务端,那么相应的Binder客户端便是StorageManager,通过binder IPC与MountService交互。

Vold :全称为Volume Daemon,用于管理外部存储设备的Native daemon进程,这是一个非常重要的守护进程,主要由NetlinkManager,VolumeManager,CommandListener这3部分组成。

1.1 模块架构

从模块地角度划分Android整个存储架构:


图解:

  • Linux Kernel:通过 uevent 向Vold的NetlinkManager发送Uevent事件;

  • NetlinkManager :接收来自Kernel的 Uevent 事件,再转发给VolumeManager;

  • VolumeManager :接收来自NetlinkManager的事件,再转发给CommandListener进行处理;

  • CommandListener :接收来自VolumeManager的事件,通过 socket 通信方式发送给MountService;

  • MountService :接收来自CommandListener的事件。

1.2 进程架构

(1)先看看 Java framework层的线程:


MountService运行在system_server进程,这里查询的便是system_server进程的所有子线程,system_server进程承载整个framework所有核心服务,子线程数有很多,这里只列举与MountService模块相关的子线程。

(2)再看看Native层的线程:


Vold作为native守护进程,进程名为"/system/bin/vold",pid=387,通过 ps -t 可查询到该进程下所有的子进程/线程。

小技巧: 有读者可能会好奇,为什么 /system/bin/sdcard 是子进程,而非子线程呢?要回答这个问题,有两个方法,其一就是直接看撸源码,会发现这是通过 fork 方式创建的,而其他子线程都是通过 pthread_create 方式创建的。当然其实还有个更快捷的小技巧,就是直接看上图中的第4列,这一列的含义是 VSIZE ,代表的是进程虚拟地址空间大小,是否共享地址空间,这是进程与线程最大的区别,再来看看/sdcard的VSIZE大小跟父进程不一样,基本可以确实/sdcard是子进程。

(3) 从进程/线程视角来看Android存储架构:


  • Java 层:采用 1个主线程 (system_server) + 3个子线程 (VoldConnector, MountService, CryptdConnector);

  • Native层:采用 1个主线程 (/system/bin/vold) + 3个子线程 (vold) + 1子进程 (/system/bin/sdcard);

注:图中红色字代表的进程/线程名,vold进程通过pthread_create的方式创建的3个子线程名都为vold,图中只是为了便于区别才标注为vold1, vold2, vold3,其实名称都为vold。

Android还可划分为内核空间(Kernel Space)和用户空间(User space),从上图可看出,Android存储系统在User space总共采用9个进程/线程的架构模型。当然,除了这9个进/线程,另外还会在handler消息处理过程中使用到system_server的两个子线程: android.fg android.io

Tips: 同一个模块可以运行在各个不同的进程/线程, 同一个进程可以运行不同模块的代码,所以从进程角度和模块角度划分看到的有所不同的.

为了阐述清楚存储系统的通信架构,主要分为以下4个过程:

  1. MountService发送消息:MountService是如何从向vold守护进程通信;

  2. MountService接收消息:MountService接收到vold发送过来的消息又是如何处理;

  3. Kernel上报事件:当存储设备发生热插拔等事件,kernel是如何通知用户空间的vold;

  4. 不请自来的广播:对于事件往往都是MountService下发,然后再收到底层的回应,但对于有些广播却非如此,而是由底层直接触发,对于MountService来说却是“不请自来”的消息。

限于篇幅过长,本文先讲述前两个过程,下一篇文章再来说说后两个过程。

1.3 类关系图



上图中4个蓝色块便是前面谈到的核心模块。

二、 通信架构

Android存储系统中涉及各个进程间通信,这个架构采用的socket,并没有采用Android binder IPC机制。这样的架构代码大量更少,整体架构逻辑也相对简单,在介绍通信过程前,先来看看MountService对象的实例化过程,那么也就基本明白进程架构中system_sever进程为了MountService服务而单独创建与共享使用到线程情况。

首先,MountService对象实例化的过程中完成是:

  1. 创建ICallbacks回调方法,FgThread线程名为"android.fg",此处用到的Looper便是线程"android.fg"中的Looper;

  2. 创建并启动线程名为"MountService"的handlerThread;

  3. 创建OBB操作的handler,IoThread线程名为"android.io",此处用到的的Looper便是线程"android.io"中的Looper;

  4. 创建NativeDaemonConnector对象

  5. 创建并启动线程名为"VoldConnector"的线程;

  6. 创建并启动线程名为"CryptdConnector"的线程;

  7. 注册监听用户添加、删除的广播;

从这里便可知道共创建了3个线程: MountService , VoldConnector , CryptdConnector ,另外还会使用到系统进程中的两个线程 android.fg android.io . 这便是在文章开头进程架构图中Java framework层进程的创建情况.

2.1 MountService发送消息

system_server进程与vold守护进程间采用socket进行通信,这个通信过程是由MountService线程向vold线程发送消息。这里以执行mount调用为例:

2.1.1 MountService.mount

public void mount(String volId) {
//【见小节2.1.2】
mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId);
}

2.1.2 NDC.execute

execute()经过层层调用到executeForList()



2.1.3 FL.onDataAvailable

MountService线程通过socket发送cmd事件给vold,对于vold守护进程在启动的过程,初始化CommandListener时通过 pthread_create 创建子线程vold来专门监听MountService发送过来的消息,当该线程接收到socket消息时,便会调用onDataAvailable()方法


2.1.4 FL.dispatchCommand


这是用于分发从MountService发送过来的命令,针对不同的命令调用不同的类。在处理过程中遇到下面情况,则会直接发送响应吗500的应答消息给MountService







请到「今天看啥」查看全文