基於Android 6.0源碼, 來分析存儲相關架構,涉及源碼:
/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
/framework/base/core/java/android/os/storage/IMountService.java
/framework/base/core/java/android/os/storage/IMountServiceListener.java
/framework/base/core/java/android/os/storage/StorageManager.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
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
[-> MountService.java]
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);
}
}
其主要功能依次是:
創建ICallbacks回調方法,FgThread線程名爲”android.fg”,此處用到的Looper便是線程”android.fg”中的Looper;
創建並啓動線程名爲”MountService”的handlerThread;
創建OBB操作的handler,IoThread線程名爲”android.io”,此處用到的的Looper便是線程”android.io”中的Looper;
創建NativeDaemonConnector對象
創建並啓動線程名爲”VoldConnector”的線程;
創建並啓動線程名爲”CryptdConnector”的線程;
註冊監聽用戶添加、刪除的廣播;
從這裏便可知道共創建了3個線程:”MountService”,”VoldConnector”,”CryptdConnector”,另外還會使用到系統進程中的兩個線程”android.fg”和”android.io”. 這便是在文章開頭進程架構圖中Java framework層進程的創建情況.
接下來再分別看看MountService創建過程中的Callbacks實例化, NativeDaemonConnector實例化,以及”vold”線程的運行.
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.13】
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
SystemClock.sleep(5000);
}
}
}
}
在線程VoldConnector中建立了名爲vold的socket的客戶端,通過循環方式不斷監聽Vold服務端發送過來的消息。 另外,同理還有一個線程CryptdConnector也採用類似的方式,建立了cryptd`的socket客戶端,監聽Vold中另個線程發送過來的消息。到此,MountService與NativeDaemonConnector都已經啓動,那麼接下來到系統啓動到達階段PHASE_ACTIVITY_MANAGER_READY,則調用到onBootPhase方法。
2.7 onBootPhase
[-> MountService.java ::Lifecycle]
由於MountService的內部Lifecycle已添加SystemServiceManager的mServices服務列表;系統啓動到PHASE_ACTIVITY_MANAGER_READY時會回調mServices中的onBootPhase方法
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=1min,即命令執行超時時長爲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;
}
}
這裏用到poll,先來看看responses = new ArrayBlockingQueue(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來說,只是普通類,虛擬機無法識別。
再回到ResponseQueue.remove(),該方法中mPendingCmds中的內容是哪裏添加的呢?其實是在NDC.listenToSocket循環監聽到消息時添加的,則接下來看看監聽過程。
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();
}
}
看完了如何向mPendingCmds中增加待處理的命令,再來回過來看看,噹噹listenToSocket剛開始監聽前,收到Native的Daemon連接後的執行操作.
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];
}
}
2.16 小節
這裏以一張簡單的流程圖來說明上述過程:
三、Vold
介紹完了Java framework層的MountService以及NativeDaemonConnector,往下走來到了Vold的世界.Vold是由開機過程中解析init.rc時啓動:
on post-fs-data
start vold //啓動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
接下來便進入Vold的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繼承於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));
//創建匿名管道
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;
}
四、小結
Linux Kernel:通過uevent向Vold的NetlinkManager發送Uevent事件;
NetlinkManager:接收來自Kernel的Uevent事件,再轉發給VolumeManager;
VolumeManager:接收來自NetlinkManager的事件,再轉發給CommandListener進行處理;
CommandListener:接收來自VolumeManager的事件,通過socket通信方式發送給MountService;
MountService:接收來自CommandListener的事件。