TShopping

 找回密碼
 註冊
搜索
TShopping 精選文章 Android 手機開發 查看內容

android vold初始化及sd卡掛載流程

2014-3-2 23:13| 發佈者: woff| 查看: 11227| 評論: 2|原作者: woff

摘要: 1 總體介紹 在Android 中,當SD卡插入系統之後,系統會自動掛載。Vold 就是負責掛載SD卡的,vold 的全稱是volume daemon。實際上是負責完成系統的CDROM,USB 大容量存儲,MMC 卡等擴展存儲的掛載任務自動完成的守護進 ...
 
1 總體介紹
  在Android 中,當SD卡插入系統之後,系統會自動掛載。Vold 就是負責掛載SD卡的,vold 的全稱是volume daemon。實際上是負責完成系統的CDROM,USB 大容量存儲,MMC 卡等擴展存儲的掛載任務自動完成的守護進程。它提供的主要特點是支持這些存儲外設的熱插拔。

1.1總體流程圖
1.jpg
O         綠色箭頭:表示插入SD卡後事件傳遞以及SD卡掛載
O         紅色箭頭:表示掛載成功後的消息傳遞流程
O         黃色箭頭:表示MountService發出掛載/卸載SD卡的命令

1.2總體類圖

2.jpg
        main.cpp,vold的入口函數,系統起來會只執行vold的可執行文件,調到這個main函數中。
        NetlinkManager.cpp位於源碼位置/system/vold/NetlinkManager.cpp。該類的主要通過引用NetlinkHandler類中的onEvent()方法來接收來自內核的事件消息,NetlinkHandler位於/system/vold/NetlinkHandler.cpp。
        VolumeManager:位於源碼位置/system/vold/VolumeManager.cpp。該類的主要作用是接收經過NetlinkManager處理過後的事件消息。
        DirectVolume:位於/system/vold/DirectVolume.cpp。該類的是一個工具類,主要負責對傳入的事件進行進一步的處理,block事件又可以分為:Add,Removed,Change,Noaction這四種。
        Volume:Volume.cpp位於/system/vold/Volume.cpp,該類是負責SD卡掛載的主要類。Volume.cpp主要負責檢查SD卡格式,以及對復合要求的SD卡進行掛載,並通過Socket將消息SD卡掛載的消息傳遞給NativeDaemonConnector。

總的講,vold程序需要分層三部分,第一部分為NetlinkManager,管理接受來自kernel的UEvent消息,第二部分為VolumeManager,主要負責處理來自NetlinkManager的消息和來自java層的消息,之後真正的掛載卸載動作就需要volume負責了。

2 初始化流程2.1 時序圖
3.jpg
2.2 代碼分析
在Android 系統啟動的時候,init進程會去解析init.rc文件,在該文件中,有如下代碼:
service vold /system/bin/vold
    class core
    socket vold stream 0660 root mount
    ioprio be 2

定義了一個vold的service,去執行vold程序,並創建了一個名字為vold的socket,init進程解析完後就去執行vold程序,創建與java層通信的Socket。
      在Android 源碼/system/vold路徑下的main.cpp,這個就是vold程序的入口,我們看看起main函數,代碼如下:
  1. int main() {
  2.     VolumeManager *vm;
  3.     CommandListener *cl;
  4.     NetlinkManager *nm;
  5.     if (!(vm = VolumeManager::Instance())) {//創建VolumeManager實例
  6.     };

  7.     if (!(nm = NetlinkManager::Instance())) {//創建NelinkManager實例
  8.     };
  9.     cl = new CommandListener(); //創建與java層socket通信的接口
  10.     vm->setBroadcaster((SocketListener *) cl);
  11.     nm->setBroadcaster((SocketListener *) cl);
  12.     if (vm->start()) { //什麼都沒做
  13.     }
  14.     if (process_config(vm)) {//初始化fstab
  15.         SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
  16.     }
  17.     if (nm->start()) {//開始監聽kernel上報的vold消息
  18. }
  19. ……
  20.     if (cl->startListener()) {//開始監聽來自java層的socket消息
  21.         SLOGE("Unable to start CommandListener (%s)", strerror(errno));
  22.         exit(1);
  23.     }
  24.     while(1) {
  25.         sleep(1000);
  26.     }
  27.     exit(0);
  28. }
