理解Android存储架构
<div class="source">
<i style="float:left;">原文</i>
<a class="cut cut70" href="http://gityuan.com/2016/07/17/android-io/?utm_source=tuicool&utm_medium=referral" style="display:inline-block;">http://gityuan.com/2016/07/17/android-io/</a>
</div>
<div>
<span>主题</span>
<a href="/topics/11100081" target="_blank">
<span class="new-label">Socket</span>
</a>
</div>
</div>
<div class="article_body" id="nei">
<div>
/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进行消息通信,比如MountService
向Vold
发送挂载SD卡的命令,或者接收到来自Vold
的外设热插拔事件。 - Vold(全称为Volume Daemon),用于管理外部存储设备的Native守护进程,这是一个非常重要的守护进程,由NetlinkManager,VolumeManager,CommandListener这3部分组成。
MountService的NativeDaemonConnector(Client端)和 Vold的CL模块(Server端)建立socket通信。
- Linux Kernel:通过uevent向Vold的NetlinkManager发送Uevent事件;
-
NetlinkManager:接收来自Kernel的Uevent
事件,再转发给VolumeManager; - VolumeManager: 接收来自NetlinkManager的事件,再转发给CommandListener进行处理;
-
CommandListener: 接收来自VolumeManager的事件,通过socket
通信方式发送给MountService; - 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(<span class="hljs-keyword">new</span> Runnable() {
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
<span class="hljs-comment">//启动到阶段550【见小节2.7】</span>
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;
<span class="hljs-comment">//FgThread线程名为“"android.fg",创建IMountServiceListener回调方法【见小节2.4】</span>
mCallbacks = <span class="hljs-keyword">new</span> Callbacks(FgThread.get().getLooper());
<span class="hljs-comment">//获取PKMS的Client端对象</span>
mPms = (PackageManagerService) ServiceManager.getService(<span class="hljs-string">"package"</span>);
<span class="hljs-comment">//创建“MountService”线程</span>
HandlerThread hthread = <span class="hljs-keyword">new</span> HandlerThread(TAG);
hthread.start();
mHandler = <span class="hljs-keyword">new</span> MountServiceHandler(hthread.getLooper());
<span class="hljs-comment">//IoThread线程名为"android.io",创建OBB操作的handler</span>
mObbActionHandler = <span class="hljs-keyword">new</span> ObbActionHandler(IoThread.get().getLooper());
<span class="hljs-keyword">File</span> dataDir = Environment.getDataDirectory();
<span class="hljs-keyword">File</span> systemDir = <span class="hljs-keyword">new</span> <span class="hljs-keyword">File</span>(dataDir, <span class="hljs-string">"system"</span>);
mLastMaintenanceFile = <span class="hljs-keyword">new</span> <span class="hljs-keyword">File</span>(systemDir, LAST_FSTRIM_FILE);
<span class="hljs-comment">//判断/data/system/last-fstrim文件,不存在则创建,存在则更新最后修改时间</span>
<span class="hljs-keyword">if</span> (!mLastMaintenanceFile.exists()) {
(<span class="hljs-keyword">new</span> FileOutputStream(mLastMaintenanceFile)).close();
...
} <span class="hljs-keyword">else</span> {
mLastMaintenance = mLastMaintenanceFile.lastModified();
}
...
<span class="hljs-comment">//将MountServiceInternalImpl登记到sLocalServiceObjects</span>
LocalServices.addService(MountServiceInternal.<span class="hljs-keyword">class</span>, mMountServiceInternal);
<span class="hljs-comment">//创建用于VoldConnector的NDC对象【见小节2.5】</span>
mConnector = <span class="hljs-keyword">new</span> NativeDaemonConnector(<span class="hljs-keyword">this</span>, <span class="hljs-string">"vold"</span>, MAX_CONTAINERS * <span class="hljs-number">2</span>, VOLD_TAG, <span class="hljs-number">25</span>,
<span class="hljs-keyword">null</span>);
mConnector.setDebug(<span class="hljs-keyword">true</span>);
<span class="hljs-comment">//创建线程名为"VoldConnector"的线程,用于跟vold通信【见小节2.6】</span>
Thread thread = <span class="hljs-keyword">new</span> Thread(mConnector, VOLD_TAG);
thread.start();
<span class="hljs-comment">//创建用于CryptdConnector工作的NDC对象</span>
mCryptConnector = <span class="hljs-keyword">new</span> NativeDaemonConnector(<span class="hljs-keyword">this</span>, <span class="hljs-string">"cryptd"</span>,
MAX_CONTAINERS * <span class="hljs-number">2</span>, CRYPTD_TAG, <span class="hljs-number">25</span>, <span class="hljs-keyword">null</span>);
mCryptConnector.setDebug(<span class="hljs-keyword">true</span>);
<span class="hljs-comment">//创建线程名为"CryptdConnector"的线程,用于加密</span>
Thread crypt_thread = <span class="hljs-keyword">new</span> Thread(mCryptConnector, CRYPTD_TAG);
crypt_thread.start();
<span class="hljs-comment">//注册监听用户添加、删除的广播</span>
<span class="hljs-keyword">final</span> IntentFilter userFilter = <span class="hljs-keyword">new</span> IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
userFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mUserReceiver, userFilter, <span class="hljs-keyword">null</span>, mHandler);
<span class="hljs-comment">//内部私有volume的路径为/data,该volume通过dumpsys mount是不会显示的</span>
addInternalVolume();
<span class="hljs-comment">//默认为false</span>
<span class="hljs-keyword">if</span> (WATCHDOG_ENABLE) {
Watchdog.getInstance().addMonitor(<span class="hljs-keyword">this</span>);
}
}
主要功能依次是:
- FgThread线程名为”android.fg”,创建IMountServiceListener回调方法;
- 创建并启动线程名为”MountService”的handlerThread;
- IoThread线程名为”android.io”,创建OBB操作的handler;
- 创建NativeDaemonConnector对象
- 创建并启动线程名为”VoldConnector”的线程;
- 创建并启动线程名为”CryptdConnector”的线程;
- 注册监听用户添加、删除的广播;
该过程总共创建了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>();
<span class="hljs-comment">//Binder死亡通知</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Callback</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">IBinder</span>.<span class="hljs-title">DeathRecipient</span> </span>{
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">binderDied</span><span class="hljs-params">()</span> </span>{
...
}
}
<span class="hljs-comment">//注册死亡回调</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">register</span><span class="hljs-params">(E callback, Object cookie)</span> </span>{
<span class="hljs-keyword">synchronized</span> (mCallbacks) {
...
IBinder binder = callback.asBinder();
Callback cb = <span class="hljs-keyword">new</span> Callback(callback, cookie);
binder.linkToDeath(cb, <span class="hljs-number">0</span>);
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);
<span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) {
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">//监听vold的socket【见小节2.7】</span>
listenToSocket();
} <span class="hljs-keyword">catch</span> (Exception e) {
loge(<span class="hljs-string">"Error in NativeDaemonConnector: "</span> + e);
SystemClock.sleep(<span class="hljs-number">5000</span>);
}
}
}
}
“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();
}
<span class="hljs-comment">//计划执行日常的fstrim操作【】</span>
<span class="hljs-selector-tag">MountServiceIdler</span><span class="hljs-selector-class">.scheduleIdlePass</span>(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();
<span class="hljs-comment">//将/data为路径的private volume添加到mVolumes</span>
addInternalVolume();
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">//【见小节2.11】</span>
mConnector.execute(<span class="hljs-string">"volume"</span>, <span class="hljs-string">"reset"</span>);
<span class="hljs-comment">//告知所有已经存在和启动的users</span>
<span class="hljs-keyword">final</span> UserManager um = mContext.getSystemService(UserManager.<span class="hljs-keyword">class</span>);
<span class="hljs-keyword">final</span> List<UserInfo> users = um.getUsers();
<span class="hljs-keyword">for</span> (UserInfo <span class="hljs-string">user :</span> users) {
mConnector.execute(<span class="hljs-string">"volume"</span>, <span class="hljs-string">"user_added"</span>, user.id, user.serialNumber);
}
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> <span class="hljs-string">userId :</span> mStartedUsers) {
mConnector.execute(<span class="hljs-string">"volume"</span>, <span class="hljs-string">"user_started"</span>, userId);
}
} <span class="hljs-keyword">catch</span> (NativeDaemonConnectorException e) {
Slog.w(TAG, <span class="hljs-string">"Failed to reset vold"</span>, 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();
<span class="hljs-keyword">final</span> ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
<span class="hljs-keyword">final</span> StringBuilder rawBuilder = <span class="hljs-keyword">new</span> StringBuilder();
<span class="hljs-keyword">final</span> StringBuilder logBuilder = <span class="hljs-keyword">new</span> StringBuilder();
<span class="hljs-comment">//mSequenceNumber初始化值为0,每执行一次该方法则进行加1操作</span>
<span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> sequenceNumber = mSequenceNumber.incrementAndGet();
makeCommand(rawBuilder, logBuilder, sequenceNumber, cmd, args);
<span class="hljs-comment">//例如:“3 volume reset”</span>
<span class="hljs-keyword">final</span> String rawCmd = rawBuilder.toString();
<span class="hljs-keyword">final</span> String logCmd = logBuilder.toString();
log(<span class="hljs-string">"SND -> {"</span> + logCmd + <span class="hljs-string">"}"</span>);
<span class="hljs-keyword">synchronized</span> (mDaemonLock) {
<span class="hljs-comment">//将cmd写入到socket的输出流</span>
mOutputStream.<span class="hljs-keyword">write</span>(rawCmd.getBytes(StandardCharsets.UTF_8));
...
}
NativeDaemonEvent event = <span class="hljs-keyword">null</span>;
<span class="hljs-keyword">do</span> {
<span class="hljs-comment">//【见小节2.12】</span>
event = mResponseQueue.remove(sequenceNumber, timeoutMs, logCmd);
events.add(event);
<span class="hljs-comment">//当收到的事件响应码属于[100,200)区间,则继续等待后续事件上报</span>
} <span class="hljs-keyword">while</span> (event.isClassContinue());
<span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> endTime = SystemClock.elapsedRealtime();
<span class="hljs-comment">//对于执行时间超过500ms则会记录到log</span>
<span class="hljs-keyword">if</span> (endTime - startTime > WARN_EXECUTE_DELAY_MS) {
loge(<span class="hljs-string">"NDC Command {"</span> + logCmd + <span class="hljs-string">"} took too long ("</span> + (endTime - startTime) + <span class="hljs-string">"ms)"</span>);
}
...
<span class="hljs-keyword">return</span> events.toArray(<span class="hljs-keyword">new</span> NativeDaemonEvent[events.<span class="hljs-keyword">size</span>()]);
}
首先,将带执行的命令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;
<span class="hljs-keyword">try</span> {
socket = <span class="hljs-keyword">new</span> LocalSocket();
LocalSocketAddress address = determineSocketAddress();
<span class="hljs-comment">//建立与"/dev/socket/vold"的socket连接</span>
socket.connect(address);
InputStream inputStream = socket.getInputStream();
synchronized (mDaemonLock) {
mOutputStream = socket.getOutputStream();
}
<span class="hljs-comment">//建立连接后,回调MS.onDaemonConnected【见小节2.15】</span>
mCallbacks.onDaemonConnected();
<span class="hljs-keyword">byte</span>[] buffer = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[BUFFER_SIZE];
<span class="hljs-keyword">int</span> start = <span class="hljs-number">0</span>;
<span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
<span class="hljs-keyword">int</span> count = inputStream.read(buffer, start, BUFFER_SIZE - start);
...
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < count; i++) {
<span class="hljs-keyword">if</span> (buffer[i] == <span class="hljs-number">0</span>) {
final String rawEvent = <span class="hljs-keyword">new</span> String(
buffer, start, i - start, StandardCharsets.UTF_8);
boolean releaseWl = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">//解析socket服务端发送的event</span>
final NativeDaemonEvent <span class="hljs-keyword">event</span> = NativeDaemonEvent.parseRawEvent(
rawEvent);
log(<span class="hljs-string">"RCV <- {"</span> + <span class="hljs-keyword">event</span> + <span class="hljs-string">"}"</span>);
<span class="hljs-comment">//当事件的响应码区间为[600,700),则发送消息交由mCallbackHandler处理</span>
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">event</span>.isClassUnsolicited()) {
<span class="hljs-keyword">if</span> (mCallbacks.onCheckHoldWakeLock(<span class="hljs-keyword">event</span>.getCode())
&& mWakeLock != <span class="hljs-literal">null</span>) {
mWakeLock.acquire();
releaseWl = <span class="hljs-literal">true</span>;
}
<span class="hljs-keyword">if</span> (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
<span class="hljs-keyword">event</span>.getCode(), <span class="hljs-keyword">event</span>.getRawEvent()))) {
releaseWl = <span class="hljs-literal">false</span>;
}
} <span class="hljs-keyword">else</span> {
<span class="hljs-comment">//对于其他的响应码则添加到mResponseQueue队列【见小节2.14】</span>
mResponseQueue.<span class="hljs-keyword">add</span>(<span class="hljs-keyword">event</span>.getCmdNumber(), <span class="hljs-keyword">event</span>);
}
} <span class="hljs-keyword">catch</span> (IllegalArgumentException e) {
log(<span class="hljs-string">"Problem parsing message "</span> + e);
} <span class="hljs-keyword">finally</span> {
<span class="hljs-keyword">if</span> (releaseWl) {
mWakeLock.acquire();
}
}
start = i + <span class="hljs-number">1</span>;
}
}
...
}
} <span class="hljs-keyword">catch</span> (IOException ex) {
<span class="hljs-keyword">throw</span> ex;
} <span class="hljs-keyword">finally</span> {
<span class="hljs-comment">//收尾清理类工作,关闭mOutputStream, socket</span>
...
}
}
这里有一个动作是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();
}
<span class="hljs-comment">//类型为CountDownLatch,用于多线程同步,阻塞await()直到计数器为零</span>
<span class="hljs-selector-tag">mConnectedSignal</span><span class="hljs-selector-class">.countDown</span>();
<span class="hljs-selector-tag">if</span> (mConnectedSignal.getCount() != <span class="hljs-number">0</span>) {
<span class="hljs-selector-tag">return</span>;
}
<span class="hljs-comment">//调用PMS来加载ASECs</span>
<span class="hljs-selector-tag">mPms</span><span class="hljs-selector-class">.scanAvailableAsecs</span>();
<span class="hljs-comment">//用于通知ASEC扫描已完成</span>
<span class="hljs-selector-tag">mAsecsScanned</span><span class="hljs-selector-class">.countDown</span>();
}
这里的PMS.scanAvailableAsecs()经过层层调用,最终核心工作还是通过MountService.getSecureContainerList。
[-> MountService.java]
public String[] getSecureContainerList() {
enforcePermission(android.Manifest.permission.ASEC_ACCESS);
//等待mConnectedSignal计数锁达到零
waitForReady();
//当没有挂载Primary卷设备,则弹出警告
warnOnNotMounted();
<span class="hljs-selector-tag">try</span> {
<span class="hljs-comment">//向vold进程发送asec list命令</span>
<span class="hljs-selector-tag">return</span> <span class="hljs-selector-tag">NativeDaemonEvent</span><span class="hljs-selector-class">.filterMessageList</span>(
mConnector.executeForList(<span class="hljs-string">"asec"</span>, <span class="hljs-string">"list"</span>), VoldResponseCode.AsecListResult);
} <span class="hljs-selector-tag">catch</span> (NativeDaemonConnectorException e) {
<span class="hljs-selector-tag">return</span> <span class="hljs-selector-tag">new</span> <span class="hljs-selector-tag">String</span><span class="hljs-selector-attr">[0]</span>;
}
}
三、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;
<span class="hljs-regexp">//</span>解析参数,设置contenxt
parse_args(argc, argv);
...
fcntl(android_get_control_socket(<span class="hljs-string">"vold"</span>), F_SETFD, FD_CLOEXEC);
fcntl(android_get_control_socket(<span class="hljs-string">"cryptd"</span>), F_SETFD, FD_CLOEXEC);
mkdir(<span class="hljs-string">"/dev/block/vold"</span>, <span class="hljs-number">0755</span>);
<span class="hljs-regexp">//</span>用于cryptfs检查,并mount加密的文件系统
klog_set_level(<span class="hljs-number">6</span>);
<span class="hljs-regexp">//</span>创建单例对象VolumeManager 【见小节<span class="hljs-number">3.2</span>.<span class="hljs-number">1</span>】
<span class="hljs-keyword">if</span> (!(vm = VolumeManager::Instance())) {
<span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}
<span class="hljs-regexp">//</span>创建单例对象NetlinkManager 【见小节<span class="hljs-number">3.3</span>.<span class="hljs-number">1</span>】
<span class="hljs-keyword">if</span> (!(nm = NetlinkManager::Instance())) {
<span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}
<span class="hljs-keyword">if</span> (property_get_bool(<span class="hljs-string">"vold.debug"</span>, false)) {
vm->setDebug(true);
}
<span class="hljs-regexp">//</span> 创建CommandListener对象 【见小节<span class="hljs-number">3.4</span>.<span class="hljs-number">1</span>】
cl = new CommandListener();
<span class="hljs-regexp">//</span> 创建CryptCommandListener对象 【见小节<span class="hljs-number">3.5</span>.<span class="hljs-number">1</span>】
ccl = new CryptCommandListener();
<span class="hljs-regexp">//</span>【见小节<span class="hljs-number">3.2</span>.<span class="hljs-number">2</span>】
vm->setBroadcaster((SocketListener *) cl);
<span class="hljs-regexp">//</span>【见小节<span class="hljs-number">3.3</span>.<span class="hljs-number">2</span>】
nm->setBroadcaster((SocketListener *) cl);
<span class="hljs-keyword">if</span> (vm->start()) { <span class="hljs-regexp">//</span>【见小节<span class="hljs-number">3.2</span>.<span class="hljs-number">3</span>】
<span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}
process_config(vm); <span class="hljs-regexp">//</span>【见小节<span class="hljs-number">3.2</span>.<span class="hljs-number">4</span>】
<span class="hljs-keyword">if</span> (nm->start()) { <span class="hljs-regexp">//</span>【见小节<span class="hljs-number">3.3</span>.<span class="hljs-number">3</span>】
<span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}
coldboot(<span class="hljs-string">"/sys/block"</span>);
<span class="hljs-regexp">//</span>启动响应命令的监听器 <span class="hljs-regexp">//</span>【见小节<span class="hljs-number">3.4</span>.<span class="hljs-number">2</span>】
<span class="hljs-keyword">if</span> (cl->startListener()) {
<span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}
<span class="hljs-keyword">if</span> (ccl->startListener()) {
<span class="hljs-keyword">exit</span>(<span class="hljs-number">1</span>);
}
<span class="hljs-regexp">//</span>Vold成为监听线程
<span class="hljs-keyword">while</span>(<span class="hljs-number">1</span>) {
sleep(<span class="hljs-number">1000</span>);
}
<span class="hljs-keyword">exit</span>(<span class="hljs-number">0</span>);
}
该方法的主要功能是创建下面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();
<span class="hljs-regexp">//</span>创建Emulated内部存储
mInternalEmulated = <span class="hljs-symbol">std:</span><span class="hljs-symbol">:shared_ptr<android</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:vold</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:VolumeBase></span>(
new <span class="hljs-symbol">android:</span><span class="hljs-symbol">:vold</span><span class="hljs-symbol">:</span><span class="hljs-symbol">:EmulatedVolume</span>(<span class="hljs-string">"/data/media"</span>));
mInternalEmulated->create();
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
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);
<span class="hljs-comment">//卸载内部存储</span>
<span class="hljs-keyword">if</span> (mInternalEmulated != <span class="hljs-literal">nullptr</span>) {
mInternalEmulated->unmount();
}
<span class="hljs-comment">//卸载外部存储</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> disk : mDisks) {
disk->unmountAll();
}
FILE* fp = setmntent(<span class="hljs-string">"/proc/mounts"</span>, <span class="hljs-string">"r"</span>);
<span class="hljs-keyword">if</span> (fp == <span class="hljs-literal">NULL</span>) {
<span class="hljs-keyword">return</span> -errno;
}
<span class="hljs-built_in">std</span>::<span class="hljs-built_in">list</span><<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>> toUnmount;
mntent* mentry;
<span class="hljs-keyword">while</span> ((mentry = getmntent(fp)) != <span class="hljs-literal">NULL</span>) {
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">strncmp</span>(mentry->mnt_dir, <span class="hljs-string">"/mnt/"</span>, <span class="hljs-number">5</span>) == <span class="hljs-number">0</span>
|| <span class="hljs-built_in">strncmp</span>(mentry->mnt_dir, <span class="hljs-string">"/storage/"</span>, <span class="hljs-number">9</span>) == <span class="hljs-number">0</span>) {
toUnmount.push_front(<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>(mentry->mnt_dir));
}
}
endmntent(fp);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">auto</span> path : toUnmount) {
<span class="hljs-comment">//强制卸载</span>
android::vold::ForceUnmount(path);
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
此处打开的”/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());
<span class="hljs-regexp">//</span>强制卸载fuse路径
ForceUnmount(mFuseDefault);
ForceUnmount(mFuseRead);
ForceUnmount(mFuseWrite);
<span class="hljs-keyword">rmdir</span>(mFuseDefault.c_str());
<span class="hljs-keyword">rmdir</span>(mFuseRead.c_str());
<span class="hljs-keyword">rmdir</span>(mFuseWrite.c_str());
mFuseDefault.clear();
mFuseRead.clear();
mFuseWrite.clear();
<span class="hljs-keyword">return</span> 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());
...
<span class="hljs-keyword">bool</span> has_adoptable = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < fstab->num_entries; i++) {
<span class="hljs-keyword">if</span> (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
<span class="hljs-keyword">if</span> (fs_mgr_is_nonremovable(&fstab->recs[i])) {
LOG(WARNING) << <span class="hljs-string">"nonremovable no longer supported; ignoring volume"</span>;
<span class="hljs-keyword">continue</span>;
}
std::string sysPattern(fstab->recs[i].blk_device);
std::string nickname(fstab->recs[i].label);
<span class="hljs-keyword">int</span> flags = <span class="hljs-number">0</span>;
<span class="hljs-keyword">if</span> (fs_mgr_is_encryptable(&fstab->recs[i])) {
flags |= android::vold::Disk::Flags::kAdoptable;
has_adoptable = <span class="hljs-literal">true</span>;
}
<span class="hljs-keyword">if</span> (fs_mgr_is_noemulatedsd(&fstab->recs[i])
|| property_get_bool(<span class="hljs-string">"vold.debug.default_primary"</span>, <span class="hljs-literal">false</span>)) {
flags |= android::vold::Disk::Flags::kDefaultPrimary;
}
vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
new VolumeManager::DiskSource(sysPattern, nickname, flags)));
}
}
property_set(<span class="hljs-string">"vold.has_adoptable"</span>, has_adoptable ? <span class="hljs-string">"1"</span> : <span class="hljs-string">"0"</span>);
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
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, <span class="hljs-number">0</span>, <span class="hljs-keyword">sizeof</span>(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = getpid(); <span class="hljs-comment">//记录当前进程的pid</span>
nladdr.nl_groups = <span class="hljs-number">0xffffffff</span>;
<span class="hljs-comment">//创建event socket</span>
<span class="hljs-keyword">if</span> ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_KOBJECT_UEVENT)) < <span class="hljs-number">0</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
<span class="hljs-comment">//设置uevent的SO_RCVBUFFORCE选项</span>
<span class="hljs-keyword">if</span> (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, <span class="hljs-keyword">sizeof</span>(sz)) < <span class="hljs-number">0</span>) {
<span class="hljs-keyword">goto</span> <span class="hljs-keyword">out</span>;
}
<span class="hljs-comment">//设置uevent的SO_PASSCRED选项</span>
<span class="hljs-keyword">if</span> (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, <span class="hljs-keyword">sizeof</span>(on)) < <span class="hljs-number">0</span>) {
<span class="hljs-keyword">goto</span> <span class="hljs-keyword">out</span>;
}
<span class="hljs-comment">//绑定uevent socket</span>
<span class="hljs-keyword">if</span> (bind(mSock, (<span class="hljs-keyword">struct</span> sockaddr *) &nladdr, <span class="hljs-keyword">sizeof</span>(nladdr)) < <span class="hljs-number">0</span>) {
<span class="hljs-keyword">goto</span> <span class="hljs-keyword">out</span>;
}
<span class="hljs-comment">//创建NetlinkHandler【见小节3.3.4】</span>
mHandler = new NetlinkHandler(mSock);
<span class="hljs-comment">//启动NetlinkHandler【见小节3.3.5】</span>
<span class="hljs-keyword">if</span> (mHandler->start()) {
<span class="hljs-keyword">goto</span> <span class="hljs-keyword">out</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
out:
close(mSock);
return -1;
}
3.3.4 NetlinkHandler
NetlinkHandler继承于 NetlinkListener
, NetlinkListener
继承于 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));
<span class="hljs-comment">//创建匿名管道</span>
<span class="hljs-keyword">if</span> (pipe(mCtrlPipe)) {
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
<span class="hljs-comment">//创建工作线程,线程运行函数threadStart【见小节3.3.6】</span>
<span class="hljs-keyword">if</span> (pthread_create(&mThread, <span class="hljs-literal">NULL</span>, SocketListener::threadStart, <span class="hljs-keyword">this</span>)) {
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
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;
<span class="hljs-type">FD_ZERO</span>(&read_fds);
<span class="hljs-keyword">if</span> (mListen) {
<span class="hljs-built_in">max</span> = mSock;
<span class="hljs-type">FD_SET</span>(mSock, &read_fds);
}
<span class="hljs-type">FD_SET</span>(mCtrlPipe[<span class="hljs-number">0</span>], &read_fds);
<span class="hljs-keyword">if</span> (mCtrlPipe[<span class="hljs-number">0</span>] > <span class="hljs-built_in">max</span>)
<span class="hljs-built_in">max</span> = mCtrlPipe[<span class="hljs-number">0</span>];
pthread_mutex_lock(&mClientsLock);
<span class="hljs-keyword">for</span> (it = mClients->begin(); it != mClients->end(); ++it) {
<span class="hljs-comment">// NB: calling out to an other object with mClientsLock held (safe)</span>
int fd = (*it)->getSocket();
<span class="hljs-type">FD_SET</span>(fd, &read_fds);
<span class="hljs-keyword">if</span> (fd > <span class="hljs-built_in">max</span>) {
<span class="hljs-built_in">max</span> = fd;
}
}
pthread_mutex_unlock(&mClientsLock);
<span class="hljs-type">SLOGV</span>(<span class="hljs-string">"mListen=%d, max=%d, mSocketName=%s"</span>, mListen, <span class="hljs-built_in">max</span>, mSocketName);
<span class="hljs-keyword">if</span> ((rc = select(<span class="hljs-built_in">max</span> + <span class="hljs-number">1</span>, &read_fds, <span class="hljs-type">NULL</span>, <span class="hljs-type">NULL</span>, <span class="hljs-type">NULL</span>)) < <span class="hljs-number">0</span>) {
<span class="hljs-keyword">if</span> (errno == <span class="hljs-type">EINTR</span>)
<span class="hljs-keyword">continue</span>;
<span class="hljs-type">SLOGE</span>(<span class="hljs-string">"select failed (%s) mListen=%d, max=%d"</span>, strerror(errno), mListen, <span class="hljs-built_in">max</span>);
sleep(<span class="hljs-number">1</span>);
<span class="hljs-keyword">continue</span>;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!rc)
<span class="hljs-keyword">continue</span>;
<span class="hljs-keyword">if</span> (<span class="hljs-type">FD_ISSET</span>(mCtrlPipe[<span class="hljs-number">0</span>], &read_fds)) {
char <span class="hljs-built_in">c</span> = <span class="hljs-type">CtrlPipe_Shutdown</span>;
<span class="hljs-type">TEMP_FAILURE_RETRY</span>(read(mCtrlPipe[<span class="hljs-number">0</span>], &<span class="hljs-built_in">c</span>, <span class="hljs-number">1</span>));
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">c</span> == <span class="hljs-type">CtrlPipe_Shutdown</span>) {
<span class="hljs-keyword">break</span>;
}
<span class="hljs-keyword">continue</span>;
}
<span class="hljs-keyword">if</span> (mListen && <span class="hljs-type">FD_ISSET</span>(mSock, &read_fds)) {
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sockaddr</span> <span class="hljs-title">addr</span>;
<span class="hljs-title">socklen_t</span> <span class="hljs-title">alen</span>;
<span class="hljs-title">int</span> <span class="hljs-title">c</span>;
<span class="hljs-title">do</span> </span>{
alen = <span class="hljs-built_in">sizeof</span>(addr);
<span class="hljs-built_in">c</span> = accept(mSock, &addr, &alen);
<span class="hljs-type">SLOGV</span>(<span class="hljs-string">"%s got %d from accept"</span>, mSocketName, <span class="hljs-built_in">c</span>);
} <span class="hljs-keyword">while</span> (<span class="hljs-built_in">c</span> < <span class="hljs-number">0</span> && errno == <span class="hljs-type">EINTR</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">c</span> < <span class="hljs-number">0</span>) {
<span class="hljs-type">SLOGE</span>(<span class="hljs-string">"accept failed (%s)"</span>, strerror(errno));
sleep(<span class="hljs-number">1</span>);
<span class="hljs-keyword">continue</span>;
}
fcntl(<span class="hljs-built_in">c</span>, <span class="hljs-type">F_SETFD</span>, <span class="hljs-type">FD_CLOEXEC</span>);
pthread_mutex_lock(&mClientsLock);
mClients->push_back(new <span class="hljs-type">SocketClient</span>(<span class="hljs-built_in">c</span>, <span class="hljs-literal">true</span>, mUseCmdNum));
pthread_mutex_unlock(&mClientsLock);
}
<span class="hljs-comment">/* Add all active clients to the pending list first */</span>
pendingList.clear();
pthread_mutex_lock(&mClientsLock);
<span class="hljs-keyword">for</span> (it = mClients->begin(); it != mClients->end(); ++it) {
<span class="hljs-type">SocketClient</span>* <span class="hljs-built_in">c</span> = *it;
<span class="hljs-comment">// NB: calling out to an other object with mClientsLock held (safe)</span>
int fd = <span class="hljs-built_in">c</span>->getSocket();
<span class="hljs-keyword">if</span> (<span class="hljs-type">FD_ISSET</span>(fd, &read_fds)) {
pendingList.push_back(<span class="hljs-built_in">c</span>);
<span class="hljs-built_in">c</span>->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);
<span class="hljs-comment">/* Process the pending list, since it is owned by the thread,
* there is no need to lock it */</span>
<span class="hljs-keyword">while</span> (!pendingList.empty()) {
<span class="hljs-comment">/* Pop the first item from the list */</span>
it = pendingList.begin();
<span class="hljs-type">SocketClient</span>* <span class="hljs-built_in">c</span> = *it;
pendingList.erase(it);
<span class="hljs-comment">/* Process it, if false is returned, remove from list */</span>
<span class="hljs-keyword">if</span> (!onDataAvailable(<span class="hljs-built_in">c</span>)) {
release(<span class="hljs-built_in">c</span>, <span class="hljs-literal">false</span>);
}
<span class="hljs-built_in">c</span>->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) {
<span class="hljs-keyword">if</span> (!mSocketName && mSock == <span class="hljs-number">-1</span>) {
...
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (mSocketName) {
<span class="hljs-comment">//获取“vold”所对应的句柄</span>
<span class="hljs-keyword">if</span> ((mSock = android_get_control_socket(mSocketName)) < <span class="hljs-number">0</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
fcntl(mSock, F_SETFD, FD_CLOEXEC);
}
<span class="hljs-comment">//CL开始监听</span>
<span class="hljs-keyword">if</span> (mListen && listen(mSock, backlog) < <span class="hljs-number">0</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
...
<span class="hljs-comment">//创建匿名管道</span>
<span class="hljs-keyword">if</span> (pipe(mCtrlPipe)) {
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
<span class="hljs-comment">//创建工作线程</span>
<span class="hljs-keyword">if</span> (pthread_create(&mThread, NULL, SocketListener::threadStart, <span class="hljs-keyword">this</span>)) {
<span class="hljs-keyword">return</span> <span class="hljs-number">-1</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
四、 案例分析
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");