Android7.0 Vold 進程工作機制分析之整體流程

Android7.0 Vold 進程工作機制分析之整體流程

一、Vold簡介

Vold是Volume Daemon的縮寫,負責管理和控制Android平臺外部存儲設備,包括SD插撥、掛載、卸載、格式化等。它是通過init進程解析init.rc腳本所啓動的進程.它處於Native層.

二、基礎架構

這裏引用Gityuan博客的一張圖。

這裏寫圖片描述


SystermServer進程和Vold進程是通過Socket進行通信的,Vold進程和Kernel是通過Netlink 進行通信的,Netlink 是一種特殊的Socket。

相關介紹:Netlink是linux提供的用於內核和用戶態進程之間的通信方式,也能用於用戶空間的兩個進程通信,通過這個機制,位於用戶空間的進程,可接收來自Kernel的一些信息(例如Vold中用到的USB或SD的插拔消息),同時應用層也可通過Netlink向Kernel發送一些控制命令。


先大體的簡單介紹一下流程:

1.由SystemServer進程發起掛載/卸載請求

運行在SystemServer進程的MountService通過NativeDaemonConnector給CommandListener發送請求,CommandListener再通知VolumeManager進行實際操作

2.由Kernel發起掛載/卸載請求

Kernel通過Netlink發送請求(傳遞uevent)給NetlinkManager,NetlinkManager通過內部的線程NetlinkHandler交給VolumeManager進行實際操作,然後VolumeManager通過CommandListener通知MountService

在分別仔細介紹着兩個流程之前,瞭解下Vold進程的啓動流程,先把握整體流程再細化。

三、Vold進程啓動流程

Vold進程代碼路徑:system/vold/

主要類的路徑(方便查閱)

system/vold/目錄下

main.cpp————————————system/vold/main.cpp
NetlinkManager.cpp———————–system/vold/NetlinkManager.cpp
NetlinkHandler.cpp————————system/vold/NetlinkHandler.cpp
VoldCommand.cpp————————system/vold/VoldCommand.cpp
VolumeBase.cpp—————————system/vold/VolumeBase.cpp
VolumeManager.cpp———————–system/vold/VolumeManager.cpp


system/core/libsysutils/src/目錄下

SocketListener.cpp————————–system/core/libsysutils/src/SocketListener.cpp
NetlinkListener.cpp————————–system/core/libsysutils/src/NetlinkListener.cpp
FrameworkListener.cpp——————–system/core/libsysutils/src/FrameworkListener.cpp
FrameworkCommand.cpp——————system/core/libsysutils/src//FrameworkCommand.cpp


system/core/include/sysutils/目錄下

對應着system/core/libsysutils/src/目錄的h文件

貼上我繪製的時序圖(縮放瀏覽器可以放大查看或者在新標籤頁打開)

這裏寫圖片描述

分步驟給大家詳細介紹下:

1.main()

Vold進程代碼位於system/vold/目錄下,從main.cpp開始,後續的截圖我只截取主要的代碼

在main方法裏,主要做以下幾件事情

初始化VolumeManager ,CommandListener ,NetlinkManager 三個類的實例
給VolumeManager 和NetlinkManager 設置CommandListener 實例,用作後續監聽兩個Socket,用得是設計模式中的Command(命令)模式
啓動VolumeManager ,CommandListener ,NetlinkManager
解析Vold的配置文件fstab
做一次冷啓動

int main(int argc, char** argv) {
    ......

    VolumeManager *vm;
    CommandListener *cl;
    NetlinkManager *nm;

    //創建文件夾/dev/block/vold
    mkdir("/dev/block/vold", 0755);

    //用於cryptfs檢查,並mount加密的文件系統
    klog_set_level(6);

    //獲取VolumeManager的單例
    if (!(vm = VolumeManager::Instance())) {
        LOG(ERROR) << "Unable to create VolumeManager";
        exit(1);
    }
    //獲取NetlinkManager的單例
    if (!(nm = NetlinkManager::Instance())) {
        LOG(ERROR) << "Unable to create NetlinkManager";
        exit(1);
    }

    //實例化
    cl = new CommandListener();

    //設置socket監聽對象
    vm->setBroadcaster((SocketListener *) cl);
    nm->setBroadcaster((SocketListener *) cl);

    //啓動VolumeManager
    if (vm->start()) {
        PLOG(ERROR) << "Unable to start VolumeManager";
        exit(1);
    }

    //解析Vold的配置文件fstab,初始化VolumeManager 
    if (process_config(vm)) {
        PLOG(ERROR) << "Error reading configuration... continuing anyways";
    }

    //啓動NetlinkManager
    if (nm->start()) {
        PLOG(ERROR) << "Unable to start NetlinkManager";
        exit(1);
    }   
    // 冷啓動,vold錯過了一些uevent,重新觸發。向/sys/block的uevent文件寫入”add\n” 字符觸發內核發送Uevent消息,相當執行了一次熱插拔。       
    coldboot("/sys/block");

    //啓動CommandListener
    if (cl->startListener()) {
        PLOG(ERROR) << "Unable to start CommandListener";
        exit(1);
    }
    ......
}

