您的位置:首页 > 移动开发 > Android开发

SDCard Mount 流程分析(二) 推荐

2012-04-12 10:13 316 查看
上一篇关于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;

}[/b]

if (!Volume::mountVol()) {

mounted = true;

}[/b]

mState = Volume::State_Idle;

}[/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(mount[/b])命令到Vold 的CommandListener里面的CommandListener::VolumeCmd::runCommand[/b]方法进入代码这里:
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{[/b]

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

}[/b]

}

这里执行挂载动作,看上面蓝色代码是为了系统第一次启动上层发送命令firstMount给CommandListener执行挂载USB硬盘的动作,红色代码即是核心要挂载的方法,调用的VolumeManage的mountVolume [/b]方法,只需传入挂载点。该方法代码是:
int VolumeManager::mountVolume(const char *label) {

Volume *v = lookupVolume(label);

if (!v) {

errno = ENOENT;

return -1;

}

return v->mountVol()[/b];

}

可以看出,这里同样调用的是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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android SDCARD MOUNTED