编号:时间:2021年x月x日书山有路勤为径,学海无涯苦作舟页码:第101页 共101页Android 2.3中关于SD卡挂载简介 在Android 2.3中,当SD卡插入系统之后,系统会自动挂载Vold 就是负责挂载SD卡的,vold 的全称是volume daemon实际上是负责完成系统的CDROM,USB 大容量存储,MMC 卡(后文有简介,具体请百度)等扩展存储的挂载任务自动完成的守护进程它提供的主要特点是支持这些存储外设的热插拔在Android上的这个vold 系统和GNU/Linux的之间存在很大的差异自Android 2.2开始,vold又做了大改动,升级为vold 2.0,之前的配置文件是system/etc/vold.conf,vold 2.0变为system/etc/vold.fstabvold.fstab中的内容显示如下:## Vold 2.0 Generic fstab## - San Mehat (san@)## ######################### Regular device mount#### Format: dev_mount
如果把sd卡插入设备,在 /dev/block/ 目录下面也会多出几个设备节点,证明sd卡的驱动已经成功加载 我自己测试的目录下面会形成 mmcblk0 和 mmcblk0p1 节点,注意:这两个节点的意思,mmcblk0代表第一个SD卡设备,mmcblk0p1代表第一个SD卡设备的第一个分区真正挂载到系统中的是 mmcblk0p1而不是mmcblk0,这一点很重要PS:MMC(MultiMedia Card)卡由西门子公司和首推CF的SanDisk于1997年推出1998年1月十四家公司联合成立了MMC协会(MultiMedia Card Association简称MMCA),现在已经有超过84个成员MMC的发展目标主要是针对数码影像、音乐、、PDA、电子书、玩具等产品,号称 是目前世界上最小的Flash Memory存贮卡,尺寸只有32mm x 24mm x 1.4mm虽然比SmartMedia厚,但整体体积却比SmartMedia小,而且也比SmartMedia轻,只有1.5克MMC也是把存贮单 元和控制器一同做到了卡上,智能的控制器使得MMC保证兼容性和灵活性MMC_百度百科SD/MMC 卡的设备构造差不多,不过 MMC 当时的设计比 SD 小一半。
所以,SD/MMC 的驱动通用,进一步的,Linux 的设备节点就延续了 MMC 的这个名字,后面的 blk 是块设备这个英文的简写, mmcblk 也就是“ mmc/sd 块设备”,0 就是这个 mmc/sd 设备的顺序编号,p1 就是第一个分区挂载流程简析内核层(kernel):当有新的SD/USB设备插入时,kernel将自动检测并加载对应的驱动,同时kernel中的sysfs机制会在有新的驱动加载时给用户层发送相应的event,然后将kernel产生的这些event传递给vold用户层(user):用户层通过sysfs可以接收来自kernel的uevent,这些收到的信息可以在/sys/block/mmcblk0下用命令cat *来查看,如:# cat *bdi: invalid length10179:0device: invalid length8holders: invalid lengthpower: invalid lengthqueue: invalid length800524288slaves: invalid length 278 813 8686 1050 0 0 0 0 0 240 1040subsystem: invalid lengthMAJOR=179MINOR=0DEVTYPE=diskNPARTS=0# 如果这时候在终端输入"pwd"指令,大家会发现路径并不是我们之前进入的路径/sys/block/mmcblk0,而是/sys/devices /platform/goldfish_mmc.0/mmc_host/mmc0/mmc0:e118/block/mmcblk0。
其中mmc0:e118这 个文件是插入SD卡之后生成的文件Sysfs传递来的是一个多行的文档,vold需要解析这个文档Vold将处理之后的事件传递给 MountService,然后MoutService会将信息进一步处理传递给StorageManager,最后我们可以在系统设置界面看到SD卡挂 载成功的信息,这包括了SD卡的总容量以及可用空间如下图:SD卡的挂载流程大致如此,MountServie实际上还会通知PackageManagerService,因为这里分析的是SD卡挂载从底层到上层的表现,因此这里暂不分析简约流程图如下: 在上一篇博文《Android 2.3 SD卡挂载流程浅析(一)》主要简单的介绍了SD卡的挂载流程包 括了从内核层到用户层事件消息的传递,以及Vold的简介本文将继续介绍SD卡的挂载,但文中并不会涉及代码的详细分析,因为这部分网上已有资料,我会 在文章结尾贴出来供大家参考本文主要目的是一方面对自己学习这一部分的总结,另一方面希望大家能够指出文中理解错误的地方 1.SD卡挂载流程图 SD卡的挂载流程图如下: 绿色箭头:表示插入SD卡后事件传递以及SD卡挂载 红色箭头:表示挂载成功后的消息传递流程 黄色箭头:表示MountService发出挂载/卸载SD卡的命令 大家可能对图中突然出现的这么多的名称感到奇怪,这些都是在Android 2.3 源码中可以找到的,接下来我会为大家一一解释这些类的作用。
2.各个文件的主要作用 (1)Kernel:这个是系统内核啦不是我要分析的文件,本文涉及内容不是内核级的哦!(努力学习中...) (2)NetlinkManager:全称是NetlinkManager.cpp位于Android 2.3源码位置/system/vold/NetlinkManager.cpp该类的主要通过引用NetlinkHandler类中的 onEvent()方法来接收来自内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp (3)VolumeManager:全称是VolumeManager.cpp位于Android 2.3源码位置/system/vold/VolumeManager.cpp 该类的主要作用是接收经过NetlinkManager处理过后的事件消息因为我们这里是SD卡的挂载,因此经过NetlinkManager处理过后 的消息会分为五种,分别是:block,switch,usb_composite,battery,power_supply这里SD卡挂载的事件是 block。
(4)DirectVolume:位于/system/vold/DirectVolume.cpp该类的是一个工具类,主要负责对传入的事件进行进一步的处理,block事件又可以分为:Add,Removed,Change,Noaction这四种后文通过介绍Add事件展开 (5)Volume:Volume.cpp位于/system/vold/Volume.cpp,该类是负责SD卡挂载的主要类Volume.cpp主 要负责检查SD卡格式,以及对复合要求的SD卡进行挂载,并通过Socket将消息SD卡挂载的消息传递给NativeDaemonConnector (6)NativeDaemonConnector:该类位于frameworks/base/services/java /com.android.server/NativeDaemonConnector.java该类用于接收来自Volume.cpp 发来的SD卡挂载消息并向上传递 (7)MountService:位于frameworks/base/services/java/com.android.server/MountService.java。
MountService是一个服务类,该服务是系统服务,提供对外部存储设备的管理、查询等在外部存储设备状态发生变化的时候,该类会发出相应的通知给上层应用在Android系统中这是一个非常重要的类 (8)StorageManaer:位于frameworks/base/core/java/andriod/os/storage /StorageManager.java在该类的说明中有提到,该类是系统存储服务的接口在系统设置中,有Storage相关项,同时 Setting也注册了该类的监听器而StorageManager又将自己的监听器注册到了MountService中,因此该类主要用于上层应用获 取SD卡状态 通过上文对各个文件的作用简介,以及整个SD卡的挂载流程图可以知道,Android 系统是如何从底层获取SD卡挂载信息的 后文将继续分析程序调用流程图在前面两篇博文《Android 2.3 SD卡挂载流程浅析(一)》《Android 2.3 SD卡挂载流程浅析(二)》中,主要简单介绍了SD卡的挂载流程以及所涉及的关键文件在《Android 2.3 SD卡挂载流程浅析(三)》中,将简要介绍Android 2.3中Vold的运行机制,并从接收内核uevent开始介绍程序调用流程。
1. Vold Vold的全称是volume daemon主要负责系统对大容量存储设备(USB/SD)的挂载/卸载任务,它是一个守护进程,该进程支持这些存储外设的热插拔自Android 2.2开始,Vold升级为vold 2.0,之前的配置文件路径在system/etc/vold.conf,Android 2.3之后变为system/etc/vold.fstab 2.Vold工作流程 Vold的工作流程大致可以分为三个部分:创建监听、引导、事件处理 (1)创建监听 创 建监听指的是创建监听链接,一方面用于监听来自内核的uevent,另一方面用于监听来自上层的控制命令,这些命令包括控制SD卡的挂载与卸载,这里所说 的链接也就是Socket在Android 系统启动的时候,init进程会去解析init.rc文件,在该文件中,有如下代码:Service vold /system/bin/vold Socket vold stream 0660 root mount Iprio be 2 这样系统会在启动的时候创建与上层通信的Socket。
在Android 2.3源码/system/vold路径下的main.cpp中创建了与内核通信的Socket在main.cpp中通过实例化VolumeManager和NetlinkManager时创建 (2)引导 Vold进程启动时候会对现有的外部存储设备进行检查首先加载并解析vold.fstab,并检查挂载点是否已被挂载然后执行SD卡的挂载,最后处理USB大容量存储因为系统是按行解析的,通过查看vold.fstab可以很清楚的知道这一点vold.fatab中最重要的语句:dev_mount sdcard /mnt/sdcard auto /devices/platform/goldfish_mmc.0 /devices/platform/msm_sdcc.2/mmc_host/mmc1dev_mount 挂载命令 标签 挂载点 子分区个数 挂载路径注: 子分区个数如果为auto则表示只有1个子分区,也可以为任何不为0的整数。
参数之间不能有空格,只能以tab为间隔(注意:这里为了对齐因此采用空格隔开,如果自行修改vold.fstab之后加以空格的话系统会识别不到的) 如果vold.fstab解析无误,VolueManager将创建DirectVolume,若vold.fstab解析不存在或者打开失败,Vold 将会读取Linux内核中的参数,此时如果参数中存在SDCARD(也就是SD的默认路径),VolumeManager则会创建AutoVolume, 如果不存在这个默认路径那么就不会创建 (3)事件处理 通过对两个socket的监听,完成对事件的处理以及对上层应用的响应 a. Kernel发出uevent NetlinkManager检测到kernel发出的uevent,解析后调用NetlinkHandler::onEvent()方法该方法会分别处理不同的事件,这里重要的事件有: “block”事件主要指Volume的mount、unmount、createAsec等由VolumeManager的 handleBlockEvent(evt)来处理,根据多态性最终将会调用AutoVolume或者DirectVolume的 handleBlockEvent方法来处理。
“switch”事件主要指Volume的connet、disconnet等根据相关操作,改变设备参数(设备类型、挂载点等)通过CommandListener告知FrameWork层 b. FrameWork发出控制命令 与a相反,CommandListener检测到FrameWork层的命令(MountService发出的命令)调用VolumeManager的函 数,VolumeManager找出对应的Volume,调用Volume函数去挂载/卸载操作而Volume类中的相关操作最终通过调用Linux函 数完成 这里再次贴上这张流程图: 3.SD卡挂载流程代码浅析 这里只是简要的分析SD卡挂载过程中重要的代码调用,并没有深入分析代码,因为这一部分网上已有牛人比较详尽的分析了,后面我会贴出这些参考文章 整个过程从Kernel检测到SD卡插入事件开始,之前的一些硬件中断的触发以及driver的加载这里并不叙述,一直到SD卡挂载消息更新到“Android——系统设置——存储”一项中 1. Kernel发出SD卡插入uevent。
2. NetlinkHandler::onEvent()接收内核发出的uevent并进行解析 3. VolumeManager::handlBlockEvent()处理经过第二步处理后的事件 4. 接下来调用DirectVolume:: handleBlockEvent() 在该方法中主要有两点需要注意: 第一,程序首先会遍历mPath容器,寻找与event对应的sysfs_path是否存在与mPath容器中 第二,针对event中的action有4种处理方式:Add,Removed,Change,Noaction 例如:在Add action中会有如下操作(因为我们这里所讲的是SD卡的挂载流程,因此以Add来说明),首先创建设备节点,其次对disk和partition两种格式的设备分别进行处理SD卡属于disk类型 5. 经过上一步之后会调用DirectVolume::handleDiskAdded()方法,在该方法中会广播disk insert消息。
6. SocketListener::runListener会接收DirectVolume::handleDiskAdded()广播的消息该方法主 要完成对event中数据的获取,通过SocketPS:这里的SocketListener.cpp位于Android源码/system /core/libsysutils/src/中,后文的FramworkListener.cpp也是,之前自己找了很久 T_T) 7. 调用FrameworkListener::onDataAvailable()方法处理接收到的消息内容 8. FrameworkListener::dispatchCommand()该方法用于分发指令 9. 在FrameworkListener::dispatchCommand()方法中,通过runCommand()方法去调用相应的指令 10. 在/system/vold/CommandListener.cpp中有runCommand()的具体实现在该类中可以找到这个方 法:CommandListener::VolumeCmd::runCommand(),从字面意思上来看这个方法就是对Volume分发指令的解析。
该方法中会执行“mount”函数:vm->mountVolume(arg[2]) 11. mountVolume(arg[2])在VolumeManager::mountVolume()中实现,在该方法中调用v->mountVol() 12. mountVol()方法在Volume::mountVol()中实现,该函数是真正的挂载函数在该方法中,后续的处理都在该方法中,在Mount过程中会广播相应的消息给上层,通过setState()函数) 13. setState(Volume::Checking);广播给上层,正在检查SD卡,为挂载做准备 14. Fat::check();SD卡检查方法,检查SD卡是否是FAT格式 15. Fat::doMount()挂载SD卡 至此,SD的挂载已算初步完成,接下来应该将SD卡挂载后的消息发送给上层,在13中也提到过,在挂载以及检查的过程中其实也有发送消息给上层的 16. MountService的构造函数中会开启监听线程,用于监听来自vold的socket信息。
Thread thread = new Thread(mConnector,VOLD_TAG); thread.start(); 17. mConnector是NativeDaemonConnector的对象,NativeDaemonConnector继承了Runnable并 Override了run方法在run方法中通过一个while(true)调用ListenToSocket()方法来实现实时监听 18. 在ListenToSocket()中,首先建立与Vold通信的Socket Server端,然后调用MountService中的onDaemonConnected()方法PS:Java与Native通信可以通过 JNI,那么Native与Java通信就需要通过Socket来实现了Android中Native与Frameworks通信 这篇文章中有简介,感兴趣的朋友可以参考一下) 19. onDaemonConnected()方法是在接口INativeDaemonConnectorCallbacks中定义 的,MountService实现了该接口并Override了onDaemonConnected()方法。
该方法开启一个线程用于更新外置存储设备的 状态,主要更新状态的方法也在其中实现 20. 然后回到ListenToSocket中,通过inputStream来获取Vold传递来的event,并存放在队列中 21. 然后这些event会在onDaemonConnected()通过队列的”队列.take()”方法取出并根据不同的event调用 updatePublicVolumeState()方法,在该方法中调用packageManagerService中的 updateExteralState()方法来更新存储设备的状态注:这里不太理解packageManagerService中的 unloadAllContainers(args)方法) 22. 更新是通过packageHelper.getMountService().finishMediaUpdate()方法来实现的 23. 在updatePublicVolumeState()方法中,更新后会执行如下代码: bl.mListener.onStorageStateChanged(); 在Android源码/packages/apps/Settings/src/com.android.settings.deviceinfo /Memory.java代码中,实现了StorageEventListener 的匿名内部类,并Override了onStorageStateChanged();方法。
因此在updatePublicVolumeState() 中调用onStorageStateChanged();方法后,Memory.java中也会收到在Memory.java中收到以后会在 Setting界面进行更新,系统设置——存储中会更新SD卡的状态从而SD卡的挂载从底层到达了上层 在经过了上面步骤之后,SD卡的挂载的消息已经从底层到达了上层这是自己在网上查找资料同时一边跟踪代码后得出的结论,其中可能还有很多不正确的地方,也有很多自己没有理解的地方,希望大家能够帮忙指正,感激不尽 后续将继续分析SD挂载广播的发出流程,以及SD卡挂载程序调用流程图前面的三篇博文《Android 2.3 SD卡挂载流程浅析(一)》、《Android 2.3 SD卡挂载流程浅析(二)》、《Android 2.3 SD卡挂载流程浅析(三)》的分析,知道了SD卡挂载的消息是如何从底层传递到上层的,在《Android 2.3 SD卡挂载流程浅析(三)》中,我们已经知道了最后是在updatePublicVolumeState()中调用onStorageStateChanged(),从而达到更新SD卡挂载信息的。
在本文《Android 2.3 SD卡挂载流程浅析(四)》中,我会将前文提到的程序调用流程图画出来,并对代码进行简单的分析 首先,还是挂出这张老图(因为每次都用这张图0_0...) 就权当复习吧,这是SD卡的整个挂载流程,而程序的调用也是根据这个流程图来的 1.接收并处理uevent 首先是接收因为插入SD卡被内核检测到而发出的Event; NetlinkHandler::onEvent(NetlinkEvent *evt)//代码路径:AndroidSourcecode2.3/system/vold/NetlinkHandler.cpp//该方法主要通过evt->getSubsystem();方法来获取系统的eventview plain1. void NetlinkHandler::onEvent(NetlinkEvent *evt) { 2. VolumeManager *vm = VolumeManager::Instance(); 3. const char *subsys = evt->getSubsystem(); 4. 5. if (!subsys) { 6. SLOGW("No subsystem found in netlink event"); 7. return; 8. } 9. 10. if (!strcmp(subsys, "block")) { 11. vm->handleBlockEvent(evt); 12. } else if (!strcmp(subsys, "switch")) { 13. vm->handleSwitchEvent(evt); 14. } else if (!strcmp(subsys, "usb_composite")) { 15. vm->handleUsbCompositeEvent(evt); 16. } else if (!strcmp(subsys, "battery")) { 17. } else if (!strcmp(subsys, "power_supply")) { 18. } 19. } [cpp] view plaincopy1. void NetlinkHandler::onEvent(NetlinkEvent *evt) { 2. VolumeManager *vm = VolumeManager::Instance(); 3. const char *subsys = evt->getSubsystem(); 4. 5. if (!subsys) { 6. SLOGW("No subsystem found in netlink event"); 7. return; 8. } 9. 10. if (!strcmp(subsys, "block")) { 11. vm->handleBlockEvent(evt); 12. } else if (!strcmp(subsys, "switch")) { 13. vm->handleSwitchEvent(evt); 14. } else if (!strcmp(subsys, "usb_composite")) { 15. vm->handleUsbCompositeEvent(evt); 16. } else if (!strcmp(subsys, "battery")) { 17. } else if (!strcmp(subsys, "power_supply")) { 18. } 19. } 2.对SD卡挂载事件开始处理 void VolumeManager::handleBlockEvent(NetlinkEvent *evt)//代码路径:AndroidSourcecode2.3/system/vold/VolumeManager.cpp//该方法的主要作用是://第一, 遍历mPath容器,寻找与event对应的sysfs_path是否存在与mPath容器中。
//第二, 针对Event中的action有4种处理方式:Add,Removed,Change,Noactionview plain1. void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { 2. const char *devpath = evt->findParam("DEVPATH"); 3. 4. /* Lookup a volume to handle this device */ 5. VolumeCollection::iterator it; 6. bool hit = false; 7. for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { 8. if (!(*it)->handleBlockEvent(evt)) { 9. #ifdef NETLINK_DEBUG 10. SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel()); 11. #endif 12. hit = true; 13. break; 14. } 15. } 16. 17. if (!hit) { 18. #ifdef NETLINK_DEBUG 19. SLOGW("No volumes handled block event for '%s'", devpath); 20. #endif 21. } 22. } [cpp] view plaincopy1. void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { 2. const char *devpath = evt->findParam("DEVPATH"); 3. 4. /* Lookup a volume to handle this device */ 5. VolumeCollection::iterator it; 6. bool hit = false; 7. for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { 8. if (!(*it)->handleBlockEvent(evt)) { 9. #ifdef NETLINK_DEBUG 10. SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel()); 11. #endif 12. hit = true; 13. break; 14. } 15. } 16. 17. if (!hit) { 18. #ifdef NETLINK_DEBUG 19. SLOGW("No volumes handled block event for '%s'", devpath); 20. #endif 21. } 22. } 3.对Block挂载事件进行处理 DirectVolume::handleBlockEvent(NetlinkEvent *evt)//代码路径:AndroidSourcecode2.3/system/vold/DirectVolume.cpp//在Add action中首先会创建设备节点,然后对disk和partion两种格式的设备分别进行处理。
这里是disk格式view plain1. int DirectVolume::handleBlockEvent(NetlinkEvent *evt) { 2. const char *dp = evt->findParam("DEVPATH"); 3. 4. PathCollection::iterator it; 5. for (it = mPaths->begin(); it != mPaths->end(); ++it) { 6. if (!strncmp(dp, *it, strlen(*it))) { 7. /* We can handle this disk */ 8. int action = evt->getAction(); 9. const char *devtype = evt->findParam("DEVTYPE"); 10. 11. if (action == NetlinkEvent::NlActionAdd) { 12. int major = atoi(evt->findParam("MAJOR")); 13. int minor = atoi(evt->findParam("MINOR")); 14. char nodepath[255]; 15. 16. snprintf(nodepath, 17. sizeof(nodepath), "/dev/block/vold/%d:%d", 18. major, minor); 19. if (createDeviceNode(nodepath, major, minor)) { 20. SLOGE("Error making device node '%s' (%s)", nodepath, 21. strerror(errno)); 22. } 23. if (!strcmp(devtype, "disk")) { 24. handleDiskAdded(dp, evt);//SD卡插入是Add事件 25. } else { 26. handlePartitionAdded(dp, evt); 27. } 28. } else if (action == NetlinkEvent::NlActionRemove) { 29. if (!strcmp(devtype, "disk")) { 30. handleDiskRemoved(dp, evt); 31. } else { 32. handlePartitionRemoved(dp, evt); 33. } 34. } else if (action == NetlinkEvent::NlActionChange) { 35. if (!strcmp(devtype, "disk")) { 36. handleDiskChanged(dp, evt); 37. } else { 38. handlePartitionChanged(dp, evt); 39. } 40. } else { 41. SLOGW("Ignoring non add/remove/change event"); 42. } 43. 44. return 0; 45. } 46. } 47. errno = ENODEV; 48. return -1; 49. } [cpp] view plaincopy1. int 2. DirectVolume::handleBlockEvent(NetlinkEvent *evt) { 3. const char *dp = evt->findParam("DEVPATH"); 4. 5. PathCollection::iterator it; 6. for (it = mPaths->begin(); it != mPaths->end(); ++it) { 7. if (!strncmp(dp, *it, strlen(*it))) { 8. /* We can handle this disk */ 9. int action = evt->getAction(); 10. const char *devtype = evt->findParam("DEVTYPE"); 11. 12. if (action == NetlinkEvent::NlActionAdd) { 13. int major = atoi(evt->findParam("MAJOR")); 14. int minor = atoi(evt->findParam("MINOR")); 15. char nodepath[255]; 16. 17. snprintf(nodepath, 18. sizeof(nodepath), "/dev/block/vold/%d:%d", 19. major, minor); 20. if (createDeviceNode(nodepath, major, minor)) { 21. SLOGE("Error making device node '%s' (%s)", 22. nodepath, 23. 24. strerror(errno)); 25. } 26. if (!strcmp(devtype, "disk")) { 27. handleDiskAdded(dp, evt);//SD卡插入是Add事件 30. } else { 31. handlePartitionAdded(dp, evt); 32. } 33. } else if (action == NetlinkEvent::NlActionRemove) { 34. if (!strcmp(devtype, "disk")) { 35. handleDiskRemoved(dp, evt); 36. } else { 37. handlePartitionRemoved(dp, evt); 38. } 39. } else if (action == NetlinkEvent::NlActionChange) { 40. if (!strcmp(devtype, "disk")) { 41. handleDiskChanged(dp, evt); 42. } else { 43. handlePartitionChanged(dp, evt); 44. } 45. } else { 46. SLOGW("Ignoring non add/remove/change event"); 47. } 48. 49. return 0; 50. } 51. } 52. errno = ENODEV; 53. return -1; 54. } 4.处理DiskAdd事件 DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt)//代码路径:AndroidSourcecode2.3/system/vold/DirectVol。