複製代碼
    首先,在main函數中,需要創建VolumeManagerNetlinkManager的實例,裡面就做了一些初始化的動作,這裡就不多說了。
接著,則是初始化vold與java層的socket通信接口。創建了的CommandListener實例。在上面的類圖關係中,我們知道,CommandListener繼承於FrameworkListener,而FrameworkListener有繼承於SocketListener。先看看CommandListener的初始化代碼:
  1. CommandListener::CommandListener() :
  2.                  FrameworkListener("vold") {
  3.     registerCmd(new DumpCmd());
  4.     registerCmd(new VolumeCmd()); //處理volume事件
  5.     registerCmd(new AsecCmd());
  6.     registerCmd(new ObbCmd());
  7.     registerCmd(new StorageCmd());
  8.     registerCmd(new XwarpCmd());
  9.     registerCmd(new CryptfsCmd());
  10. }
複製代碼
    在上面代碼中我們看到,先以「vold」為參數構造FrameworkListener類,完成之後,則調用FrameworkListener類中的registerCmd()方法,註冊一些處理方法類,而對於sd卡掛載的事件,我們先關注VolumeCmd類,它是FrameworkListener的內部類,用於處理Volume事件。接下來,看FrameworkListener的構造函數:
  1. FrameworkListener::FrameworkListener(const char *socketName) :SocketListener (socketName, true) {
  2.     mCommands = new FrameworkCommandCollection();
  3. }
複製代碼

    以之前傳進來的「vold」參數構造SocketListener類,然後在FrameworkListener構造函數中,創建FrameworkCommandCollection的實例,其實它就是一個容器,用於存儲之前調用的registerCmd()註冊的處理方法類。下面就看SocketListener的構造函數:
  1. SocketListener::SocketListener(const char *socketName, bool listen) {
  2.     mListen = listen;
  3.     mSocketName = socketName; /將vold字符串存儲在mSocketName變量中
  4.     mSock = -1;
  5.     pthread_mutex_init(&mClientsLock, NULL);
  6.     mClients = new SocketClientCollection(); //創建socket客戶端容器
  7. }
複製代碼
  其實很簡單,就是做了一些變量的初始化工作,用mSocketName變量存儲「vold」字符串,這個vold是很有講究的,因為是init.rc定義的vold Service中,就創建了一個名字為voldsocket端口,後面將通過「vold」獲取到該 socket端口。
到此,CommandListener的初始化就完成了的。
我們回到main函數中,創建了CommandListener實例之後,然後調用VolumeManger的setBroadcaster方法,將CommandListener的實例存儲在mBroadcaster變量中,代碼如下:
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

其實NetlinkManager也做了同樣的設置,但我還沒發現它有什麼用,所以就不再關注了。
接下來就開始調用了main.cpp的process_config()方法了,在介紹之前,我必須先介紹下vold.fstab配置文件,這個配置文件就是在process_config()中被解析的,而vold.fstab配置文件,就是用於描述vold的掛載動作的,其配置例子如下:
dev_mount        sdcard         /mnt/sdcard         
auto     /devices/platform/goldfish_mmc.0   
掛載命令            標籤           掛載點              子分區個數               掛載路徑        

