from:http://blog.chinaunix.net/uid-26281173-id-3761483.html



StorageManager

前言

Android系统中,常用的存储介质是NandFlash;系统的二进制镜像、Android的文件系统等通常都保存在NandFlash中。通常使用的Micro-SD卡的管理则是由卷守护进程(VolumeDaemon,vold)去完成的,包括SD卡的插拔事件检测、挂载、卸载、格式化等。这里会从Volume的原理和机制去分析Android对于UMS的管理;并通过一个SDCard的挂载过程详细说明。当热插拔SDCard的时候所引起的LinuxKernel的变化,如如何发出uevent消息等没深入研究,Vold接收这些uevent消息也仅仅是提到而已;因此这几着重介绍Voldsyslibutils以及MountService

1Vold的原理与机制

有关Vold的架构可以用图1-1来表示


1-1Vold架构

从图1-1Vold架构,可以大致的了解Android针对外部存储的管理流程。

1、外部存储插入的时候,LinuxKernel会发出uevent事件给Vold

2、Vold守护进程通过Socket机制从LinuxKernel获取相应的uevent,并解析成不同的状态;

3、然后在由MountService来获取这些由Vold解析出的相应状态决定发出什么样的广播、给Vold作出什么样的反应。

4、进而Vold依据MountService的反应稍加处理交由Kernel处理。

这里需要走出一个误区:当插入SD卡的时候Kernel发出ueventVold处理eventMountServiceVold获取相应信息发出广播,app在接收到广播之后会作出相应的处理;其实到这里SD卡并没有真正的被挂载到系统中,仅仅是触发了相应的uevent,而真正的挂载并没有执行。实际情况是如图1-1Vold架构所示,先得到Uevent在交由Vold进行解析,然后由MountService获取信息发出mountunmount等命令给Vold,在由Kernel进行针对存储设备的挂载、卸载、格式化等操作。

1.1Vold针对不同信息的处理

上面有讲到,Vold不仅仅要处理来自KernelUevent等信息,还得处理来自MountService的信息。那么到底Vold是如何获取到Kernel发上来的Uevent信息?Vold又是如何从MountService获取到上层要发给Kernel的命令的呢?

在这里拥有一个重要的基类SocketListener。不仅对kernel发出的uevent进行监听还对MountServicekernel发出的命令进行监听。其常用类之间的结构关系如图1-2Vold重要类关系图


1-2Vold重要类关系图

通过类图,我们可以大致的了解到Vold的工作流程、工作原理:VoldAndroid平台中外部存储系统的管理中心;从kernel来的相关event以及从frameworks来的commond都要通过Vold来进行通信。这里KernelFrameworks不仅仅是从Vold获取东西也会想Vold发出命令。下图我们将会更加详细的介绍存储设备的挂载流程。

如图1-2处理流程,更加清晰的说明了Android针对StorageManager的处理流程。就更加凸显了SocketListener这个类的重要性;并且VoldMountService之间在进行进程间通信的时候并没有用到复杂Binder,而是直接的使用了Socket;这样就是的本身简单的Vold系统更加易懂。


1-2处理流程

2Vold工作过程

2.1处理Kernel信息

当外部存储SDCard插入之后,Kernel会发出相应的Uevent消息;而这时Vold也正在监听,所以会有Vold当中的NetLinkManager接受到消息。当然在这消息的接受-在发送过程中肯定是有消息的解析并针对解析出的相关信息在作出动作。设计到的主要类如下图所示


2-1Sock相关类

在能否获取KernelUevent之前,SocketListener便会启动一个线程去针对Kernelevent进行监听;同时还会创建一个用于传输数据的pipe。当Kernelevent抵达,也就是有数据通过Socket发送过来,就会调用SocketListener自己的onDataAvailable对数据进行处理。而这里SocketListeneronDataAvailable是一个纯虚函数,其真正的实现在NetlinkListener中,所以数据的真正解析在NetlinkListener中。如果解析eventOK,那么这个时候就该发出一个eventVolumeManager了。

其实通过这里,我们也可以看出Vold这个较小的进程也分为两个部分:一个同Kernel接触,一个桶Framework接触。

2.2Framework发出通知

几乎所有从Kernel上来的mount信息以及从FrameworksKernelmount消息都需要经过DirectVolume.cpphandleBlockEvent来进行处理。

