Android7.0 Vold 進程工做機制分析之由Kernel發起掛載請求

Android7.0 Vold 進程工做機制分析之由Kernel發起掛載請求

PS:本文已同步至個人博客:點擊打開

1、MountService簡介

MountService是一個系統服務,負責與Vold進程通訊的,運行在SystemServer進程,當收到Vold的掛載消息後,會經過廣播的方式通知上層應用.java

它是在SystemServer的startOtherServices方法裏啓動的android

SystemServer路徑------------------/frameworks/base/services/java/com/android/server/SystemServer.java瀏覽器


private void startOtherServices() {
    ......
    //若是不爲低級工廠測試
   if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            if (!disableStorage &&!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
                try {
                    //啓動MountService
                    mSystemServiceManager.startService(MOUNT_SERVICE_CLASS);
                    mountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
                } catch (Throwable e) {
                    reportWtf("starting Mount Service", e);
                }
            }
        }
    ......
    }複製代碼

這裏並不打算詳細介紹MountService的啓動過程,只需關注它的構造方法便可(在啓動時會調用構造方法)安全

MountService路徑------------------/frameworks/base/services/core/java/com/android/server/MountService.javasocket

class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
        ...... 
public MountService(Context context) { 
        ......   

        //建立並啓動一個帶消息循環的MountService工做線程  
        HandlerThread hthread = new HandlerThread(TAG);
        hthread.start();
        //爲MountService工做線程建立一個Handler  
        mHandler = new MountServiceHandler(hthread.getLooper());

        ......  
        /*
            NativeDaemonConnector用於Socket通訊,第二個參數「vold」表示將和Vold通訊,也就是
            和CL模塊中的那個socket創建通訊鏈接。第一個參數爲INativeDaemonConnectorCallbacks
            接口。它提供兩個回調函數:
            onDaemonConnected:當NativeDaemonConnector鏈接上Vold後回調。
            onEvent:當NativeDaemonConnector收到來自Vold的數據後回調。
        */            
        mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
                null);

        //建立線程名爲"VoldConnector"的線程,用於跟vold通訊
        mConnectorThread = new Thread(mConnector, VOLD_TAG);
        ......  
    }複製代碼

主要作了如下幾件事
①建立並啓動一個帶消息循環的MountService工做線程 HandlerThread
②爲MountService工做線程建立一個Handler
③建立線程名爲"VoldConnector"的線程,用於跟vold通訊ide

在MountService的start方法啓動這個線程函數

private void start() {
        mConnectorThread.start();
    }複製代碼

這個VoldConnector線程在run方法裏會調用一個方法listenToSocket,監聽Vold進程發過來的Socket消息oop

NativeDaemonConnector路徑------/frameworks/base/services/core/java/com/android/server/NativeDaemonConnector.java測試

final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
    ...... 
 @Override
    public void run() {

        mCallbackHandler = new Handler(mLooper, this);

        while (true) {
            try {
                //監聽Vold進程發過來的socket消息
                listenToSocket();
            } catch (Exception e) {
                loge("Error in NativeDaemonConnector: " + e);
                SystemClock.sleep(5000);
            }
        }
    }
    ...... 
}複製代碼

在listenToSocket方法裏會創建Socket鏈接,把接收的消息解析成NativeDaemonEvent事件,經過mCallbackHandler處理這個事件 ui

private void listenToSocket() throws IOException {
        LocalSocket socket = null;
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = determineSocketAddress();
            //創建與"/dev/socket/vold"的socket鏈接
            socket.connect(address);
             ...... 
             try {
            //解析成    NativeDaemonEvent事件            
            final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent, fdList);

             //當響應碼區間爲[600,700),則發送消息交由mCallbackHandler處理                
            Message msg = mCallbackHandler.obtainMessage( event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());    
            ......  
        }
    }複製代碼

關於MountService的簡介先到這裏,下面分析Kernel發起掛載請求的流程.

2、Kernel發起掛載請求的流程

上一篇文章Android7.0 Vold 進程工做機制分析之總體流程已經講了,Kernel是經過Netlink 進行通訊的,Netlink 是一種特殊的Socket。


如下是上一篇文章的Vold 進程啓動時序圖(縮放瀏覽器能夠放大查看或者在新標籤頁打開)


這裏以SD卡插入,Kernel掛載請求爲例,流程從NetlinkListener的onDataAvailable開始

1.NetlinkListener::onDataAvailable

bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -1;
......
NetlinkEvent *evt = new NetlinkEvent();
//解析得到NetlinkEvent實例
if (evt->decode(mBuffer, count, mFormat)) {
//傳入NetlinkEvent實例
onEvent(evt);
}
......
}複製代碼

解析得到NetlinkEvent實例,調用onEvent方法,onEvent由子類NetlinkHandler實現

2.NetlinkHandler::onEvent

void NetlinkHandler::onEvent(NetlinkEvent *evt) {
//獲取VolumeManager 單例對象
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();


if (!strcmp(subsys, "block")) {
//調用VolumeManager 的handleBlockEvent方法
vm->handleBlockEvent(evt);
}
}複製代碼

獲取VolumeManager 的單例而後調用handleBlockEvent方法

3.vm->handleBlockEvent

void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {

    switch (evt->getAction()) {
    case NetlinkEvent::Action::kAdd: {
        for (auto source : mDiskSources) {
            if (source->matches(eventPath)) {
                ......
                auto disk = new android::vold::Disk(eventPath, device,source->getNickname(), flags);
                //調用disk 的create方法
                disk->create();
                mDisks.push_back(std::shared_ptr
  
  
  

 
  
  (disk)); break; } } break; } case NetlinkEvent::Action::kChange: { ...... break; } case NetlinkEvent::Action::kRemove: { ...... break; } ...... } } 

 複製代碼

SD卡插入爲add事件。那麼調用了disk->create()方法。

4.disk->create()

路徑:
disk.cpp-----------------------system/vold/disk.cpp

status_t Disk::create() {
    CHECK(!mCreated);
    mCreated = true;
    //調用notifyEvent方法
    notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));    
    readMetadata();
    //讀取分區數據,建立Volume
    readPartitions();
    return OK;
}複製代碼

在create方法裏調用notifyEvent通知DiskCreated事件,而後還有readMetadata方法建立Volume.全部這裏也要分兩條線56

5. Disk::notifyEvent

void Disk::notifyEvent(int event) {
    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,getId().c_str(), false);
}複製代碼

獲取單例VolumeManager對象,而後獲取到SocketListener對象調用sendBroadcast方法
sendBroadcast方法的實現以下
在safelist列表中添加SocketClient,而後調用sendMsg方法

5. 1 SocketListener::sendBroadcast

void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
    SocketClientCollection safeList;

    //首先添加全部活動的SockClient到安全列表中
    safeList.clear();

    for (i = mClients->begin(); i != mClients->end(); ++i) {
        SocketClient* c = *i;
        c->incRef();
        //添加
        safeList.push_back(c);
    }

    while (!safeList.empty()) {
        /* Pop the first item from the list */
        i = safeList.begin();
        SocketClient* c = *i;
        safeList.erase(i);
        //調用SockClient的sendMSg方法發送消息
        if (c->sendMsg(code, msg, addErrno, false)) {
            SLOGW("Error sending broadcast (%s)", strerror(errno));
        }
        c->decRef();
    }
}複製代碼

SockClient
路徑:
SockClient.cpp------------------------system/core/libsysutils/src/SockClient.cpp

調用sendMsg方法通過層層跳轉,到sendDataLockedv方法中,往Socket中寫入信息

5.2 sendDataLockedv

int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
    ......       
    for (;;) {
        ssize_t rc = TEMP_FAILURE_RETRY(
            writev(mSocket, iov + current, iovcnt - current));
    ......    
}複製代碼

寫入到Socket以後,SystemServer中的MountService會收到,就是前面講到的NativeDaemonConnector的listenToSocket方法

5.3 NativeDaemonConnector::listenToSocket

在listenToSocket方法裏會創建Socket鏈接,把接收的消息解析成NativeDaemonEvent事件,經過mCallbackHandler處理這個事件

private void listenToSocket() throws IOException {
        LocalSocket socket = null;
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = determineSocketAddress();
            //創建與"/dev/socket/vold"的socket鏈接
            socket.connect(address);
             ...... 
             try {
            //解析成   NativeDaemonEvent事件         
            final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(rawEvent, fdList);

             //當響應碼區間爲[600,700),則發送消息交由mCallbackHandler處理               
            Message msg = mCallbackHandler.obtainMessage( event.getCode(), uptimeMillisInt(), 0, event.getRawEvent());    
            ......  
        }
    }複製代碼

mCallbackHandler處理事件,HandleMessage方法以下

5.4 NativeDaemonConnector::handleMessage

@Override
    public boolean handleMessage(Message msg) {
        final String event = (String) msg.obj;
        final int start = uptimeMillisInt();
        final int sent = msg.arg1;
        try {
            if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) {
                log(String.format("Unhandled event '%s'", event));
            }
        }  
        ......
        return true;
    }複製代碼

