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

 

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; 
  
          
  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 
  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的挂载方法。

这里,无论是SDCARD或者USB硬盘在主动挂载时,都会走DirectVolume。

 

 手动挂载

手动挂载是由上层发Mount 命令,代码位于MountService里面的doMountVolume方法,具体如何实现我们先不深究,它这里通过发送socket(mount)命令到Vold 的CommandListener里面的CommandListener::VolumeCmd::runCommand方法进入代码这里:


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 方法,只需传入挂载点。该方法代码是:



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

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

      
  return 
   v->mountVol(); 
  
 }

 可以看出,这里同样调用的是Volume的mountVol方法,殊途同归,接下来着重看一下Volume类里面这个mountVol方法,究竟干了些啥。

Volume::mountVol 方法深究

 别的先不管,来看一下代码

 



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,  
  false,  
  false,  
  1000,  
  1015,  
  0702,  
  true)) { 
  
                 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挂载的支持和多分区挂载的支持,但基本流程是相同的。


 代码有详细的注释,这里要注意的是:sdcard和USB的支持不同,sdcard 挂载时需要先挂载到临时目录/mnt/secure/staging,然后再移动到最终需要挂载的挂载点,而USB硬盘特别是多分区的支持,不用先挂载到临时目录,而是可以支持挂载到想要挂载的挂载点,这里是比较需要注意到的地方(在这里栽过跟头,会出现“随机性的挂载失败”)。

ok.