我們就以上面例子來說明,意思就是將/devices/platform/goldfish_mmc.0掛載到/mnt/sdcard中,/devices/platform/goldfish_mmc.0可以認為是kernel上報上來的路徑。子分區個數如果為auto則表示只有1個子分區,也可以為任何不為0的整數。如果vold.fstab解析無誤,VolueManager將創建DirectVolume。
好了,下面可以看 process_config()方法了,代碼如下:
  1. static int process_config(VolumeManager *vm) {
  2.     FILE *fp;
  3.     int n = 0;
  4.     char line[255];
  5.     if (!(fp = fopen("/etc/vold.fstab", "r"))) {
  6.         return -1;
  7.     }
  8.     while(fgets(line, sizeof(line), fp)) {
  9.         const char *delim = " \t";
  10.         char *save_ptr;
  11.         char *type, *label, *mount_point, *mount_flags, *sysfs_path;
  12.         int flags;
  13.         n++;
  14.         line[strlen(line)-1] = '\0';

  15.         if (line[0] == '#' || line[0] == '\0')
  16.             continue;
  17.         if (!(type = strtok_r(line, delim, &save_ptr))) {
  18.             goto out_syntax;
  19.         }
  20.         if (!(label = strtok_r(NULL, delim, &save_ptr))) {
  21.             goto out_syntax;
  22.         }
  23.         if (!(mount_point = strtok_r(NULL, delim, &save_ptr))) {
  24.             goto out_syntax;
  25.         }
  26.         if (!strcmp(type, "dev_mount")) {
  27.             DirectVolume *dv = NULL;
  28.             char *part;
  29.             if (!(part = strtok_r(NULL, delim, &save_ptr))) {
  30.                 goto out_syntax;
  31.             }
  32.             if (strcmp(part, "auto") && atoi(part) == 0) {
  33.                 goto out_syntax;
  34.             }
  35.             if (!strcmp(part, "auto")) {//如果解析沒有錯,那麼就將創建DirectVolume
  36.                 dv = new DirectVolume(vm, label, mount_point, -1);
  37.             } else {
  38.                 dv = new DirectVolume(vm, label, mount_point, atoi(part));
  39.             }
  40.             while ((sysfs_path = strtok_r(NULL, delim, &save_ptr))) {
  41.                 if (*sysfs_path != '/') {
  42.                     break;
  43.                 }
  44.                 if (dv->addPath(sysfs_path)) {
  45.                     goto out_fail;
  46.                 }
  47.             }
  48.             if (sysfs_path)
  49.                 flags = parse_mount_flags(sysfs_path);
  50.             else
  51.                 flags = 0;
  52.             dv->setFlags(flags);
  53.             vm->addVolume(dv); //將創建的DirectVolume添加到VolumeManager中。
  54.         } else if (!strcmp(type, "map_mount")) {
  55.         } else {
  56.         }
  57.     }
  58.     fclose(fp);
  59.     return 0;
  60. }
複製代碼

    該方法,通過一個wihle方法,逐行進行解析,如果認為合理,那麼將拿到的信息用於創建DirectVolume實例,然後調用VolumeManageraddVolume方法,存儲在mVolumes變量中。
好了,下面就開始看註冊監聽kernel的sockect端口了。就是NetLinkManager的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();
    nladdr.nl_groups = 0xffffffff;
    if ((mSock = socket(PF_NETLINK,//創建socket,返回文件描述符
                        SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
        SLOGE("Unable to create uevent socket: %s", strerror(errno));
        return -1;
    }
    if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
        SLOGE("Unable to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno));
        return -1;
    }
    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
        return -1;
    }
    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
        SLOGE("Unable to bind uevent socket: %s", strerror(errno));
        return -1;
    }
    mHandler = new NetlinkHandler(mSock);
    if (mHandler->start()) {
        return -1;
    }
    return 0;
}

其實就是調用socket()創建socket端口,返回描述符,經過一些設置,然後就描述符作為參數,創建的NetlinkHandler實例,然後就直接調用起start方法。看NetLinkHandler構造函數:
NetlinkHandler::NetlinkHandler(int listenerSocket) :
                NetlinkListener(listenerSocket) {
}
構造函數里什麼都沒做,NetlinkHandler繼承於NetlinkListener,然後講socket端口的描述符傳進去。
NetlinkListener::NetlinkListener(int socket) :
                            SocketListener(socket, false) {
    mFormat = NETLINK_FORMAT_ASCII;
}

又是這麼幾句代碼,NetlinkListener也是繼承於SocketListener,所以還將socket描述符傳進去,再次創建了SocketListener的實例,所以,在vold系統中,有兩個SocketListener的實例。看其構造函數,這裡的構造函數與之前的是不一樣的,代碼如下:
SocketListener::SocketListener(int socketFd, bool listen) {
    mListen = listen;
    mSocketName = NULL;
    mSock = socketFd;
    pthread_mutex_init(&mClientsLock, NULL);
    mClients = new SocketClientCollection();
}

其實,與上面的構造函數,還是差不多的,只是傳進來的參數不一樣而已,之前的是一個「vold」字符串,而這裡是一個socket的描述符。
好了,構造函數創建好了,那麼接著看NetlinkHandler->start()方法:
int NetlinkHandler::start() {
    return this->startListener();//指到了socketListener中了
}