如果vold.fstab解析無誤,VolueManager將創建具體的Volume,若vold.fstab解析不存在或者打開失敗,Vold將會讀取Linux內核中的參數,此時如果參數中存在SDCARD(也就是SD的默認路徑),VolumeManager則會創建AutoVolume,如果不存在這個默認路徑那麼就不會創建。

它的格式對應如下:

type———————–掛載命令
lable———————–標籤
mount_point ————掛載點
part ———————–第幾個分區
sysfs_path—————設備的sysfs paths

sysfs_path可以有多個 part指定分區個數,如果是auto沒有分區


coldboot方法會調用do_coldboot方法,往/sys/block目錄寫入add\n事件。

static void do_coldboot(DIR *d, int lvl) {
    struct dirent *de;
    int dfd, fd;

    dfd = dirfd(d);

    fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
    if(fd >= 0) {
        //寫入add\n事件
        write(fd, "add\n", 4);
        close(fd);
    }

2.new CommandListener()

前面有講過,CommandListener是用來監聽Socket的,監聽Vold與Framework層的進程通信。它的關係圖如下:
這裏寫圖片描述

它繼承自FrameworkListener,FrameworkListener繼承自SocketListener.
路徑:
CommandListener.cpp———————–system/vold/CommandListener.cpp

這一步創建CommandListener的實例,則構造方法被調用

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());
}

調用registerCmd方法,註冊一些指令,重寫的是父類的registerCmd方法

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

會把指令添加到mCommands 中,mCommands 是FrameworkCommandCollection的實例,在FrameworkListener.h文件中聲明的

class FrameworkListener : public SocketListener {
    ......
private:
    FrameworkCommandCollection *mCommands;
    ......
    }

這裏附上一張NetlinkManager家族的簡單類圖,幫助後續理解
這裏寫圖片描述

3.vm->start()

啓動VolumeManager
VolumeManager模塊負責管理所有掛載的設備節點以及相關操作的實際執行,
路徑:
VolumeManager.cpp———————–system/vold/VolumeManager.cpp

int VolumeManager::start() {

    //卸載所有設備
    unmountAll();

    //智能指針創建一個VolumeBase實例
    mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
            new android::vold::EmulatedVolume("/data/media"));
    //調用create()方法
    mInternalEmulated->create();

    return 0;
}

①卸載所有設備
②創建一個VolumeBase實例
③調用VolumeBase的create方法

這裏附上一張VolumeManager家族的簡單類圖,幫助後續理解

這裏寫圖片描述

看下第③個步驟

3.1 mInternalEmulated->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發送了VolumeCreated的消息,然後設置狀態爲已卸載.
這個notifyEvent的實現如下

3.2 notifyEvent

void VolumeBase::notifyEvent(int event, const std::string& value) {
    if (mSilent) return;
    VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
}

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

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中寫入信息

3.3 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會收到,後續再細講.

4.nm->start()

這一步啓動NetlinkManager
NetlinkManager
路徑:
NetlinkManager.cpp———————–system/vold/NetlinkManager.cpp

NetlinkManager模塊接收從Kernel通過Netlink機制發送過來的Uevent消息,解析轉換成NetlinkEvent對象,再將此NetlinkEvent對象傳遞給VolumeManager處理。

start方法如下

int NetlinkManager::start() {
    ......
    //創建Socket
    if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
            NETLINK_KOBJECT_UEVENT)) < 0) {
        SLOGE("Unable to create uevent socket: %s", strerror(errno));
        return -1;
    }
    //設置Socket的SO_RCVBUFFORCE大小
    if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
        SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
        goto out;
    }
    //設置Socket的SO_PASSCRED大小
    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
        goto out;
    }
    //綁定Socket
    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
        SLOGE("Unable to bind uevent socket: %s", strerror(errno));
        goto out;
    }

    //創建一個NetlinkHandler對象,並把創建好的Socket句柄傳給它。
    mHandler = new NetlinkHandler(mSock);
    //啓動NetlinkHandler
    if (mHandler->start()) {
        SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
        goto out;
    }
    return 0;
    ......

}

①創建Socket,爲PF_NETLINK類型
②設置Socket的SO_RCVBUFFORCE(接受緩存區)小
③設置Socket的SO_PASSCRED大小
④創建一個NetlinkHandler對象,並啓動它

