分享

Android

 free_light 2014-06-23

作者:gzshun. 原创作品,转载请标明出处!

上篇文章分析到了handleBlockEvent函数,该函数包含了以下6个处理函数:

  1. void handleDiskAdded(const char *devpath, NetlinkEvent *evt);  
  2. void handleDiskRemoved(const char *devpath, NetlinkEvent *evt);  
  3. void handleDiskChanged(const char *devpath, NetlinkEvent *evt);  
  4. void handlePartitionAdded(const char *devpath, NetlinkEvent *evt);  
  5. void handlePartitionRemoved(const char *devpath, NetlinkEvent *evt);  
  6. void handlePartitionChanged(const char *devpath, NetlinkEvent *evt);  

以下是精简版的handleBlockEvent函数:

  1. if (action == NetlinkEvent::NlActionAdd) {  
  2.     ...  
  3.     if (!strcmp(devtype, "disk")) {  
  4.         handleDiskAdded(dp, evt);  
  5.     } else {  
  6.         handlePartitionAdded(dp, evt);  
  7.     }  
  8. }  
  9. else if (action == NetlinkEvent::NlActionRemove) {  
  10.     if (!strcmp(devtype, "disk")) {  
  11.         handleDiskRemoved(dp, evt);  
  12.     } else {  
  13.         handlePartitionRemoved(dp, evt);  
  14.     }  
  15. }  
  16. else if (action == NetlinkEvent::NlActionChange) {  
  17.     if (!strcmp(devtype, "disk")) {  
  18.         handleDiskChanged(dp, evt);  
  19.     }  
  20.     else {  
  21.         handlePartitionChanged(dp, evt);  
  22.     }  
  23. }  

这样看起来就比较清楚每个函数的作用了,贴源码其实是比较直接的方法,程序员对代码都比较敏感,一看就明白意思,好,开始分析。

  1. void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {  
  2.     mDiskMajor = atoi(evt->findParam("MAJOR"));  
  3.     mDiskMinor = atoi(evt->findParam("MINOR"));  
  4.       
  5.     const char *tmp = evt->findParam("NPARTS");  
  6.     if (tmp) {  
  7.         mDiskNumParts = atoi(tmp);  
  8.     } else {  
  9.         SLOGW("Kernel block uevent missing 'NPARTS'");  
  10.         mDiskNumParts = 0;  
  11.     }  
  12.     /********************************************************************************** 
  13.     **mPendingPartsCount是一个全局变量,用来保存该存储设备的分区数量;这里需要说明一个 
  14.     **存储设备识别的顺序: 
  15.     **当插入一块5个分区的硬盘,首先会调用handleDiskAdded函数获取该存储设备的事件信息, 
  16.     **随后会调用若干次handlePartitionAdded函数来识别该存储设备的多个分区的事件信息, 
  17.     **当然,一般5个分区的硬盘肯定有一个扩展节点(因为mbr最多支持4个主分区); 
  18.     **调用顺序是这样: 
  19.     **handleDiskAdded函数调用1次; 
  20.     **handlePartitionAdded函数调用6次; 
  21.     **该变量的作用是这样:插入一块硬盘,此时mPendingPartsCount变量为分区数量, 
  22.     **开始用handlePartitionAdded函数识别分区,每识别一个分区, 
  23.     **mPendingPartsCount自减一次,当mPendingPartsCount==0时,结束该存储设备事件的捕获。 
  24.     **********************************************************************************/  
  25.     mPendingPartsCount = mDiskNumParts;  
  26.       
  27.     if (mDiskNumParts == 0) {  
  28.         /********************************************************************************** 
  29.         **broadcastDiskAdded函数的作用是通知framework,系统插入一块存储设备,源码如下:       
  30.         void DirectVolume::broadcastDiskAdded() 
  31.         { 
  32.             setState(Volume::State_Idle); 
  33.             char msg[255]; 
  34.             snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)", 
  35.                     getLabel(), getMountpoint(), mDiskMajor, mDiskMinor); 
  36.             mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted, msg, false); 
  37.         } 
  38.         **********************************************************************************/  
  39.         broadcastDiskAdded();  
  40.     } else {  
  41.         /********************************************************************************** 
  42.         **setState函数起到的作用很大,相当于一只信鸽,每次sd卡发现状态改变,该函数马上就将 
  43.         **最新的状态广播给framework,后面会详细介绍该函数与framework的通信。 
  44.         **********************************************************************************/  
  45.         setState(Volume::State_Pending);  
  46.     }  
  47. }  