点击(此处)折叠或打开

  1. int DirectVolume::handleBlockEvent(NetlinkEvent*evt){

  2. const char *dp = evt->findParam("DEVPATH");

  3. PathCollection::iterator it;

  4. for (it= mPaths->begin(); it!= mPaths->end();++it){

  5. if (!strncmp(dp,*it, strlen(*it))){

  6. /* We can handle this disk*/

  7. int action = evt->getAction();

  8. const char *devtype = evt->findParam("DEVTYPE");

  9. if (action == NetlinkEvent::NlActionAdd){

  10. int major = atoi(evt->findParam("MAJOR"));

  11. int minor = atoi(evt->findParam("MINOR"));

  12. char nodepath[255];

  13. /*获得当前node的信息*/

  14. snprintf(nodepath,

  15. sizeof(nodepath),"/dev/block/vold/%d:%d",

  16. major, minor);

  17. /*创建node mknod*/

  18. if (createDeviceNode(nodepath, major, minor)){

  19. SLOGE("Error making device node '%s' (%s)", nodepath,

  20. strerror(errno));

  21. }

  22. /*是否有分区,disk 没有分区*/

  23. if (!strcmp(devtype,"disk")){

  24. handleDiskAdded(dp, evt);

  25. } else {

  26. handlePartitionAdded(dp, evt);

  27. }

  28. /* Send notification iff diskis ready(ie all partitions found)*/

  29. /*开始给Frameworks发广播了。。*/

  30. if (getState()== Volume::State_Idle){

  31. char msg[255];

  32. snprintf(msg, sizeof(msg),

  33. "Volume %s %s disk inserted (%d:%d)", getLabel(),

  34. getMountpoint(), mDiskMajor, mDiskMinor);

  35. mVm->getBroadcaster()->sendBroadcast(

  36. ResponseCode::VolumeDiskInserted, msg,false);

  37. }

  38. /*下面的两个不注释了。。。*/

  39. } else if (action== NetlinkEvent::NlActionRemove){

  40. if (!strcmp(devtype,"disk")){

  41. handleDiskRemoved(dp, evt);

  42. } else {

  43. handlePartitionRemoved(dp, evt);

  44. }

  45. } else if (action== NetlinkEvent::NlActionChange){

  46. if (!strcmp(devtype,"disk")){

  47. handleDiskChanged(dp, evt);

  48. } else {

  49. handlePartitionChanged(dp, evt);

  50. }

  51. } else {

  52. SLOGW("Ignoring non add/remove/change event");

  53. }

  54. return 0;

  55. }

  56. }

  57. errno = ENODEV;

  58. return -1;

  59. }




在调用handleDiskAdded(dp,evt);完成之后,便会从系统中获取该存储设备的相关信息;准备发广播给Frameworks了。


点击(此处)折叠或打开

  1. snprintf(msg, sizeof(msg),"Volume %s %s disk inserted (%d:%d)", getLabel(),
  2. getMountpoint(), mDiskMajor, mDiskMinor);
  3. mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted, msg, false);


这里需要注意一点:在handleDiskAdded中,处理完成了之后,会更新当前磁盘的状态

setState(Volume::State_Idle);这个函数需要重点关注

这样Frameworks就收到了有存储设备的消息;下面来看看Framew是如何处理这样的信息的。