在handleMessage方法裏會回調MountService的onEvent方法

5.5 MountService::onEvent

@Override
    public boolean onEvent(int code, String raw, String[] cooked) {
        synchronized (mLock) {
            return onEventLocked(code, raw, cooked);
        }
    }複製代碼

交給onEventLocked處理了

5.6 MountService::onEventLocked

private boolean onEventLocked(int code, String raw, String[] cooked) {
        switch (code) {
            //處理DISK_CREATED建立成功事件
            case VoldResponseCode.DISK_CREATED: {
                if (cooked.length != 3) break;
                final String id = cooked[1];
                int flags = Integer.parseInt(cooked[2]);
                if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
                        || mForceAdoptable) {
                    flags |= DiskInfo.FLAG_ADOPTABLE;
                }
                mDisks.put(id, new DiskInfo(id, flags));
                break;
            }
            ......
            //這個事件就是以前4.disk->create()方法走第6條線最後回到這裏處理
             //處理VOLUME_CREATED建立成功事件
            case VoldResponseCode.VOLUME_CREATED: {
                final String id = cooked[1];
                final int type = Integer.parseInt(cooked[2]);
                final String diskId = TextUtils.nullIfEmpty(cooked[3]);
                final String partGuid = TextUtils.nullIfEmpty(cooked[4]);

                final DiskInfo disk = mDisks.get(diskId);
                final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
                mVolumes.put(id, vol);
                onVolumeCreatedLocked(vol);
                break;
            }
             ......
   }
        return true;
    }複製代碼

小結

在這裏處理了DISK_CREATED建立成功事件,至此這個事件傳遞就完了.我畫一張UML圖
(縮放瀏覽器能夠放大查看或者在新標籤頁打開)


下面介紹的是從第6條線開始的流程

6 Disk::readPartitions()

在這個方法裏會讀取分區數據,而後建立Volume

status_t Disk::readPartitions() { 
            ......
            if (table == Table::kMbr) {
            ......
                    //6.2 建立PublicVolume
                    createPublicVolume(partDevice);
                    break;
                }
            } else if (table == Table::kGpt) {
             ......
                if (!strcasecmp(typeGuid, kGptBasicData)) {

                    createPublicVolume(partDevice);
                } else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {

                    createPrivateVolume(partDevice, partGuid);
                }
            }
        }
    }
    if (table == Table::kUnknown || !foundParts) {
          ......
        if (ReadMetadataUntrusted(mDevPath, fsType, unused, unused) == OK) {

            createPublicVolume(mDevice);
        }  
    }
        ......
}複製代碼

6.1 Disk::createPublicVolume()

void Disk::createPublicVolume(dev_t device) {
    //獲取PublicVolume實例
    auto vol = std::shared_ptr
  
  
  

 
  
  (new PublicVolume(device)); if (mJustPartitioned) { LOG(DEBUG) << "Device just partitioned; silently formatting"; vol->setSilent(true); vol->create(); vol->format("auto"); vol->destroy(); vol->setSilent(false); } mVolumes.push_back(vol); vol->setDiskId(getId()); //調用VolumeBase的create方法 vol->create(); } 

 複製代碼

6.2 VolumeBase::create()

status_t VolumeBase::create() {

    mCreated = true;
    status_t res = doCreate();
    //向VolumeManager發送VolumeCreated命令
    notifyEvent(ResponseCode::VolumeCreated,StringPrintf("%d \"%s\" \"%s\"", mType, mDiskId.c_str(), mPartGuid.c_str()));
    //設置已卸載狀態
    setState(State::kUnmounted);
    return res;
}複製代碼

在這個方法裏通知VolumeManager建立了Volume,這個方法以後的步驟就和第5步之後同樣的了,就不重複介紹了.
以後走到5.6 MountService::onEventLocked方法裏的case VoldResponseCode.VOLUME_CREATED:處理,調用onVolumeCreatedLocked(vol)方法

6.3 MountService::onVolumeCreatedLocked()

class MountService extends IMountService.Stub implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
        ...... 
private void onVolumeCreatedLocked(VolumeInfo vol) {
        ...... 

        if (vol.type == VolumeInfo.TYPE_EMULATED) {
            final StorageManager storage = mContext.getSystemService(StorageManager.class);
            final VolumeInfo privateVol = storage.findPrivateForEmulated(vol);

            if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)
                    && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) {
                Slog.v(TAG, "Found primary storage at " + vol);
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY;
                vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
                //handler發送消息
                mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
            ...... 

            }
            }複製代碼