這裡的startListener方法是SocketListener中的,代碼如下:
int SocketListener::startListener() {
    if (!mSocketName && mSock == -1) {
        return -1;
    } else if (mSocketName) {
        if ((mSock = android_get_control_socket(mSocketName)) < 0) {
            return -1;
        }
    }
    if (mListen && listen(mSock, 4) < 0) {
        return -1;
    } else if (!mListen)
        mClients->push_back(new SocketClient(mSock, false));//創建socket客戶端,並添加到mClients容器中。
    if (pipe(mCtrlPipe)) {
        return -1;
    }
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//創建新線程
        return -1;
    }
    return 0;
}

此時的條件下,mSocketName=null,mSock!=0,繼續往下看,創建了SocketClient實例,並添加到mClients容器中,用於接收客戶端發過來的消息。
接著創建新的一個線程,用於讀取socket客戶端發過來的消息,線程執行的方法如下:
void *SocketListener::threadStart(void *obj) {
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);
    me->runListener();
    pthread_exit(NULL);
    return NULL;
}

看runListener()方法:
void SocketListener::runListener() {
    SocketClientCollection *pendingList = new SocketClientCollection();
    while(1) {
        ……
        for (it = mClients->begin(); it != mClients->end(); ++it) {
            int fd = (*it)->getSocket();
            if (FD_ISSET(fd, &read_fds)) {
                pendingList->push_back(*it);
            }
        }
        pthread_mutex_unlock(&mClientsLock);
        while (!pendingList->empty()) {//客戶端有消息
            it = pendingList->begin();
            SocketClient* c = *it;
            pendingList->erase(it);
            if (!onDataAvailable(c) && mListen) {//  處理消息         
            }
        }
    }
    delete pendingList;
}

在該方法中,一個while循環,不斷讀取socket消息,如果發現有socket消息,那麼就調用方法onDataAvailable處理,該方法是在NetlinkListener方法實現的,其代碼如下:
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
    int socket = cli->getSocket();
    ssize_t count;
    count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, sizeof(mBuffer)));
    if (count < 0) {
        SLOGE("recvmsg failed (%s)", strerror(errno));
        return false;
    }
    NetlinkEvent *evt = new NetlinkEvent();
    if (!evt->decode(mBuffer, count, mFormat)) {
        SLOGE("Error decoding NetlinkEvent");
    } else {
        onEvent(evt);//NetlinkHandler被實現
    }
    return true;

就是經過了處理,跳轉到了NetlinkHandler的onEvent()方法處理。好了,註冊kernel監聽就到此先搞一段落了。
回到了main函數中,最後,看到調用了CommandListener->startListener(),其實就是調用了SocketListener中的startListener方法。代碼就不再次貼出來了,同樣也是創建了一個新的線程讀取socket消息,只是,發現有消息後,調用的是FrameworkListener中的onDataAvailable方法處理。
好了,到此,vold的初始化已經完成了。下面看看sd的mount流程吧。

3 SDmount流程
3.1時序圖
4.jpg

3.2 流程圖
5.jpg

3.3 代碼分析
經過前面的介紹,我們知道了,在NetlinkHandler的onEvent方法中,收到了kernel的消息。其代碼如下:
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
    VolumeManager *vm = VolumeManager::Instance();
    const char *subsys = evt->getSubsystem();
    if (!subsys) {
        return;
    }
    if (!strcmp(subsys, "block")) {
        vm->handleBlockEvent(evt);//進一步處理
    }
}

這裡就只處理block消息了,看看VolumeManager的handleBlockEvent方法吧:
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
    const char *devpath = evt->findParam("DEVPATH");
    VolumeCollection::iterator it;
    bool hit = false;
    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
        if (!(*it)->handleBlockEvent(evt)) {//DirectVolume處理
            hit = true;
            break;
        }
    }
}

