天天看點

SDCard Mount 流程分析(二)

上一篇關于Mount的分析,分析了main的作用和一些挂載系統的分析。下面深入分析Mount的流程走法。

主動挂載(插入SDCARD或者USB硬碟時系統自動挂載)

手動挂載(解除安裝SDCARD或者USB硬碟後,再點選加載裝置的手動挂載) 

不同挂載走的流程并不相同,比如手動挂載是由上層發指令給vold 執行挂動作,而主動挂載是由kernel 分指令給vold 再由vold 發挂載消息給上層,上層得到挂載消息和狀态後再發指令給vold 執行挂載。主動挂載較之複雜些。不過雖然流程不一樣,但最終還是要調用Volume的挂載函數,下面将詳細介紹兩者的行走的流程。

由于會涉及SDCARD或者USB硬碟,其中調用的方法就不詳細說明,這裡隻說出當插入SDCARD或者USB硬碟會走的流程。

主動挂載時,會走向DirectVolume類,調用DirectVolume::mountVol方法,代碼如下:

int DirectVolume::mountVol() {

    char errmsg[255];

    dev_t deviceNodes[64];

    int i, n = 0;

    if (getState() == Volume::State_NoMedia) {

        snprintf(errmsg, sizeof(errmsg),

                 "Volume %s %s mount failed - no media",

                 getLabel(), getMountpoint());

        mVm->getBroadcaster()->sendBroadcast(

                                         ResponseCode::VolumeMountFailedNoMedia,

                                         errmsg, false);

        errno = ENODEV;

        return -1;

    } else if (getState() != Volume::State_Idle) {

        errno = EBUSY;

    }

    n = getDeviceNodes((dev_t *) &deviceNodes, 64);

    if (!n) {

        SLOGE("Failed to get device nodes (%s)\n", strerror(errno));

    bool mounted = false;

    <b>for</b><b> (i = 0; i &lt; n; i++) {</b>

        mDevNodeIndex = deviceNodes[i];

        //XXX: hack mountpoint

        if ( mMountpointParsed ) { free(mMountpointParsed); mMountpointParsed = NULL; }

        mMountpointParsed = getParsedMountPoint(mMountpoint, i);

        if (isMountpointMounted(getMountpoint())) {

            SLOGW("Volume is idle but appears to be mounted - fixing");

            setState(Volume::State_Mounted);

            // mCurrentlyMountedKdev = XXX

            errno = EBUSY;

            continue;

        }

       <b> if (!Volume::mountVol()) {</b>

            mounted = true;

        mState = Volume::State_Idle;

   <b> }</b>

    if ( mMountpointParsed ) { free(mMountpointParsed); mMountpointParsed = NULL; }

    if ( mounted ) {

        // at least on partition has been mounted successful, mark disk as mounted

        setState(Volume::State_Mounted);

        return 0;

    SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());

    setState(Volume::State_Idle);

    return -1;

}

 代碼加亮部分,藍色部分,會循環整個裝置節點系統目錄位于(/dev/block/vold),然後調用紅色部分代碼,調用Volume的挂載方法。

這裡,無論是SDCARD或者USB硬碟在主動挂載時,都會走DirectVolume。

手動挂載是由上層發Mount 指令,代碼位于MountService裡面的doMountVolume方法,具體如何實作我們先不深究,它這裡通過發送socket(<b>mount</b>)指令到Vold 的CommandListener裡面的<b>CommandListener::VolumeCmd::runCommand</b>方法進入代碼這裡:

else if (!strcmp(argv[1], "mount")) {

        if (argc != 3) {

            cli-&gt;sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount &lt;path&gt;", false);

            return 0;

        <b>if</b><b>(!strcmp(argv[2],"firstMount")){</b>

            VolumeCollection::iterator i;

              if(mVolumes!=NULL){

              for (i = mVolumes-&gt;begin(); i != mVolumes-&gt;end(); ++i) {

              if (strcmp("/sdcard", (*i)-&gt;getMountpoint())) {

                  vm-&gt;mountVolume((*i)-&gt;getMountpoint());

               }

            }

         }

        }else{

           <b>vm-&gt;mountVolume(argv[2]);</b>

        <b>}</b>

    } 

 這裡執行挂載動作,看上面藍色代碼是為了系統第一次啟動上層發送指令firstMount給CommandListener執行挂載USB硬碟的動作,紅色代碼即是核心要挂載的方法,調用的VolumeManage的<b>mountVolume </b>方法,隻需傳入挂載點。該方法代碼是:

int VolumeManager::mountVolume(const char *label) {

    Volume *v = lookupVolume(label);

    if (!v) {

        errno = ENOENT;

    <b>return</b><b> v-&gt;mountVol()</b>;

 可以看出,這裡同樣調用的是Volume的mountVol方法,殊途同歸,接下來着重看一下Volume類裡面這個mountVol方法,究竟幹了些啥。

 别的先不管,來看一下代碼

 int Volume::mountVol() {

    int rc = 0;

    const char *mountPath;

        char devicePath[255];

        sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(mDevNodeIndex),

                MINOR(mDevNodeIndex));//得到裝置節點,如:/dev/block/vold/8:1

        SLOGI("%s being considered for volume %s ...major : %d minor: %d\n", devicePath, getLabel(),

         MAJOR(mDevNodeIndex),MINOR(mDevNodeIndex));

        errno = 0;

        setState(Volume::State_Checking);//設定狀态為checking整型為3

        // TODO: find a way to read the filesystem ID

        bool isFatFs = true;

        bool isNtfsFS = true;

         //檢查裝置格式是否為Fat32

        if (Fat::check(devicePath)) {

            if (errno == ENODATA) {

                SLOGW("%s does not contain a FAT filesystem\n", devicePath);

                isFatFs = false;

            } else {

              errno = EIO;

              /* Badness - abort the mount */

              SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));

              setState(Volume::State_Idle);

              return -1;

        //建立挂載目錄

       // create mountpoint

        if (mkdir(getMountpoint(), 0755)) {

            if (errno != EEXIST) {

                SLOGE("Failed to create mountpoint %s (%s)", getMountpoint(), strerror(errno));

                return -1;

        /*

         * Mount the device on our internal staging mountpoint so we can

         * muck with it before exposing it to non priviledged users.

         */

        //如果為sdcard則挂載到/mnt/secure/staging,否則挂載到挂載點

         if(!strcmp(getLabel(),"sdcard"))

            mountPath="/mnt/secure/staging";

        else

            mountPath=getMountpoint();

         //接下來就是不同格式不同的挂載,這裡支援兩種格式:fat32,Ntfs

        if ( isFatFs ) {

            if (Fat::doMount(devicePath,mountPath, false, false, 1000, 1015, 0702, true)) {

                SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));

            isNtfsFS = false;

        if ( isNtfsFS ) {

            if (Ntfs::doMount(devicePath, mountPath, true)) {

                SLOGE("%s failed to mount via NTFS (%s)\n", devicePath, strerror(errno));

                isNtfsFS = false;

        if ( !isFatFs &amp;&amp; !isNtfsFS ) {

            // unsupported filesystem

            return -1;

        SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());

        if ( !strcmp(getLabel(), "sdcard") ) {

            protectFromAutorunStupidity();

            if (createBindMounts()) {

                SLOGE("Failed to create bindmounts (%s)", strerror(errno));

                umount("/mnt/secure/staging");

                setState(Volume::State_Idle);

         * Now that the bindmount trickery is done, atomically move the

         * whole subtree to expose it to non priviledged users.

         * 如果為sdcard則将/mnt/secure/staging 目錄移動到挂載點,并将該目錄unmount

        if(!strcmp(getLabel(),"sdcard")){

          if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {

              SLOGE("Failed to move mount (%s)", strerror(errno));

              umount("/mnt/secure/staging");

               return -1;

          }

       }

        setState(Volume::State_Mounted);//設定狀态到MountService

        mCurrentlyMountedKdev = mDevNodeIndex;

注意:原生的代碼可能跟上面貼出來的代碼有點不同,上面的代碼是增加了Ntfs-3g挂載的支援和多分區挂載的支援,但基本流程是相同的。

 代碼有詳細的注釋,這裡要注意的是:sdcard和USB的支援不同,sdcard 挂載時需要先挂載到臨時目錄/mnt/secure/staging,然後再移動到最終需要挂載的挂載點,而USB硬碟特别是多分區的支援,不用先挂載到臨時目錄,而是可以支援挂載到想要挂載的挂載點,這裡是比較需要注意到的地方(在這裡栽過跟頭,會出現“随機性的挂載失敗”)。

ok. 

 本文轉自 terry_龍 51CTO部落格,原文連結:http://blog.51cto.com/terryblog/832082,如需轉載請自行聯系原作者