点击(此处)折叠或打开

  1. public boolean onEvent(int code,String raw,String[] cooked){

  2. /*……………*/

  3. } else if ((code== VoldResponseCode.VolumeDiskInserted)||

  4. (code == VoldResponseCode.VolumeDiskRemoved)||

  5. (code == VoldResponseCode.VolumeBadRemoval)){

  6. // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)

  7. // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)

  8. // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)

  9. final String path= cooked[3];

  10. /*这里是见证奇迹的时刻之一。。哈哈*/

  11. if (code== VoldResponseCode.VolumeDiskInserted){

  12. if ((rc= doMountVolume(path))!= StorageResultCode.OperationSucceeded){

这个onEvent目前先看到这里,去看看doMountVolume


点击(此处)折叠或打开

  1. private int doMountVolume(String path){

  2. mConnector.execute("volume","mount", path);

这里mConnector是一个NativeDaemonConnector对象,算是一个守护程序吧,跟守护进程差不多,专门用来处理libsysutils中通过FrameworkListener协议来进行信息传递。

这里转了一大圈又回到了Vold。接下来继续回到Vold进行查看。在这其中会经过libsysutils的一些转换,这里直接去Volume.cpp中的mountVol查看。首先会检测是否已经被mount了。


点击(此处)折叠或打开

  1. if (isMountpointMounted(getMountpoint())){
  2. setState(Volume::State_Mounted);
  3. // mCurrentlyMountedKdev= XXX
  4. return 0;
  5. }


到这里,setState()的作用,我觉得可以这么来理解,他同Activity的生命周期相同,只不过这里是“显示”的去修改各个状态,而ActivityAndroid系统本身已经做好了。这里假设目前还没有被mount,接着往下看:


点击(此处)折叠或打开

  1. /* Wenow have the new sysfs pathfor the decrypted block device,and the

  2. * majore and minor numbersfor it. So, create the device, update the

  3. * path to the new sysfs path,and continue.

  4. */

  5. snprintf(nodepath, sizeof(nodepath),"/dev/block/vold/%d:%d",

  6. new_major, new_minor);

  7. if (createDeviceNode(nodepath, new_major, new_minor)){

  8. SLOGE("Error making device node '%s' (%s)", nodepath, strerror(errno));

  9. }

  10. // Todo: Either create sys filename from nodepath,or passin bogus path so

  11. // vold ignores state changeson this internal device.

  12. updateDeviceInfo(nodepath, new_major, new_minor);

  13. /* Get the device nodes again, because they just changed*/

  14. n = getDeviceNodes((dev_t*)&deviceNodes, 4);

  15. 。。。。

  16. sprintf(devicePath,"/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),

  17. MINOR(deviceNodes[i]));

  18. setState(Volume::State_Checking);

  19. 。。。。。。

  20. if (Fat::doMount(devicePath,"/mnt/secure/staging",false,false, false,

  21. AID_SYSTEM, gid, 0702,true)){

  22. continue;

  23. }

  24. /*。。。。*/

  25. /*删除autorun.inf.为啥??不为啥?就是要删!!*/

  26. protectFromAutorunStupidity();

  27. /*

  28. * Now that the bindmount trickeryis done, atomically move the

  29. * whole subtree to expose it to non priviledged users.*/

  30. if (doMoveMount("/mnt/secure/staging", getMountpoint(),false)){

  31. SLOGE("Failed to move mount (%s)", strerror(errno));

  32. umount("/mnt/secure/staging");

  33. setState(Volume::State_Idle);

  34. return -1;

  35. }

  36. /* 又是这个神一样的函数,setState,这次可是设置了State_Mounted,

  37. * 很明显,这里要发广播了,给Frameworks发!告诉Frameworks,

  38. * 我这儿已经mounted完成了,剩下就是你的事儿了

  39. */

  40. setState(Volume::State_Mounted);

  41. mCurrentlyMountedKdev = deviceNodes[i];

  42. return 0;

  43. }

从现在开始,磁盘的大部分操作同VoldKernel基本上没什么关系了,继续来看看MountService对消息的处理。


点击(此处)折叠或打开

  1. if (code == VoldResponseCode.VolumeStateChange){
  2. /*
  3.  * One of the volumes we're managing has changed state.
  4. * Format: "NNN Volume <label> <path> state changed
  5. * from <old_#> (<old_str>) to <new_#> (<new_str>)"
  6. */
  7. notifyVolumeStateChange(cooked[2], cooked[3],
  8.             Integer.parseInt(cooked[7]),Integer.parseInt(cooked[10]));


notifyVolumeStateChange函数基本上就干了一件事儿:发ACTION_MEDIA_*各种广播。。这就不多说了。到这儿所有APP,该接受的接受,该处理的处理,干干啥的干啥。

Over,完了




名词解释

UMSUSBMassStorageUSB大容量存储

Vold:VolumeDaemon卷守护进程

MTDMemoryTechnologyDevice内存技术设备

EMMCEmbeddedMultiMediaCard内嵌式多媒体卡

Volume状态

staticconstintState_Init=-1;

staticconstintState_NoMedia=0;

staticconstintState_Idle=1;

staticconstintState_Pending=2;

staticconstintState_Checking=3;

staticconstintState_Mounted=4;

staticconstintState_Unmounting=5;

staticconstintState_Formatting=6;

staticconstintState_Shared=7;

staticconstintState_SharedMnt=8;


更多相关文章

  1. Android(安卓)几种常用关于屏幕操作的方法(获取屏幕大小,全屏,显示
  2. Android(安卓)Studio TCP IP 服务器和客户端建立
  3. java获取http:图片下载代码——android基础编
  4. android实用技巧:android实现listview异步加载图片
  5. 【Android】获取设备标识号
  6. Android(安卓)2.2.1系统广播大全
  7. 修改Android模拟器RAM大小方法
  8. Android(安卓)Context.getSystemService() 与 ServiceManager 的
  9. Android获取View的宽高与View.measure详解

随机推荐

  1. Android第十二课 jni函数的静态绑定
  2. Android(安卓)取消GridView和ListView it
  3. 换一种方式理解 Android协程
  4. Android(安卓)SlidingMenu 的实现
  5. android下数据库SQLite
  6. android创建txt文件,读取txt文件内容
  7. Android录音上————AudioRecord实现录
  8. android之Handler整理
  9. android客户端与服务端交互的工具类
  10. android的短信发送全过程源代码分析