這裡的for循環遍歷mVolumes,它其實是DirectVolume實例列表,在解析vold.fstab中,創建的DirectVolume實例並添加到mVolumes列表中。然後再調用DirectVolume的handleBlockEvent方法嘗試處理該消息,看是否能匹配,起代碼如下:
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
    const char *dp = evt->findParam("DEVPATH");

    PathCollection::iterator  it;
    for (it = mPaths->begin(); it != mPaths->end(); ++it) {//遍歷vold.fstab定義的路徑
        if (!strncmp(dp, *it, strlen(*it))) {//kernel上報上來的路徑與vold.fstab中定義的匹配
           
            int action = evt->getAction();
            const char *devtype = evt->findParam("DEVTYPE");

            if (action == NetlinkEvent::NlActionAdd) {
                int major = atoi(evt->findParam("MAJOR"));
                int minor = atoi(evt->findParam("MINOR"));
                char nodepath[255];

                snprintf(nodepath,
                         sizeof(nodepath), "/dev/block/vold/%d:%d",
                         major, minor);
                if (createDeviceNode(nodepath, major, minor)) {
                }
                if (!strcmp(devtype, "disk")) {//插入設備消息
                   handleDiskAdded(dp, evt);//上報一個物理分區
                } else {
                    handlePartitionAdded(dp, evt);//上報一個邏輯分區
                }
            } else if (action == NetlinkEvent::NlActionRemove) {//拔出設備消息
                if (!strcmp(devtype, "disk")) {
                    handleDiskRemoved(dp, evt);
                } else {
                    handlePartitionRemoved(dp, evt);
                }
            } else if (action == NetlinkEvent::NlActionChange) {//設備狀態改變消息
                if (!strcmp(devtype, "disk")) {
                    handleDiskChanged(dp, evt);
                } else {
                    handlePartitionChanged(dp, evt);
                }
            } else {
                    SLOGW("Ignoring non add/remove/change event");
            }
            return 0;
        }
    }
    errno = ENODEV;
    return -1;
}

Kernel上報上來的消息中,有一個路徑的消息,將與vold.fstab中定義的路徑進行匹配,如果匹配,那麼說明這個消息是有效的,那麼就繼續處理。
那麼,kernel上報的消息也分為三類,分別是設備插入、拔出、狀態改變。我們這裡就先關注插入的消息吧。
那麼,插入的消息,又分是物理分區還是一個邏輯分區。假如插入一個sd卡,它只有一個分區,那麼上報的就是Disk消息。假如插入一個sd卡,該卡有內部又被分成多個分區,那麼就先上報的是一個Dist消息,用於描述這個sd卡,後面還會上報多個消息,每個消息對應sd卡中的一個分區,也就是partition消息。
在這裡,我們關注Dist消息吧,看看handleDiskAdded()方法,代碼如下;
void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
    mDiskMajor = atoi(evt->findParam("MAJOR"));
    mDiskMinor = atoi(evt->findParam("MINOR"));
    const char *tmp = evt->findParam("NPARTS");
    if (tmp) {
        mDiskNumParts = atoi(tmp);//如果上報的是只有一個分區的sd,該變量為0
    } else {
        mDiskNumParts = 1;
    }
       mPartsEventCnt = 0;
    char msg[255];
    int partmask = 0;
    int i;
    for (i = 1; i <= mDiskNumParts; i++) {
        partmask |= (1 << i);
    }
    mPendingPartMap = partmask;
    if (mDiskNumParts == 0) {
        setState(Volume::State_Idle);//設置初始狀態
    } else {
        setState(Volume::State_Pending);
    }
    snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",
             getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);//構造消息
    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,msg, false);//socket消息到java
}

如果是Disk消息,那麼上報的sd卡只有一個分區,所以上面的mDiskNumParts=0。看下面,調用snprintf()構造msg消息,然後調用mVm->getBroadcaster()->sendBroadcast發送到java層。其實mVm->getBroadcaster()就是放回CommandListener的實例變量,sendBroadcast就是在SocketListener中,代碼如下:
void SocketListener::sendBroadcast(const char *msg) {
    pthread_mutex_lock(&mClientsLock);
    SocketClientCollection::iterator i;
    for (i = mClients->begin(); i != mClients->end(); ++i) {
        if ((*i)->sendMsg(msg)) {//發送socket消息
        }
    }
    pthread_mutex_unlock(&mClientsLock);
}

