Android-vold源码分析之handleBlockEvent

本文详细解析了磁盘事件处理过程,包括磁盘及分区的添加、移除、更改等事件的具体处理函数与流程,展示了如何通过代码进行事件捕获与通知。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

到了handleBlockEvent函数,该函数包含了以下6个处理函数:

  1. voidhandleDiskAdded(constchar*devpath,NetlinkEvent*evt);
  2. voidhandleDiskRemoved(constchar*devpath,NetlinkEvent*evt);
  3. voidhandleDiskChanged(constchar*devpath,NetlinkEvent*evt);
  4. voidhandlePartitionAdded(constchar*devpath,NetlinkEvent*evt);
  5. voidhandlePartitionRemoved(constchar*devpath,NetlinkEvent*evt);
  6. voidhandlePartitionChanged(constchar*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. elseif(action==NetlinkEvent::NlActionRemove){
  10. if(!strcmp(devtype,"disk")){
  11. handleDiskRemoved(dp,evt);
  12. }else{
  13. handlePartitionRemoved(dp,evt);
  14. }
  15. }
  16. elseif(action==NetlinkEvent::NlActionChange){
  17. if(!strcmp(devtype,"disk")){
  18. handleDiskChanged(dp,evt);
  19. }
  20. else{
  21. handlePartitionChanged(dp,evt);
  22. }
  23. }

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

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

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

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

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

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

分区的改变:

  1. voidDirectVolume::handlePartitionChanged(constchar*devpath,NetlinkEvent*evt){
  2. intmajor=atoi(evt->findParam("MAJOR"));
  3. intminor=atoi(evt->findParam("MINOR"));
  4. SLOGD("Volume%s%spartition%d:%dchanged\n",getLabel(),getMountpoint(),major,minor);
  5. }

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值