Android 存储系统之源码篇

基于Android 6.0源码, 来分析存储相关架构,涉及源码:

一、概述

本文主要介绍跟存储相关的模块MountService和Vold的整体流程与架构设计.

  • MountService:Android Binder服务,运行在system_server进程,用于跟Vold进行消息通信,比如MountServiceVold发送挂载SD卡的命令,或者接收到来自Vold的外设热插拔事件。
  • Vold:全称为Volume Daemon,用于管理外部存储设备的Native守护进程,这是一个非常重要的守护进程,由NetlinkManager,VolumeManager,CommandListener这3部分组成。

二、MountService

MountService运行在system_server进程,在系统启动到阶段PHASE_WAIT_FOR_DEFAULT_DISPLAY后,进入startOtherServices会启动MountService.

2.1 启动

[-> SystemServer.java]

NotificationManagerService依赖于MountService,比如media/usb通知事件,所以需要先启动MountService。此处MOUNT_SERVICE_CLASS=com.android.server.MountService$Lifecycle.

2.2 startService

mSystemServiceManager.startService(MOUNT_SERVICE_CLASS)主要完成3件事:

  • 创建MOUNT_SERVICE_CLASS所指类的Lifecycle对象;
  • 将该对象添加SystemServiceManager的mServices服务列表;
  • 最后调用Lifecycle的onStart()方法,主要工作量这个过程,如下:

[-> MountService.java]

创建MountService对象,并向Binder服务的大管家ServiceManager登记,该服务名为“mount”,对应服务对象为mMountService。登记之后,其他地方当需要MountService的服务时便可以通过服务名来向ServiceManager来查询具体的MountService服务。

2.3 MountService

[-> MountService.java]

其主要功能依次是:

  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层进程的创建情况.

接下来再分别看看MountService创建过程中的Callbacks实例化, NativeDaemonConnector实例化,以及”vold”线程的运行.

2.4 Callbacks

创建Callbacks时的Looper为FgThread.get().getLooper(),其中FgThread采用单例模式,是一个线程名为”android.fg”的HandlerThread。另外,Callbacks对象有一个成员变量mCallbacks,如下:

[-> RemoteCallbackList.java]

通过register()方法添加IMountServiceListener对象信息到mCallbacks成员变量。RemoteCallbackList的内部类Callback继承于IBinder.DeathRecipient,很显然这是死亡通知,当binder服务端进程死亡后,回调binderDied方法通知binder客户端进行相应地处理。

2.5 NativeDaemonConnector

[-> NativeDaemonConnector.java]

  • mLooper为FgThread.get().getLooper(),即运行在”android.fg”线程;
  • mResponseQueue对象中成员变量mPendingCmds数据类型为LinkedList,记录着vold进程上报的响应事件,事件个数上限为500。

2.6 NDC.run

[-> NativeDaemonConnector.java]

在线程VoldConnector中建立了名为vold的socket的客户端,通过循环方式不断监听Vold服务端发送过来的消息。 另外,同理还有一个线程CryptdConnector也采用类似的方式,建立了cryptd的socket客户端,监听Vold中另个线程发送过来的消息。到此,MountService与NativeDaemonConnector都已经启动,那么接下来到系统启动到达阶段PHASE_ACTIVITY_MANAGER_READY,则调用到onBootPhase方法。

2.7 onBootPhase

[-> MountService.java ::Lifecycle]

由于MountService的内部Lifecycle已添加SystemServiceManager的mServices服务列表;系统启动到PHASE_ACTIVITY_MANAGER_READY时会回调mServices中的onBootPhase方法

再调用MountService.systemReady方法,该方法主要是通过mHandler发送消息。

此处mHandler = new MountServiceHandler(hthread.getLooper()),采用的是线程”MountService”中的Looper。到此system_server主线程通过handler向线程”MountService”发送H_SYSTEM_READY消息,接下来进入线程”MountService”的MountServiceHandler对象(简称MSH)的handleMessage()来处理相关的消息。

2.8 MSH.handleMessage

[-> MountService.java ::MountServiceHandler]

2.9 handleSystemReady

[-> MountService.java]

2.10 resetIfReadyAndConnectedLocked

[-> MountService.java]

2.11 NDC.execute

[-> NativeDaemonConnector.java]