Ok,看到了吧,這裡就發送了一個VolumeDiskInserted的消息到java層。
但如果是系統改啟動的話,kernel早早就發來了消息,但是java層還沒起來呢。所以,等到mountService起來之後,就收到了socket消息了。
我們直接看mountService的onEvent()方法吧代碼如下:
public boolean onEvent(int code, String raw, String[] cooked) {
   …….
if (code == VoldResponseCode.VolumeDiskInserted) {
   new Thread() {
    public void run() {
           try {
             int rc;
             if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
                      Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
                            }
               } catch (Exception ex) {
                      }
                    }
                }.start();
            } else if (code == VoldResponseCode.VolumeDiskRemoved) {
             }
}

這裡我們只看onEvent的處理VoldResponseCode.VolumeDiskInserted消息,我們看到,對於VolumeDiskInserted消息,mountService立刻調用了方法doMountVolume(path),其實就是通過socket對vold發送了一個條mount的命令。
所以對與java層來講,可以發mount、unmount消息到vold中。那麼現在,就看vold處理吧。
前面也介紹過,java層發送的socket消息,vold層在SocketListener中讀取到,然後會在FrameworkListener的onDataAvailable()方法中處理,代碼如下:
bool FrameworkListener::onDataAvailable(SocketClient *c) {
    char buffer[255];
    int len;
    len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
    if (len < 0) {
        return false;
    } else if (!len)
        return false;
    int offset = 0;
    int i;
    for (i = 0; i < len; i++) {
        if (buffer == '\0') {
            dispatchCommand(c, buffer + offset);//開始派發消息
            offset = i + 1;
        }
    }
    return true;
}

調用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));
            }
    }
}

一堆的處理,代碼也就不貼出來了,直接看關鍵的部分吧。記得在CommandListener的構造函數中嗎,裡面調用了FrameworkListener的registerCmd()方法,註冊了一些處理方法類,其實就是添加到了mCommands容器中了,這裡當然需要遍歷咯,找到其合適的處理方法類,然後調用其runComand()方法,看看其代碼吧:
int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
                                                      int argc, char **argv) {
    dumpArgs(argc, argv, -1);
    …….
    VolumeManager *vm = VolumeManager::Instance();
    int rc = 0;
    if (!strcmp(argv[1], "list")) {
        return vm->listVolumes(cli);
    } else if (!strcmp(argv[1], "debug")) {
    } else if (!strcmp(argv[1], "mount")) {//處理mount消息
       rc = vm->mountVolume(argv[2]);
    } else if (!strcmp(argv[1], "unmount")) {
        rc = vm->unmountVolume(argv[2], force, revert);
} else if (!strcmp(argv[1], "format")) {  //處理格式化消息
        rc = vm->formatVolume(argv[2]);
    } else if (!strcmp(argv[1], "share")) { //處理掛載到pc消息
        rc = vm->shareVolume(argv[2], argv[3]);
    } else if (!strcmp(argv[1], "unshare")) {
        rc = vm->unshareVolume(argv[2], argv[3]);
    } else if (!strcmp(argv[1], "shared")) {
        bool enabled = false;
        if (vm->shareEnabled(argv[2], argv[3], &enabled)) {
    }
    return 0;
}


在這裡處理Volume消息,我們就只看mount消息吧,就調用了VolumeManager的mountVolume方法,代碼如下:
int VolumeManager::mountVolume(const char *label) {
    Volume *v = lookupVolume(label);//找到該掛載點的Volume的實例
    if (!v) {
        return -1;
    }
    return v->mountVol();//去掛載啦
}

到Volume的mountVol()中掛載,代碼如下:
int Volume::mountVol() {
  …..
}

這個方法代碼量比較大,就不貼出來了,但是完成mount的動作就是在該方法中,然後呢,Volume中還包含了其他的功能方法,比如unmount、share、unshare。
好了,花了一個下午的時間整理出來,vold的初始化即sd卡的掛載流程就講解到這吧,我這裡講流程的比較多,很多細節問題也沒有講,其實我寫的文檔,還是比較註冊流程,消息是怎麼傳遞的,至於細節,用到的時候,再詳細看!




發表評論

最新評論

引用 呵呵 2013-12-14 20:41
牛X海海!!!
引用 david0zan 2013-12-14 23:09
頂!好貼   好文~~~一定要分享出去.

查看全部評論(2)



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2024-4-19 18:28 , Processed in 0.035984 second(s), 24 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

返回頂部