随后就是handlePartitionAdded函数了,这个函数要识别插入的设备的所有分区,源码如下:

  1. void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     int part_num;  
  5.       
  6.     const char *tmp = evt->findParam("PARTN");  
  7.       
  8.     if (tmp) {  
  9.         part_num = atoi(tmp);  
  10.     } else {  
  11.         SLOGW("Kernel block uevent missing 'PARTN'");  
  12.         part_num = 1;  
  13.     }  
  14.       
  15.     if (part_num > mDiskNumParts) {  
  16.         mDiskNumParts = part_num;  
  17.     }  
  18.       
  19.     if (major != mDiskMajor) {  
  20.         SLOGE("Partition '%s' has a different major than its disk!", devpath);  
  21.         return;  
  22.     }  
  23.     /********************************************************************************** 
  24.     **上面就是做一下其他的判断,不重要; 
  25.     **MAX_PARTITIONS定义在system/vold/DirectVolume.h文件中,声明如下: 
  26.     static const int MAX_PARTITIONS = 4; 
  27.     Android系统支持太有限,谷歌太懒,呵呵,最多就识别4个分区,当然如果有些厂商想多实现 
  28.     **分区的识别数量,需要修改源码; 
  29.     **我觉得,Android系统是做得不错,但磁盘管理方面不太完善,自从分析修改了vold源码, 
  30.     **vold支持得太少,也许谷歌以前只想到应用于手机,要是哥想识别一块10个分区的硬盘, 
  31.     **咋办?修改源码咯。。。 
  32.     **********************************************************************************/  
  33.     if (part_num > MAX_PARTITIONS) {  
  34.         SLOGE("Dv:partAdd: ignoring part_num = %d (max: %d)\n", part_num, MAX_PARTITIONS);  
  35.     } else {  
  36.         /*全局数组,用来存放磁盘分区的此设备号*/  
  37.         mPartMinors[part_num -1] = minor;  
  38.     }  
  39.     /*看到了吧,上面那个函数说到mPendingPartsCount每识别一个分区要自减一次,就在这里*/  
  40.     --mPendingPartsCount;  
  41.     /*这里就在判断mPendingPartsCount变量了,如果mPendingPartsCount==0时,再向framework广播 
  42.     一次该设备的插入,所以framework总共需要收到磁盘的插入广播2次,才会下发操作命令。*/  
  43.     if (!mPendingPartsCount) {  
  44.         /*判断了磁盘的状态,如果正在格式化,将不做操作*/  
  45.         if (getState() != Volume::State_Formatting) {  
  46.             broadcastDiskAdded();  
  47.         }  
  48.     } else {  
  49.     }  
  50. }  

磁盘在被系统识别完后,可能发生改变,这种改变的例子如下:
在Linux系统,大家格式化硬盘就会使用到了,就是fdisk命令,该命令会修改磁盘的一些分区参数,
当然,fdisk只是把分区信息写到存储设备的第一个设备节点或扩展节点。
fdisk里面有一个操作是修改分区类型id,按“t”就能修改,当修改完成后,保存退出fdisk,磁盘的设备节点
将会重新生成。
这里是Android系统,也可能遇到这种情况,以下是源码:

  1. void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.       
  5.     if ((major != mDiskMajor) || (minor != mDiskMinor)) {  
  6.         return;  
  7.     }  
  8.       
  9.     SLOGI("Volume %s disk has changed", getLabel());  
  10.     const char *tmp = evt->findParam("NPARTS");  
  11.     if (tmp) {  
  12.         mDiskNumParts = atoi(tmp);  
  13.     } else {  
  14.         SLOGW("Kernel block uevent missing 'NPARTS'");  
  15.         mDiskNumParts = 0;  
  16.     }  
  17.     mPendingPartsCount = mDiskNumParts;  
  18.       
  19.     if (getState() != Volume::State_Formatting) {  
  20.         if (mDiskNumParts == 0) {  
  21.             /*这里类似于fdisk将删除存储设备的所有分区,这样存储设备的分区数量mDiskNumParts 
  22.             就等于0,此时广播磁盘的空闲状态*/  
  23.             setState(Volume::State_Idle);  
  24.         } else {  
  25.             setState(Volume::State_Pending);  
  26.         }  
  27.     }  
  28. }  