主要看第④步

4.1 mHandler->start()

構造函數傳入了mSock,然後調用了NetlinkHandler的start方法
看一下它的實現
路徑:
NetlinkHandler.cpp———————–system/vold/NetlinkHandler.cpp

NetlinkHandler::NetlinkHandler(int listenerSocket) :NetlinkListener(listenerSocket) {
}

int NetlinkHandler::start() {
    //startListener由SocketListener實現
    return this->startListener();
}

void NetlinkHandler::onEvent(NetlinkEvent *evt) {
    VolumeManager *vm = VolumeManager::Instance();
    const char *subsys = evt->getSubsystem();
    // VolumeManager 調用handleBlockEvent處理事件
    if (!strcmp(subsys, "block")) {
        vm->handleBlockEvent(evt);
    }
}

NetlinkHandler繼承自NetlinkListener,NetlinkListener繼承自SocketListener,三者關係如下

這裏寫圖片描述

4.2 startListener

會在父類SocketListener中實現,調用startListener方法

路徑:
SocketListener.cpp————————–system/core/libsysutils/src/SocketListener.cpp

SocketListener

int SocketListener::startListener(int backlog) {
     ...... 
    //創建線程執行函數threadStart  
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
        SLOGE("pthread_create (%s)", strerror(errno));
        return -1;
    }
    return 0;
}

會開啓一個線程,那麼繼續看threadStart方法

void *SocketListener::threadStart(void *obj) {
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);
    //調用runListener方法
    me->runListener();
    pthread_exit(NULL);
    return NULL;
}

調用runListener

4.3 runListener

void SocketListener::runListener() {

    SocketClientCollection pendingList;
        ......
        while (!pendingList.empty()) {

            it = pendingList.begin();
            SocketClient* c = *it;
            pendingList.erase(it);
             //onDataAvailable方法處理有數據發送的socket 
            if (!onDataAvailable(c)) {
                release(c, false);
            }
            c->decRef();
        }
    }
}

會調用onDataAvailable方法,改方法由子類NetlinkListener和FrameWorkListener實現,所以這裏要分兩條線4.4和4.5。

4.4 onDataAvailable

由子類FrameWorkListener實現這個方法

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消息,然後分發命令

4.4.1 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());
}

這裏以其中一個指令爲VolumeCmd例,會進入到VolumeCmd的runCommand方法

4.4.2 CL.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);

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

        return sendGenericOkFail(cli, vol->unmount());
        ......
    }  
}

會根據cmd的不同做相應的處理,在”cmd=mount”操作中,就有

int res = vol->mount();

4.4.3 vol->mount()

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

執行操作之後會發送應答消息給MountService,這個後續再細講。

這條線結束了。下面介紹另一條線。回到4.3 runListener,往下走到4.5 onDataAvailable
由子類NetlinkListener實現

4.5 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);
    } 
    ......
}

NetlinkListener沒有實現這個方法,由子類NetlinkHandler實現

4.5.1 onEvent

void NetlinkHandler::onEvent(NetlinkEvent *evt) {
    VolumeManager *vm = VolumeManager::Instance();
    const char *subsys = evt->getSubsystem();

    if (!subsys) {
        SLOGW("No subsystem found in netlink event");
        return;
    }

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

獲取VolumeManager 單例,調用handleBlockEvent方法

4.5.2 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<android::vold::Disk>(disk));
                break;
            }
        }
        break;
    }
    case NetlinkEvent::Action::kChange: {
        ......
        break;
    }
    case NetlinkEvent::Action::kRemove: {
        ......
        break;
    }
    ......
    }
}

根據NetlinkEvent 的不同Action值做相應處理,有add,change,remove三個值。在add裏調用了disk->create()

4.5.3 disk->create()

路徑:
disk.cpp———————–system/vold/disk.cpp

status_t Disk::create() {
    CHECK(!mCreated);
    mCreated = true;
    //調用notifyEvent方法
    notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
    readMetadata();
    readPartitions();
    return OK;
}

在這一步會調用notifyEvent方法通知SockListener.

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

到這一步驟,會發現又回到了3.2 notifyEvent步驟了,之後就跟着3.2繼續走了,這裏就不重複介紹了.

四、Vold進程總結

關於Vold的整體流程的講完了,可以跟着我繪製的時序圖跟着步驟走,來回多看幾次,注意圖文結合.

後面會針對

1.由SystemServer進程發起掛載/卸載請求

2.由Kernel發起掛載/卸載請求

這兩種情況做更具體的分析,分析源碼切記:先整體後局部,不然就沒有頭緒了,只見樹木不見森林.

PS:水平有限,有不對的地方歡迎一起探討

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章