其中DEFAULT_TIMEOUT=1min,即命令执行超时时长为1分钟。经过层层调用,executeForList()

首先,将带执行的命令mSequenceNumber执行加1操作,再将cmd(例如3 volume reset)写入到socket的输出流,通过循环与poll机制等待执行底层响应该操作结果,否则直到1分钟超时才结束该方法。即便收到底层的响应码,如果响应码属于[100,200)区间,则继续阻塞等待后续事件上报。

2.12 ResponseQueue.remove

[-> MountService.java ::ResponseQueue]

这里用到poll,先来看看responses = new ArrayBlockingQueue<NativeDaemonEvent>(10),这是一个长度为10的可阻塞队列。 这里的poll也是阻塞的方式来轮询事件。

responses.poll

[-> ArrayBlockingQueue.java]

小知识:这里用到了ReentrantLock同步锁,该锁跟synchronized有功能有很相似,用于多线程并发访问。那么ReentrantLock与synchronized相比,

ReentrantLock优势:

  • ReentrantLock功能更为强大,比如有时间锁等候,可中断锁等候(lockInterruptibly),锁投票等功能;
  • ReentrantLock性能更好些;
  • ReentrantLock提供可轮询的锁请求(tryLock),相对不容易产生死锁;而synchronized只要进入,要么成功获取,要么一直阻塞等待。

ReentrantLock的劣势:

  • lock必须在finally块显式地释放,否则如果代码抛出Exception,锁将一直得不到释放;对于synchronized而言,JVM或者ART虚拟机都会确保该锁能自动释放。
  • synchronized锁,在dump线程转储时会记录锁信息,对于分析调试大有裨益;对于Lock来说,只是普通类,虚拟机无法识别。

再回到ResponseQueue.remove(),该方法中mPendingCmds中的内容是哪里添加的呢?其实是在NDC.listenToSocket循环监听到消息时添加的,则接下来看看监听过程。

2.13 listenToSocket

[-> NativeDaemonConnector.java]

这里有一个动作是mResponseQueue.add(),通过该方法便能触发ResponseQueue.poll阻塞操作继续往下执行。

2.14 ResponseQueue.add

[-> NativeDaemonConnector.java]

responses.put

[-> ArrayBlockingQueue.java]

看完了如何向mPendingCmds中增加待处理的命令,再来回过来看看,当当listenToSocket刚开始监听前,收到Native的Daemon连接后的执行操作.

2.15 MS.onDaemonConnected

[-> MountService.java]

当前主线程发送消息H_DAEMON_CONNECTED给线程MountService,该线程收到消息后调用MountServiceHandler的handleMessage()相应分支后,进而调用handleDaemonConnected()方法。

这里的PMS.scanAvailableAsecs()经过层层调用,最终核心工作还是通过MountService.getSecureContainerList。

[-> MountService.java]

2.16 小节

这里以一张简单的流程图来说明上述过程:

volume_reset

三、Vold

介绍完了Java framework层的MountService以及NativeDaemonConnector,往下走来到了Vold的世界.Vold是由开机过程中解析init.rc时启动:

Vold的service定义如下:

接下来便进入Vold的main(),在开启新的征途之前,为了不被代码弄晕,先来用一幅图来介绍下这些核心类之间的关系以及主要方法,以方便更好的往下阅读.

vold

 

volume

3.1 main

[-> system/vold/Main.cpp]

该方法的主要功能是创建下面4个对象并启动

  • VolumeManager
  • NetlinkManager (NetlinkHandler)
  • CommandListener
  • CryptCommandListener

接下来分别说说几个类:

3.2 VolumeManager

3.2.1 创建

[-> VolumeManager.cpp]

创建单例模式的VolumeManager对象

3.2.2 vm->setBroadcaster

将新创建的CommandListener对象sl赋值给vm对象的成员变量mBroadcaster

3.2.3 vm->start

mInternalEmulated的据类型为EmulatedVolume,设备路径为/data/media,id和label为“emulated”,mMountFlags=0。EmulatedVolume继承于VolumeBase

3.2.3.1 unmountAll

此处打开的”/proc/mounts”每一行内容依次是文件名,目录,类型,操作。例如:

该方法的主要工作是卸载:

  • 内部存储mInternalEmulated;
  • 外部存储mDisks,比如sdcard等;
  • “/proc/mounts”中目录包含mnt或者storage的路径;

卸载内部存储:

KillProcessesUsingPath的功能很强大,通过文件path来查看其所在进程,并杀掉相应进程。当以下5处任意一处存在与path相同的地方,则会杀掉相应的进程:

  • proc/<pid>/fd,打开文件;
  • proc/<pid>/maps 打开文件映射;
  • proc/<pid>/cwd 链接文件;
  • proc/<pid>/root 链接文件;
  • proc/<pid>/exe 链接文件;
3.2.3.2 EV.create

[-> VolumeBase.cpp]

3.2.4 process_config(vm)

[-> system/vold/Main.cpp]

Fstab路径:首先通过getprop ro.hardware,比如高通芯片则为qcom那么Fstab路径就是/fstab.qcom,那么该文件的具体内容,例如(当然这个不同手机会有所不同):

src mnt_point type mnt_flags and options fs_mgr_flags
/dev/block/bootdevice/by-name/system /system ext4 ro,barrier=1,discard wait,verify
/dev/block/bootdevice/by-name/userdata /data ext4 nosuid,nodev,barrier=1,noauto_da_alloc,discard wait,check,forceencrypt=footer
/dev/block/bootdevice/by-name/cust /cust ext4 nosuid,nodev,barrier=1 wait,check
/devices/soc.0/7864900.sdhci/mmc_host* /storage/sdcard1 vfat nosuid,nodev wait,voldmanaged=sdcard1:auto,noemulatedsd,encryptable=footer
/dev/block/bootdevice/by-name/config /frp emmc defaults defaults
/devices/platform/msm_hsusb* /storage/usbotg vfat nosuid,nodev wait,voldmanaged=usbotg:auto,encryptable=footer

3.3 NetlinkManager

3.3.1 创建

[-> NetlinkManager.cpp]

3.3.2 nm->setBroadcaster

3.3.3 nm->start

3.3.4 NetlinkHandler

NetlinkHandler继承于NetlinkListenerNetlinkListener继承于SocketListener。new NetlinkHandler(mSock)中参数mSock是用于与Kernel进行通信的socket对象。由于这个继承关系,当NetlinkHandler初始化时会调用基类的初始化,最终调用到:

[-> SocketListener.cpp]

到此,mListen = false; mSocketName = NULL; mUseCmdNum = false。 另外,这里用到的同步锁,用于控制多线程并发访问。 接着在来看看start过程:

3.3.5 NH->start

[-> NetlinkHandler.cpp]

[-> SocketListener.cpp]

mCtrlPipe是匿名管道,这是一个二元数组,mCtrlPipe[0]从管道读数据,mCtrlPipe[1]从管道写数据。

3.3.6 threadStart

[-> SocketListener.cpp]

3.3.7 SL->runListener

[-> SocketListener.cpp]

3.4 CommandListener

3.4.1 创建

[-> CommandListener.cpp]

3.4.1.1 FrameworkListener

[-> FrameworkListener.cpp]

3.4.1.2 SocketListener

[-> SocketListener.cpp]

socket名为“vold”

3.4.1.3 registerCmd

创建这些对象 DumpCmd,VolumeCmd,AsecCmd,ObbCmd,StorageCmd,FstrimCmd,并都加入到mCommands队列。

3.4.2 cl->startListener

四、小结

  • Linux Kernel:通过uevent向Vold的NetlinkManager发送Uevent事件;
  • NetlinkManager:接收来自Kernel的Uevent事件,再转发给VolumeManager;
  • VolumeManager:接收来自NetlinkManager的事件,再转发给CommandListener进行处理;
  • CommandListener:接收来自VolumeManager的事件,通过socket通信方式发送给MountService;
  • MountService:接收来自CommandListener的事件。

本文从源码视角主要介绍了相关模块的创建与启动过程以及部分流程的介绍。要想更进一步了解,Android存储系统之架构篇.

作者微博:@Gityuan

原文出处:Gityuan.com

1 收藏 评论

关于作者:gityuan

Android系统工程师,www.gityuan.com博主,个人新浪微博:http://weibo.com/gityuan 个人主页 · 我的文章 · 3 ·     

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部