分区的改变:

  1. void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor);  
  5. }  

以上两个参数基本没涉及到什么重要的内容,看下源码就行。
刚才上面分析了handleDiskAdded和handlePartitionAdded,这两个增加磁盘或分区的函数,当然也需要
对应移除磁盘或分区的函数,是handleDiskRemoved和handlePartitionRemoved函数。

  1. void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     char msg[255];  
  5.       
  6.     SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);  
  7.     snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",  
  8.                 getLabel(), getMountpoint(), major, minor);  
  9.     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved, msg, false);  
  10.     /*设备移除后,广播通知framework*/  
  11.     setState(Volume::State_NoMedia);  
  12. }  

移除一块存储设备比较方便,当移除分区涉及的比较多,移除分区需要卸载分区,并且删除设备节点,以下是删除分区的源码:

  1. void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {  
  2.     int major = atoi(evt->findParam("MAJOR"));  
  3.     int minor = atoi(evt->findParam("MINOR"));  
  4.     char msg[255];  
  5.     int state;  
  6.       
  7.     SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);  
  8.       
  9.     state = getState();  
  10.     if (state != Volume::State_Mounted && state != Volume::State_Shared) {  
  11.         return;  
  12.     }  
  13.       
  14.     if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {  
  15.         snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",  
  16.                 getLabel(), getMountpoint(), major, minor);  
  17.         /*mCurrentlyMountedKdev变量保存着目前正挂载在系统的存储设备的设备号, 
  18.         这里的判断是这样:如果目前正在移除的分区等于挂载的存储设备的设备号,说明 
  19.         该存储设备没有被安全删除,也就是没有先卸载后移除*/  
  20.         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, msg, false);  
  21.           
  22.         /*卸载挂载在asec目录的分区*/  
  23.         if (mVm->cleanupAsec(this, true)) {  
  24.             SLOGE("Failed to cleanup ASEC - unmount will probably fail!");  
  25.         }  
  26.         /*卸载该分区挂载的所有挂载点,这里为什么用所有来形容了,因为Android 
  27.         系统挂载一个分区的期间,重复挂载在好几个目录,将分区挂载在/mnt/asec目录,也挂载 
  28.         在/mnt/secure/asec目录,也挂载在/mnt/sdcard目录下,总共三次挂载,谷歌不知为什么搞这么复杂? 
  29.         待深究。。*/  
  30.         if (Volume::unmountVol(true)) {  
  31.             SLOGE("Failed to unmount volume on bad removal (%s)",   
  32.             strerror(errno));  
  33.         } else {  
  34.             SLOGD("Crisis averted");  
  35.         }  
  36.     } else if (state == Volume::State_Shared) {  
  37.         snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)",  
  38.         getLabel(), major, minor);  
  39.         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, msg, false);  
  40.         /*这种情况是这样:如果手机跟电脑连接在一起,电脑正在使用sd卡,你把sd卡取出, 
  41.         就会广播该错误信息给framework*/  
  42.         if (mVm->unshareVolume(getLabel(), "ums")) {  
  43.             SLOGE("Failed to unshare volume on bad removal (%s)",  
  44.             strerror(errno));  
  45.         } else {  
  46.             SLOGD("Crisis averted");  
  47.         }  
  48.     }  
  49. }  

这几章介绍了磁盘事件的处理,总算可以告一段落,这些工作就是在main函数中的nm->start()函数负责的,下一篇文章可以分析其他处理函数了,但这些事件的处理起着至关重要的作用,如果没有做这些工作,framework也就根本不理会也不知道底层发生了什么事情。
下一篇继续磁盘操作部分。。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多