转载

理解Android存储架构

/framework/base/services/java/com/android/server/SystemServer.java /framework/base/services/core/java/com/android/server/MountService.java /framework/base/services/core/java/com/android/server/NativeDaemonConnector.java /framework/base/services/core/java/com/android/server/NativeDaemonEvent.java  /system/vold/Main.cpp /system/vold/VolumeManager.cpp /system/vold/NetlinkManager.cpp /system/vold/NetlinkHandler.cpp /system/vold/CommandListener.cpp /system/vold/VoldCommand.cpp /system/vold/VolumeBase.cpp /system/vold/PublicVolume.cpp /system/vold/EmulatedVolume.cpp /system/vold/PublicVolume.cpp /system/vold/Disk.cpp  /system/core/libsysutils/src/NetlinkListener.cpp /system/core/libsysutils/src/SocketListener.cpp /system/core/libsysutils/src/FrameworkListener.cpp /system/core/libsysutils/src/FrameworkCommand.cpp /system/core/include/sysutils/NetlinkListener.h /system/core/include/sysutils/SocketListener.h /system/core/include/sysutils/FrameworkListener.h /system/core/include/sysutils/FrameworkCommand.h

一、概述

本文主要介绍跟存储相关的模块MountService和Vold,并不涉及底层文件系统。

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

MountService的NativeDaemonConnector(Client端)和 Vold的CL模块(Server端)建立socket通信。

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

1.1 存储架构设计

存储架构从进程/线程视角:

  • Java层采用1个system_server主线程+3个子线程; 另外还会使用到系统进程中的两个线程”android.fg”和”android.io”。
  • Native采用1个vold主线程+3个子线程+1子进程的架构;

先看看Java层的线程:

root@gityuan:/ # ps -t | grep 1212 system    1212  557   2334024 160340 SyS_epoll_ 7faedddbe4 S system_server system    2662  1212  2334024 160340 SyS_epoll_ 7faedddbe4 S MountService system    2663  1212  2334024 160340 unix_strea 7faedde73c S VoldConnector system    2664  1212  2334024 160340 unix_strea 7faedde73c S CryptdConnector ...

再看看Native层的线程:

root@gityuan:/ # ps -t | grep " 387 " USER      PID   PPID  VSIZE  RSS   WCHAN              PC  NAME root      387   1     13572  2912  hrtimer_na 7fa34755d4 S /system/bin/vold root      397   387   13572  2912  poll_sched 7fa3474d1c S vold root      399   387   13572  2912  poll_sched 7fa3474d1c S vold root      400   387   13572  2912  poll_sched 7fa3474d1c S vold media_rw  2702  387   7140   2036  inotify_re 7f84b1d6ac S /system/bin/sdcard

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

Tips: 同一个模块可以运行在各个不同的进程/线程, 同一个进程可以运行不同模块的代码。

1.2 NativeDaemonEvent

响应码 事件类别 对应方法
[100, 200) 部分响应,随后继续产生事件 isClassContinue
[200, 300) 成功响应 isClassOk
[400, 500) 远程服务端错误 isClassServerError
[500, 600) 本地客户端错误 isClassClientError
[600, 700) 远程Vold进程自触发的事件 isClassUnsolicited

例如当操作执行成功,VoldConnector线程能收到类似“RCV <- {200 3 Command succeeded}”的响应事件。

这里在详细列举远程Vold进程的那些”不请自来”的事件,也就是指底层触发的响应码,范围为[600,700)

命令 响应吗
DISK_CREATED 640
DISK_SIZE_CHANGED 641
DISK_LABEL_CHANGED 642
DISK_SCANNED 643
DISK_SYS_PATH_CHANGED 644
DISK_DESTROYED 649
VOLUME_CREATED 650
VOLUME_STATE_CHANGED 651
VOLUME_FS_TYPE_CHANGED 652
VOLUME_FS_UUID_CHANGED 653
VOLUME_FS_LABEL_CHANGED 654
VOLUME_PATH_CHANGED 655
VOLUME_INTERNAL_PATH_CHANGED 656
VOLUME_DESTROYED 659
MOVE_STATUS 660
BENCHMARK_RESULT 661
TRIM_RESULT 662

这些命令主要是针对disk,volume的一系列操作,比如设备创建,状态、路径改变,以及文件类型、uid、标签改变等事件都是底层直接触发。

二、MountService

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

2.1 启动

[-> SystemServer.java]

