SDCard Mount 流程分析(二)

上一篇關於Mount的分析,分析了main的做用和一些掛載系統的分析。下面深刻分析Mount的流程走法。node

 

Mount流程分爲兩個部分

 

  • 主動掛載(插入SDCARD或者USB硬盤時系統自動掛載)
  • 手動掛載(卸載SDCARD或者USB硬盤後,再點擊加載設備的手動掛載) 
不一樣掛載走的流程並不相同,好比手動掛載是由上層發命令給vold 執行掛動做,而主動掛載是由kernel 分命令給vold 再由vold 發掛載消息給上層,上層獲得掛載消息和狀態後再發命令給vold 執行掛載。主動掛載較之複雜些。不過雖然流程不同,但最終仍是要調用Volume的掛載函數,下面將詳細介紹二者的行走的流程。

 

因爲會涉及SDCARD或者USB硬盤,其中調用的方法就不詳細說明,這裏只說出當插入SDCARD或者USB硬盤會走的流程。app

 

 主動掛載

 

主動掛載時,會走向DirectVolume類,調用DirectVolume::mountVol方法,代碼以下:socket


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;
         return - 1;
    }
    
    n = getDeviceNodes((dev_t *) &deviceNodes,  64);
     
     if (!n) {
        SLOGE( " Failed to get device nodes (%s)\n ", strerror(errno));
         return - 1;
    }
     bool mounted =  false;
    
     for  (i = 0; i < n; i++) {
        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;
        }

    
         if (!Volume::mountVol()) {
            mounted = true;
        }

        
        mState = Volume::State_Idle;
     }
    
     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的掛載方法。ide

這裏,不管是SDCARD或者USB硬盤在主動掛載時,都會走DirectVolume。函數

 

 手動掛載

手動掛載是由上層發Mount 命令,代碼位於MountService裏面的doMountVolume方法,具體如何實現咱們先不深究,它這裏經過發送socket(mount)命令到Vold 的CommandListener裏面的CommandListener::VolumeCmd::runCommand方法進入代碼這裏:ui

else  if (!strcmp(argv[ 1],  " mount ")) {
         if (argc !=  3) {
            cli->sendMsg(ResponseCode::CommandSyntaxError,  " Usage: volume mount <path> "false);
             return  0;
        }
        
         if (!strcmp(argv[2],"firstMount")){
            VolumeCollection::iterator i;
              if(mVolumes!=NULL){
              for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
              if (strcmp("/sdcard", (*i)->getMountpoint())) {
                  vm->mountVolume((*i)->getMountpoint());
               }
            }
         }
        }else{

            vm->mountVolume(argv[2]);
         }
            
    } 

 

 這裏執行掛載動做,看上面藍色代碼是爲了系統第一次啓動上層發送命令firstMount給CommandListener執行掛載USB硬盤的動做,紅色代碼便是核心要掛載的方法,調用的VolumeManage的mountVolume 方法,只需傳入掛載點。該方法代碼是:atom

int VolumeManager::mountVolume( const  char *label) {
    Volume *v = lookupVolume(label);

     if (!v) {
        errno = ENOENT;
         return - 1;
    }

     return  v->mountVol();
}

 

 能夠看出,這裏一樣調用的是Volume的mountVol方法,異曲同工,接下來着重看一下Volume類裏面這個mountVol方法,究竟幹了些啥。spa

Volume::mountVol 方法深究

 別的先無論,來看一下代碼code

 

 int Volume::mountVol() {
     int rc =  0;
     char errmsg[ 255];
     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.
         
*/
        errno =  0;
        //若是爲sdcard則掛載到 /mnt/secure/staging,不然掛載到掛載點
          if(!strcmp(getLabel(), " sdcard "))
            mountPath= " /mnt/secure/staging ";
         else
            mountPath=getMountpoint();
         //接下來就是不一樣格式不一樣的掛載,這裏支持兩種格式:fat32,Ntfs
         if ( isFatFs ) {
             if (Fat::doMount(devicePath,mountPath,  falsefalse100010150702true)) {
                SLOGE( " %s failed to mount via VFAT (%s)\n ", devicePath, strerror(errno));
                
                isFatFs =  false;
            }
            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 && !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);
                 return - 1;
            }
        }
    
         /*
         * 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 ");
              setState(Volume::State_Idle);
                return - 1;
          }
       }
        setState(Volume::State_Mounted);//設置狀態到MountService
        mCurrentlyMountedKdev = mDevNodeIndex;
                
         return  0;
    
}

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


 代碼有詳細的註釋,這裏要注意的是:sdcard和USB的支持不一樣,sdcard 掛載時須要先掛載到臨時目錄/mnt/secure/staging,而後再移動到最終須要掛載的掛載點,而USB硬盤特別是多分區的支持,不用先掛載到臨時目錄,而是能夠支持掛載到想要掛載的掛載點,這裏是比較須要注意到的地方(在這裏栽過跟頭,會出現「隨機性的掛載失敗」)。

ok. 

相關文章
相關標籤/搜索