以後走到Handler處理H_VOLUME_MOUNT消息

6.4 MountService::handleMessage()

class MountServiceHandler extends Handler {
        public MountServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            ......
                  case H_VOLUME_MOUNT: {
                    final VolumeInfo vol = (VolumeInfo) msg.obj;
                    if (isMountDisallowed(vol)) {
                        Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
                        break;
                    }
                    try {
                        //執行mount(掛載)命令
                        mConnector.execute("volume", "mount", vol.id, vol.mountFlags,
                                vol.mountUserId);
                    } catch (NativeDaemonConnectorException ignored) {
                    }
                    break;
                }
            ......    
            }複製代碼

會調用NativeDaemonConnector的execute方法,通過跳轉到executeForList方法

6.5 NativeDaemonConnector::executeForList()

final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
    ...... 
    public NativeDaemonEvent[] executeForList(long timeoutMs, String cmd, Object... args)
            throws NativeDaemonConnectorException {
     ...... 
      synchronized (mDaemonLock) {
            if (mOutputStream == null) {
                throw new NativeDaemonConnectorException("missing output stream");
            } else {
                try {
        //往Socket 輸出流寫入命令            
                    mOutputStream.write(rawCmd.getBytes(StandardCharsets.UTF_8));
                } catch (IOException e) {
                    throw new NativeDaemonConnectorException("problem sending command", e);
                }
            }
        ...... 
        }複製代碼

往Socket寫入輸出流以後,Vold中FrameWorkListener的onDataAvailable會收到

6.6 FrameWorkListener::onDataAvailable()

bool FrameworkListener::onDataAvailable(SocketClient *c) {
    char buffer[CMD_BUF_SIZE];
    int len;
    //讀取socket消息
    len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
    .....
    int i;
    for (i = 0; i < len; i++) {
        if (buffer[i] == '\0') {
            //根據消息內容 派發命令
            dispatchCommand(c, buffer + offset);
            offset = i + 1;
        }
    }

    return true;
}複製代碼

在onDataAvailable方法裏會先讀取Socket消息,而後分發命令

6.7 FrameWorkListener::dispatchCommand()

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
     ......
    //執行對應的消息
    for (i = mCommands->begin(); i != mCommands->end(); ++i) {
        FrameworkCommand *c = *i;
        //匹配命令
        if (!strcmp(argv[0], c->getCommand())) {
            //執行命令
            if (c->runCommand(cli, argc, argv)) {
                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
            }
            goto out;
        }
    }
     ......
}複製代碼

會調用FrameworkCommand 的runCommand方法,以前在CommandListener的構造方法裏註冊的這些指令,就是FrameWorkCommand類型,以下

FrameworkListener.cpp

void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
    //添加元素
    mCommands->push_back(cmd);
}複製代碼

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());
    registerCmd(new AppFuseCmd());
}複製代碼

這裏插入SD卡是掛載指令,即VolumeCmd指令,會進入到VolumeCmd的runCommand方法

6.8 CommandListener::runCommand()

CommandListener.cpp

int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) {

    ......

    } else if (cmd == "mount" && argc > 2) {
        // mount [volId] [flags] [user]
        std::string id(argv[2]);
        auto vol = vm->findVolume(id);
        if (vol == nullptr) {
            return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
        }

        int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
        userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;

        vol->setMountFlags(mountFlags);
        vol->setMountUserId(mountUserId);
        //執行真正的掛載操做 
        int res = vol->mount();
        if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
            vm->setPrimary(vol);
        }
        //發送應答消息給MountService
        return sendGenericOkFail(cli, res);
        ......
    }  
}複製代碼

會執行實際的mount操做

6.9 vol->mount()

vol是VolumeBase的實例,VolumeBase的mount方法由具體的子類EmulatedVolume、PublicVolume、PrivateVolume等實現

執行操做以後會發送應答消息給MountService.

status_t VolumeBase::mount() {

    ......
    setState(State::kChecking);
    //doMount由子類實現實際掛載操做
    status_t res = doMount();
    if (res == OK) {
        setState(State::kMounted);
    } else {
        setState(State::kUnmountable);
    }

    return res;
}複製代碼

6.10 PublicVolume->doMount()

PublicVolume.cpp

status_t PublicVolume::doMount() {
    ......
}複製代碼

3、小結

至此,這個掛載操做就完成了.時序圖以下:
下一篇講解從上層MountService發起掛載請求的流程.

相關文章
相關標籤/搜索