博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android SDCard Mount 流程分析(二)
阅读量:4947 次
发布时间:2019-06-11

本文共 5912 字,大约阅读时间需要 19 分钟。

上一篇关于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
 (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的挂载方法。

这里,无论是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. 

 

 

转载于:https://www.cnblogs.com/TerryBlog/archive/2012/04/12/2443443.html

你可能感兴趣的文章
转载:Linux命令行快捷键
查看>>
多个viewpager可能产生的问题
查看>>
webdriver api
查看>>
转载-FileZilla Server源码分析(1)
查看>>
apache 实现图标缓存客户端
查看>>
MediaWiki左侧导航栏通过特殊页面就可以设置。
查看>>
html基础之DOM操作
查看>>
几种图表库
查看>>
01、python入门
查看>>
Python +urllib+urllib2 带数据的post请求实例
查看>>
深入理解操作系统:程序机器级表示---读书笔记(一)
查看>>
HTML页面去缓存
查看>>
技能get,React的优雅升级!
查看>>
unity 实现树木生长的插件
查看>>
揭秘:黑客必备的Kali Linux是什么,有哪些弊端?
查看>>
linux系统的远程控制方法——学神IT教育
查看>>
springboot+mybatis报错Invalid bound statement (not found)
查看>>
Linux环境下SolrCloud集群环境搭建关键步骤
查看>>
SPARK安装一:Windows下VirtualBox安装CentOS
查看>>
P3565 [POI2014]HOT-Hotels
查看>>