上一篇關於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,
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掛載的支持和多分區掛載的支持,但基本流程是相同的。blog
代碼有詳細的註釋,這裏要注意的是:sdcard和USB的支持不一樣,sdcard 掛載時須要先掛載到臨時目錄/mnt/secure/staging,而後再移動到最終須要掛載的掛載點,而USB硬盤特別是多分區的支持,不用先掛載到臨時目錄,而是能夠支持掛載到想要掛載的掛載點,這裏是比較須要注意到的地方(在這裏栽過跟頭,會出現「隨機性的掛載失敗」)。
ok.