private void startOtherServices() {     ...     IMountService mountService = null;     //启动MountService服务,【见小节2.2】     mSystemServiceManager.startService(MOUNT_SERVICE_CLASS);     //等价new IMountService.Stub.Proxy(),即获取MountService的proxy对象     mountService = IMountService.Stub.asInterface(             ServiceManager.getService("mount"));     ...      mActivityManagerService.systemReady(new Runnable() {         public void run() {             //启动到阶段550【见小节2.7】             mSystemServiceManager.startBootPhase(                         SystemService.PHASE_ACTIVITY_MANAGER_READY);         ...     }); }

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]

class MountService extends IMountService.Stub         implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {     public static class Lifecycle extends SystemService {         public void onStart() {             //创建MountService对象【见小节2.3】             mMountService = new MountService(getContext());             //登记Binder服务             publishBinderService("mount", mMountService);         }         ...     }     ... }

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

2.3 MountService

public MountService(Context context) {     sSelf = this;      mContext = context;     //FgThread线程名为“"android.fg",创建IMountServiceListener回调方法【见小节2.4】     mCallbacks = new Callbacks(FgThread.get().getLooper());     //获取PKMS的Client端对象     mPms = (PackageManagerService) ServiceManager.getService("package");     //创建“MountService”线程     HandlerThread hthread = new HandlerThread(TAG);     hthread.start();      mHandler = new MountServiceHandler(hthread.getLooper());     //IoThread线程名为"android.io",创建OBB操作的handler     mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());      File dataDir = Environment.getDataDirectory();     File systemDir = new File(dataDir, "system");     mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);     //判断/data/system/last-fstrim文件,不存在则创建,存在则更新最后修改时间     if (!mLastMaintenanceFile.exists()) {         (new FileOutputStream(mLastMaintenanceFile)).close();         ...     } else {         mLastMaintenance = mLastMaintenanceFile.lastModified();     }     ...     //将MountServiceInternalImpl登记到sLocalServiceObjects     LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);     //创建用于VoldConnector的NDC对象【见小节2.5】     mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,             null);     mConnector.setDebug(true);     //创建线程名为"VoldConnector"的线程,用于跟vold通信【见小节2.6】     Thread thread = new Thread(mConnector, VOLD_TAG);     thread.start();      //创建用于CryptdConnector工作的NDC对象     mCryptConnector = new NativeDaemonConnector(this, "cryptd",             MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);     mCryptConnector.setDebug(true);     //创建线程名为"CryptdConnector"的线程,用于加密     Thread crypt_thread = new Thread(mCryptConnector, CRYPTD_TAG);     crypt_thread.start();      //注册监听用户添加、删除的广播     final IntentFilter userFilter = new IntentFilter();     userFilter.addAction(Intent.ACTION_USER_ADDED);     userFilter.addAction(Intent.ACTION_USER_REMOVED);     mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);      //内部私有volume的路径为/data,该volume通过dumpsys mount是不会显示的     addInternalVolume();      //默认为false     if (WATCHDOG_ENABLE) {         Watchdog.getInstance().addMonitor(this);     } }

主要功能依次是:

  1. FgThread线程名为”android.fg”,创建IMountServiceListener回调方法;
  2. 创建并启动线程名为”MountService”的handlerThread;
  3. IoThread线程名为”android.io”,创建OBB操作的handler;
  4. 创建NativeDaemonConnector对象
  5. 创建并启动线程名为”VoldConnector”的线程;
  6. 创建并启动线程名为”CryptdConnector”的线程;
  7. 注册监听用户添加、删除的广播;

该过程总共创建了3个线程:”MountService”,”VoldConnector”,”CryptdConnector”,另外还会使用到系统进程中的两个线程”android.fg”和”android.io”

2.4 Callbacks

class MountService {     ...     private static class Callbacks extends Handler {         private final RemoteCallbackList<IMountServiceListener>                         mCallbacks = new RemoteCallbackList<>();         public Callbacks(Looper looper) {             super(looper);         }         ...     } }

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

[-> RemoteCallbackList.java]

public class RemoteCallbackList<E extends IInterface> {     ArrayMap<IBinder, Callback> mCallbacks             = new ArrayMap<IBinder, Callback>();      //Binder死亡通知     private final class Callback implements IBinder.DeathRecipient {         public void binderDied() {             ...         }     }      //注册死亡回调     public boolean register(E callback, Object cookie) {         synchronized (mCallbacks) {             ...             IBinder binder = callback.asBinder();             Callback cb = new Callback(callback, cookie);             binder.linkToDeath(cb, 0);             mCallbacks.put(binder, cb);             ...         }     }     ... }

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

2.5 NativeDaemonConnector

[-> NativeDaemonConnector.java]

NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,         int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl) {     this(callbacks, socket, responseQueueSize, logTag, maxLogSize, wl,             FgThread.get().getLooper()); }  NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,         int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl,         Looper looper) {     mCallbacks = callbacks;     //socket名为"vold"     mSocket = socket;     //对象响应个数为500     mResponseQueue = new ResponseQueue(responseQueueSize);     mWakeLock = wl;     if (mWakeLock != null) {         mWakeLock.setReferenceCounted(true);     }     mLooper = looper;     mSequenceNumber = new AtomicInteger(0);     //TAG为"VoldConnector"     TAG = logTag != null ? logTag : "NativeDaemonConnector";     mLocalLog = new LocalLog(maxLogSize); }
  • mLooper为FgThread.get().getLooper(),即运行在”android.fg”线程;
  • mResponseQueue对象中成员变量 mPendingCmds 数据类型为LinkedList,记录着vold进程上报的响应事件,事件个数上限为500。

2.6 NDC.run

[-> NativeDaemonConnector.java]

final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {     public void run() {         mCallbackHandler = new Handler(mLooper, this);          while (true) {             try {                 //监听vold的socket【见小节2.7】                 listenToSocket();             } catch (Exception e) {                 loge("Error in NativeDaemonConnector: " + e);                 SystemClock.sleep(5000);             }         }     } }

“VoldConnector”线程建立了”vold“的socket的客户端,通过循环方式不断监听Vold服务端发送过来的消息。 另外,同理还有一个线程“CryptdConnector”也采用类似的方式,建立了“cryptd”的socket客户端,监听Vold中另个线程发送过来的消息。

MountService与NDC都启动,那么接下来到系统启动到达阶段PHASE_ACTIVITY_MANAGER_READY,则调用到onBootPhase方法。

2.7 onBootPhase

[-> MountService.java ::Lifecycle]

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

public static class Lifecycle extends SystemService {     public void onBootPhase(int phase) {         if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {             mMountService.systemReady();         }     } }

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

private void systemReady() {     mSystemReady = true;     mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); }

此处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]

class MountServiceHandler extends Handler {     public void handleMessage(Message msg) {         switch (msg.what) {             case H_SYSTEM_READY: {                 handleSystemReady(); //【见小节2.9】                 break;             }             ...         }     } }

2.9 handleSystemReady

[-> MountService.java]

private void handleSystemReady() {     synchronized (mLock) {         //【见小节2.10】         resetIfReadyAndConnectedLocked();     }      //计划执行日常的fstrim操作【】     MountServiceIdler.scheduleIdlePass(mContext); }

2.10 resetIfReadyAndConnectedLocked

[-> MountService.java]

private void resetIfReadyAndConnectedLocked() {     Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady             + ", mDaemonConnected=" + mDaemonConnected);     //当系统启动到阶段550,并且已经与vold守护进程建立连接,则执行reset     if (mSystemReady && mDaemonConnected) {         killMediaProvider();         mDisks.clear();         mVolumes.clear();          //将/data为路径的private volume添加到mVolumes         addInternalVolume();          try {             //【见小节2.11】             mConnector.execute("volume", "reset");              //告知所有已经存在和启动的users             final UserManager um = mContext.getSystemService(UserManager.class);             final List<UserInfo> users = um.getUsers();             for (UserInfo user : users) {                 mConnector.execute("volume", "user_added", user.id, user.serialNumber);             }             for (int userId : mStartedUsers) {                 mConnector.execute("volume", "user_started", userId);             }         } catch (NativeDaemonConnectorException e) {             Slog.w(TAG, "Failed to reset vold", e);         }     } }

2.11 NDC.execute

[-> NativeDaemonConnector.java]

public NativeDaemonEvent execute(String cmd, Object... args)         throws NativeDaemonConnectorException {     return execute(DEFAULT_TIMEOUT, cmd, args); }

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

public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)         throws NativeDaemonConnectorException {     final long startTime = SystemClock.elapsedRealtime();      final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();      final StringBuilder rawBuilder = new StringBuilder();     final StringBuilder logBuilder = new StringBuilder();      //mSequenceNumber初始化值为0,每执行一次该方法则进行加1操作     final int sequenceNumber = mSequenceNumber.incrementAndGet();      makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);      //例如:“3 volume reset”     final String rawCmd = rawBuilder.toString();     final String logCmd = logBuilder.toString();      log("SND -> {" + logCmd + "}");      synchronized (mDaemonLock) {         //将cmd写入到socket的输出流         mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));         ...     }      NativeDaemonEvent event = null;     do {         //【见小节2.12】         event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd);         events.add(event);     //当收到的事件响应码属于[100,200)区间,则继续等待后续事件上报     } while (event.isClassContinue());      final long endTime = SystemClock.elapsedRealtime();     //对于执行时间超过500ms则会记录到log     if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {         loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");     }     ...     return events.toArray(new NativeDaemonEvent[events.size()]); }

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

2.12 ResponseQueue.remove

[-> MountService.java ::ResponseQueue]

private static class ResponseQueue {     public NativeDaemonEvent remove(int cmdNum, long timeoutMs, String logCmd) {         PendingCmd found = null;         synchronized (mPendingCmds) {             //从mPendingCmds查询cmdNum             for (PendingCmd pendingCmd : mPendingCmds) {                 if (pendingCmd.cmdNum == cmdNum) {                     found = pendingCmd;                     break;                 }             }             //如果已有的mPendingCmds中查询不到,则创建一个新的PendingCmd             if (found == null) {                 found = new PendingCmd(cmdNum, logCmd);                 mPendingCmds.add(found);             }             found.availableResponseCount--;             if (found.availableResponseCount == 0) mPendingCmds.remove(found);         }         NativeDaemonEvent result = null;         try {             //采用poll轮询方式等待底层上报该事件,直到1分钟超时             result = found.responses.poll(timeoutMs, TimeUnit.MILLISECONDS);         } catch (InterruptedException e) {}         return result;     } }

mPendingCmds中的内容是如何添加的呢?其实是在NDC.listenToSocket循环监听到消息时添加的,则接下来看看监听过程。 这里用到poll,先来看看 responses = new ArrayBlockingQueue<NativeDaemonEvent>(10) ,这是一个长度为10的可阻塞队列。 这里的poll也是阻塞的方式来轮询事件。

responses.poll

[-> ArrayBlockingQueue.java]

public E poll(long timeout, TimeUnit unit) throws InterruptedException {     long nanos = unit.toNanos(timeout);     final ReentrantLock lock = this.lock;     //可中断的锁等待     lock.lockInterruptibly();     try {         //当队列长度为空,循环等待         while (count == 0) {             if (nanos <= 0)                 return null;             nanos = notEmpty.awaitNanos(nanos);         }         return extract();     } finally {         lock.unlock();     } }

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

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

ReentrantLock的劣势:

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

2.13 listenToSocket

[-> NativeDaemonConnector.java]

private void listenToSocket() throws IOException {     LocalSocket socket = null;      try {         socket = new LocalSocket();         LocalSocketAddress address = determineSocketAddress();         //建立与"/dev/socket/vold"的socket连接         socket.connect(address);          InputStream inputStream = socket.getInputStream();         synchronized (mDaemonLock) {             mOutputStream = socket.getOutputStream();         }         //建立连接后,回调MS.onDaemonConnected【见小节2.15】         mCallbacks.onDaemonConnected();          byte[] buffer = new byte[BUFFER_SIZE];         int start = 0;          while (true) {             int count = inputStream.read(buffer, start, BUFFER_SIZE - start);             ...              for (int i = 0; i < count; i++) {                 if (buffer[i] == 0) {                     final String rawEvent = new String(                             buffer, start, i - start, StandardCharsets.UTF_8);                      boolean releaseWl = false;                     try {                         //解析socket服务端发送的event                         final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(                                 rawEvent);                          log("RCV <- {" + event + "}");                         //当事件的响应码区间为[600,700),则发送消息交由mCallbackHandler处理                         if (event.isClassUnsolicited()) {                             if (mCallbacks.onCheckHoldWakeLock(event.getCode())                                     && mWakeLock != null) {                                 mWakeLock.acquire();                                 releaseWl = true;                             }                             if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(                                     event.getCode(), event.getRawEvent()))) {                                 releaseWl = false;                             }                         } else {                             //对于其他的响应码则添加到mResponseQueue队列【见小节2.14】                             mResponseQueue.add(event.getCmdNumber(), event);                         }                     } catch (IllegalArgumentException e) {                         log("Problem parsing message " + e);                     } finally {                         if (releaseWl) {                             mWakeLock.acquire();                         }                     }                     start = i + 1;                 }             }             ...         }     } catch (IOException ex) {         throw ex;     } finally {         //收尾清理类工作,关闭mOutputStream, socket         ...     } }

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

2.14 ResponseQueue.add

[-> NativeDaemonConnector.java]

private static class ResponseQueue {     public void add(int cmdNum, NativeDaemonEvent response) {        PendingCmd found = null;        synchronized (mPendingCmds) {            for (PendingCmd pendingCmd : mPendingCmds) {                if (pendingCmd.cmdNum == cmdNum) {                    found = pendingCmd;                    break;                }            }             //没有找到则创建相应的PendingCmd            if (found == null) {                while (mPendingCmds.size() >= mMaxCount) {                    PendingCmd pendingCmd = mPendingCmds.remove();                }                found = new PendingCmd(cmdNum, null);                mPendingCmds.add(found);            }            found.availableResponseCount++;            if (found.availableResponseCount == 0) mPendingCmds.remove(found);        }        try {            found.responses.put(response);        } catch (InterruptedException e) { }    } }

responses.put

[-> ArrayBlockingQueue.java]

public void put(E e) throws InterruptedException {     checkNotNull(e);     final ReentrantLock lock = this.lock;     lock.lockInterruptibly();     try {         //当队列满了则等待         while (count == items.length)             notFull.await();         insert(e);     } finally {         lock.unlock();     } }

2.15 MS.onDaemonConnected

[-> MountService.java]

public void onDaemonConnected() {     mDaemonConnected = true;     mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget(); }

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

private void handleDaemonConnected() {     synchronized (mLock) {         resetIfReadyAndConnectedLocked();     }      //类型为CountDownLatch,用于多线程同步,阻塞await()直到计数器为零     mConnectedSignal.countDown();     if (mConnectedSignal.getCount() != 0) {         return;     }      //调用PMS来加载ASECs     mPms.scanAvailableAsecs();      //用于通知ASEC扫描已完成     mAsecsScanned.countDown(); }

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

[-> MountService.java]

public String[] getSecureContainerList() {     enforcePermission(android.Manifest.permission.ASEC_ACCESS);     //等待mConnectedSignal计数锁达到零     waitForReady();     //当没有挂载Primary卷设备,则弹出警告     warnOnNotMounted();      try {         //向vold进程发送asec list命令         return NativeDaemonEvent.filterMessageList(                 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult);     } catch (NativeDaemonConnectorException e) {         return new String[0];     } }

三、Vold

Vold是由开机过程中解析init.rc时启动:

on post-fs-data     start vold

Vold的service定义如下:

service vold /system/bin/vold     class core     socket vold stream 0660 root mount     socket cryptd stream 0660 root mount     ioprio be 2

接下来便进入main()方法:

3.1 main

[-> system/vold/Main.cpp]

int main(int argc, char** argv) {     setenv("ANDROID_LOG_TAGS", "*:v", 1);     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));      VolumeManager *vm;     CommandListener *cl;     CryptCommandListener *ccl;     NetlinkManager *nm;      //解析参数,设置contenxt     parse_args(argc, argv);     ...      fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);     fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);      mkdir("/dev/block/vold", 0755);      //用于cryptfs检查,并mount加密的文件系统     klog_set_level(6);      //创建单例对象VolumeManager 【见小节3.2.1】     if (!(vm = VolumeManager::Instance())) {         exit(1);     }      //创建单例对象NetlinkManager 【见小节3.3.1】     if (!(nm = NetlinkManager::Instance())) {         exit(1);     }      if (property_get_bool("vold.debug", false)) {         vm->setDebug(true);     }      // 创建CommandListener对象 【见小节3.4.1】     cl = new CommandListener();     // 创建CryptCommandListener对象 【见小节3.5.1】     ccl = new CryptCommandListener();      //【见小节3.2.2】     vm->setBroadcaster((SocketListener *) cl);     //【见小节3.3.2】     nm->setBroadcaster((SocketListener *) cl);      if (vm->start()) { //【见小节3.2.3】         exit(1);     }      process_config(vm); //【见小节3.2.4】      if (nm->start()) {  //【见小节3.3.3】         exit(1);     }      coldboot("/sys/block");      //启动响应命令的监听器 //【见小节3.4.2】     if (cl->startListener()) {         exit(1);     }      if (ccl->startListener()) {         exit(1);     }      //Vold成为监听线程     while(1) {         sleep(1000);     }      exit(0); }

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

  • VolumeManager
  • NetlinkManager (NetlinkHandler)
  • CommandListener
  • CryptCommandListener

接下来分别说说几个类:

3.2 VolumeManager

3.2.1 创建

[-> VolumeManager.cpp]

VolumeManager *VolumeManager::Instance() {     if (!sInstance)         sInstance = new VolumeManager();     return sInstance; }

创建单例模式的VolumeManager对象

VolumeManager::VolumeManager() {     mDebug = false;     mActiveContainers = new AsecIdCollection();     mBroadcaster = NULL;     mUmsSharingCount = 0;     mSavedDirtyRatio = -1;     //当UMS获取时,则设置dirty ratio为0     mUmsDirtyRatio = 0; }

3.2.2 vm->setBroadcaster

void setBroadcaster(SocketListener *sl) {      mBroadcaster = sl;  }

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

3.2.3 vm->start

int VolumeManager::start() {     //卸载所有设备,以提供最干净的环境     unmountAll();      //创建Emulated内部存储     mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(             new android::vold::EmulatedVolume("/data/media"));     mInternalEmulated->create();     return 0; }

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

3.2.3.1 unmountAll

int VolumeManager::unmountAll() {     std::lock_guard<std::mutex> lock(mLock);      //卸载内部存储     if (mInternalEmulated != nullptr) {         mInternalEmulated->unmount();     }     //卸载外部存储     for (auto disk : mDisks) {         disk->unmountAll();     }      FILE* fp = setmntent("/proc/mounts", "r");     if (fp == NULL) {         return -errno;     }      std::list<std::string> toUnmount;     mntent* mentry;     while ((mentry = getmntent(fp)) != NULL) {         if (strncmp(mentry->mnt_dir, "/mnt/", 5) == 0                 || strncmp(mentry->mnt_dir, "/storage/", 9) == 0) {             toUnmount.push_front(std::string(mentry->mnt_dir));         }     }     endmntent(fp);      for (auto path : toUnmount) {         //强制卸载         android::vold::ForceUnmount(path);     }      return 0; }

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

/dev/fuse /mnt/runtime/default/emulated fuse rw,nosuid,nodev,...

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

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

卸载内部存储:

status_t EmulatedVolume::doUnmount() {     if (mFusePid > 0) {         kill(mFusePid, SIGTERM);         TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));         mFusePid = 0;     }      KillProcessesUsingPath(getPath());     //强制卸载fuse路径     ForceUnmount(mFuseDefault);     ForceUnmount(mFuseRead);     ForceUnmount(mFuseWrite);      rmdir(mFuseDefault.c_str());     rmdir(mFuseRead.c_str());     rmdir(mFuseWrite.c_str());      mFuseDefault.clear();     mFuseRead.clear();     mFuseWrite.clear();      return OK; }

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]

status_t VolumeBase::create() {     mCreated = true;     status_t res = doCreate();     //通知VolumeCreated事件     notifyEvent(ResponseCode::VolumeCreated,             StringPrintf("%d /"%s/" /"%s/"", mType, mDiskId.c_str(), mPartGuid.c_str()));     //设置为非挂载状态     setState(State::kUnmounted);     return res; }   void VolumeBase::notifyEvent(int event, const std::string& value) {     if (mSilent) return;     //通过socket向MountService发送创建volume的命令(650)     VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,             StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false); }

3.2.4 process_config(vm)

[-> system/vold/Main.cpp]

static int process_config(VolumeManager *vm) {     //获取Fstab路径     std::string path(android::vold::DefaultFstabPath());     fstab = fs_mgr_read_fstab(path.c_str());     ...      bool has_adoptable = false;     for (int i = 0; i < fstab->num_entries; i++) {         if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {             if (fs_mgr_is_nonremovable(&fstab->recs[i])) {                 LOG(WARNING) << "nonremovable no longer supported; ignoring volume";                 continue;             }              std::string sysPattern(fstab->recs[i].blk_device);             std::string nickname(fstab->recs[i].label);             int flags = 0;              if (fs_mgr_is_encryptable(&fstab->recs[i])) {                 flags |= android::vold::Disk::Flags::kAdoptable;                 has_adoptable = true;             }             if (fs_mgr_is_noemulatedsd(&fstab->recs[i])                     || property_get_bool("vold.debug.default_primary", false)) {                 flags |= android::vold::Disk::Flags::kDefaultPrimary;             }              vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(                     new VolumeManager::DiskSource(sysPattern, nickname, flags)));         }     }     property_set("vold.has_adoptable", has_adoptable ? "1" : "0");     return 0; }

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]

NetlinkManager *NetlinkManager::Instance() {     if (!sInstance)         sInstance = new NetlinkManager();     return sInstance; }

3.3.2 nm->setBroadcaster

void setBroadcaster(SocketListener *sl) {     mBroadcaster = sl; }

3.3.3 nm->start

int NetlinkManager::start() {     struct sockaddr_nl nladdr;     int sz = 64 * 1024;     int on = 1;      memset(&nladdr, 0, sizeof(nladdr));     nladdr.nl_family = AF_NETLINK;     nladdr.nl_pid = getpid(); //记录当前进程的pid     nladdr.nl_groups = 0xffffffff;      //创建event socket     if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,             NETLINK_KOBJECT_UEVENT)) < 0) {         return -1;     }      //设置uevent的SO_RCVBUFFORCE选项     if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {         goto out;     }      //设置uevent的SO_PASSCRED选项     if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {         goto out;     }     //绑定uevent socket     if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {         goto out;     }      //创建NetlinkHandler【见小节3.3.4】     mHandler = new NetlinkHandler(mSock);     //启动NetlinkHandler【见小节3.3.5】     if (mHandler->start()) {         goto out;     }      return 0;  out:     close(mSock);     return -1; }

3.3.4 NetlinkHandler

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

[-> SocketListener.cpp]

SocketListener::SocketListener(int socketFd, bool listen) {     //listen=false     init(NULL, socketFd, listen, false); }  void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {     mListen = listen;     mSocketName = socketName;     //用于监听Kernel发送过程的uevent事件     mSock = socketFd;     mUseCmdNum = useCmdNum;     //初始化同步锁     pthread_mutex_init(&mClientsLock, NULL);     //创建socket通信的client端     mClients = new SocketClientCollection(); }

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

3.3.5 NH->start

[-> NetlinkHandler.cpp]

int NetlinkHandler::start() {     return this->startListener(); }

[-> SocketListener.cpp]

int SocketListener::startListener() {     return startListener(4); }  int SocketListener::startListener(int backlog) {     ...     //mListen =false     if (mListen && listen(mSock, backlog) < 0) {         return -1;     } else if (!mListen)         //创建SocketClient对象,并加入到mClients队列         mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));      //创建匿名管道     if (pipe(mCtrlPipe)) {         return -1;     }      //创建工作线程,线程运行函数threadStart【见小节3.3.6】     if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {         return -1;     }      return 0; }

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

3.3.6 threadStart

[-> SocketListener.cpp]

void *SocketListener::threadStart(void *obj) {     SocketListener *me = reinterpret_cast<SocketListener *>(obj);     //【见小节3.3.7】     me->runListener();     pthread_exit(NULL); //线程退出     return NULL; }

3.3.7 SL->runListener

[-> SocketListener.cpp]

void SocketListener::runListener() {     SocketClientCollection pendingList;     while(1) {         SocketClientCollection::iterator it;         fd_set read_fds;         int rc = 0;         int max = -1;          FD_ZERO(&read_fds);          if (mListen) {             max = mSock;             FD_SET(mSock, &read_fds);         }          FD_SET(mCtrlPipe[0], &read_fds);         if (mCtrlPipe[0] > max)             max = mCtrlPipe[0];          pthread_mutex_lock(&mClientsLock);         for (it = mClients->begin(); it != mClients->end(); ++it) {             // NB: calling out to an other object with mClientsLock held (safe)             int fd = (*it)->getSocket();             FD_SET(fd, &read_fds);             if (fd > max) {                 max = fd;             }         }         pthread_mutex_unlock(&mClientsLock);         SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);         if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {             if (errno == EINTR)                 continue;             SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);             sleep(1);             continue;         } else if (!rc)             continue;          if (FD_ISSET(mCtrlPipe[0], &read_fds)) {             char c = CtrlPipe_Shutdown;             TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));             if (c == CtrlPipe_Shutdown) {                 break;             }             continue;         }         if (mListen && FD_ISSET(mSock, &read_fds)) {             struct sockaddr addr;             socklen_t alen;             int c;              do {                 alen = sizeof(addr);                 c = accept(mSock, &addr, &alen);                 SLOGV("%s got %d from accept", mSocketName, c);             } while (c < 0 && errno == EINTR);             if (c < 0) {                 SLOGE("accept failed (%s)", strerror(errno));                 sleep(1);                 continue;             }             fcntl(c, F_SETFD, FD_CLOEXEC);             pthread_mutex_lock(&mClientsLock);             mClients->push_back(new SocketClient(c, true, mUseCmdNum));             pthread_mutex_unlock(&mClientsLock);         }          /* Add all active clients to the pending list first */         pendingList.clear();         pthread_mutex_lock(&mClientsLock);         for (it = mClients->begin(); it != mClients->end(); ++it) {             SocketClient* c = *it;             // NB: calling out to an other object with mClientsLock held (safe)             int fd = c->getSocket();             if (FD_ISSET(fd, &read_fds)) {                 pendingList.push_back(c);                 c->incRef();             }         }         pthread_mutex_unlock(&mClientsLock);          /* Process the pending list, since it is owned by the thread,          * there is no need to lock it */         while (!pendingList.empty()) {             /* Pop the first item from the list */             it = pendingList.begin();             SocketClient* c = *it;             pendingList.erase(it);             /* Process it, if false is returned, remove from list */             if (!onDataAvailable(c)) {                 release(c, false);             }             c->decRef();         }     } }

3.4 CommandListener

3.4.1 创建

[-> CommandListener.cpp]

CommandListener::CommandListener() :                  FrameworkListener("vold", true) {     registerCmd(new DumpCmd());     registerCmd(new VolumeCmd());     registerCmd(new AsecCmd());     registerCmd(new ObbCmd());     registerCmd(new StorageCmd());     registerCmd(new FstrimCmd()); }

3.4.1.1 FrameworkListener

[-> FrameworkListener.cpp]

FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :                             SocketListener(socketName, true, withSeq) {     init(socketName, withSeq); }  void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {     mCommands = new FrameworkCommandCollection();     errorRate = 0;     mCommandCount = 0;     mWithSeq = withSeq; //true }

3.4.1.2 SocketListener

[-> SocketListener.cpp]

SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {     init(socketName, -1, listen, useCmdNum); }  void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {     mListen = listen; //true     mSocketName = socketName; //"vold"     mSock = socketFd; // -1     mUseCmdNum = useCmdNum; //true     pthread_mutex_init(&mClientsLock, NULL);     mClients = new SocketClientCollection(); }

socket名为“vold”

3.4.1.3 registerCmd

void FrameworkListener::registerCmd(FrameworkCommand *cmd) {     mCommands->push_back(cmd); }  CommandListener::VolumeCmd::VolumeCmd() :              VoldCommand("volume") { }

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

3.4.2 cl->startListener

int SocketListener::startListener() {     return startListener(4); }  int SocketListener::startListener(int backlog) {      if (!mSocketName && mSock == -1) {         ...     } else if (mSocketName) {         //获取“vold”所对应的句柄         if ((mSock = android_get_control_socket(mSocketName)) < 0) {             return -1;         }         fcntl(mSock, F_SETFD, FD_CLOEXEC);     }      //CL开始监听     if (mListen && listen(mSock, backlog) < 0) {         return -1;     }     ...     //创建匿名管道     if (pipe(mCtrlPipe)) {         return -1;     }      //创建工作线程     if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {         return -1;     }      return 0; }

四、 案例分析

MountService根据收到消息会发送VM处理。 例如待SD卡插入后,VM会将(来自NM的“Disk Insert”的)消息发送给MountService,而后MountService则发送“Mount”指令给Vold,指示它挂载这个SD卡。有些应用程序需要检测外部存储卡的插入/拔出事件,这些事件是由MountService通过Intent广播发出的,例如外部存储卡插入后,MountService就会发送ACTION_MEDIA_MOUNTED消息。

4.1 Kernel上报处理流程

插入SD卡动作: 通过硬件驱动会引起Kernel向NM发送uevent,NM转发消息给VM,再通过CL发送给MountService;MountService收到后则向Vold发送Mount命令,来挂载SD卡。

整个流程: Kernel Uevent -> volumeManager -> NetlinkManager -> CommandListener -> NativeDaemonConnector -> MountService

Kernel发出Uevnt事件 SocketListener::startListener SocketListener::threadStart SocketListener::runListener NetlinkListener.onDataAvailable NetlinkHandler.onEvent VM.handleBlockEvent Disk.create Disk::notifyEvent SocketListener::sendBroadcast 发送socket事件给上层

4.2 framework下发处理流程

MountServic发送socket,执行mount SocketListener::startListener SocketListener::threadStart SocketListener::runListener FrameworkListener::onDataAvailable FrameworkListener::dispatchCommand VolumeCmd.runCommand VolumeBase.mount EmulatedVolume.doMount(内置) PublicVolume.doMount(外置) vfat::Check vfat::Mount fork (/sdcard)

4.3 startUser流程

AMS.systemReady goingCallback.run(); onBootPhase(550) MountService.systemReady(); => mSystemReady = true MountService.resetIfReadyAndConnectedLocked mConnector.execute(“volume”, “reset”); RCV <- {650 emulated –> VOLUME_CREATED RCV <- {650 public –> VOLUME_CREATED mConnector.execute(“volume”, “user_added”, user.id, user.serialNumber); mConnector.execute(“volume”, “user_started”, userId);

mSystemServiceManager.startUser(mCurrentUserId);     MountService.onStartUser         mConnector.execute("volume", "user_started", userId);         mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();             MountServiceHandler.handleMessage -->case H_VOLUME_BROADCAS

NDC监听流程:

NDC.istenToSocket      NativeDaemonConnector.handleMessage -->         MountService.onEvent             onEventLocked -->                 case VoldResponseCode.VOLUME_CREATED                     MountService.onVolumeCreatedLocked                         MountServiceHandler.handleMessage -->                             case H_VOLUME_MOUNT                                 mConnector.execute("volume", "mount");

五、未完待续

本文还在整理中,已经深夜了,有点乱,后面再整理。。。

欢迎关注我的微博: Gityuan 。如果觉得我的文章对您所有帮助,请 ¥打赏支持

,或者点击下方分享给更多的朋友。您的支持将激励我创作更多技术干货!

原文  http://gityuan.com/2016/07/17/android-io/
正文到此结束
Loading...