到了handleBlockEvent函数,该函数包含了以下6个处理函数:
- voidhandleDiskAdded(constchar*devpath,NetlinkEvent*evt);
- voidhandleDiskRemoved(constchar*devpath,NetlinkEvent*evt);
- voidhandleDiskChanged(constchar*devpath,NetlinkEvent*evt);
- voidhandlePartitionAdded(constchar*devpath,NetlinkEvent*evt);
- voidhandlePartitionRemoved(constchar*devpath,NetlinkEvent*evt);
- voidhandlePartitionChanged(constchar*devpath,NetlinkEvent*evt);
以下是精简版的handleBlockEvent函数:
- if(action==NetlinkEvent::NlActionAdd){
- ...
- if(!strcmp(devtype,"disk")){
- handleDiskAdded(dp,evt);
- }else{
- handlePartitionAdded(dp,evt);
- }
- }
- elseif(action==NetlinkEvent::NlActionRemove){
- if(!strcmp(devtype,"disk")){
- handleDiskRemoved(dp,evt);
- }else{
- handlePartitionRemoved(dp,evt);
- }
- }
- elseif(action==NetlinkEvent::NlActionChange){
- if(!strcmp(devtype,"disk")){
- handleDiskChanged(dp,evt);
- }
- else{
- handlePartitionChanged(dp,evt);
- }
- }
这样看起来就比较清楚每个函数的作用了,贴源码其实是比较直接的方法,程序员对代码都比较敏感,一看就明白意思,好,开始分析。
- voidDirectVolume::handleDiskAdded(constchar*devpath,NetlinkEvent*evt){
- mDiskMajor=atoi(evt->findParam("MAJOR"));
- mDiskMinor=atoi(evt->findParam("MINOR"));
- constchar*tmp=evt->findParam("NPARTS");
- if(tmp){
- mDiskNumParts=atoi(tmp);
- }else{
- SLOGW("Kernelblockueventmissing'NPARTS'");
- mDiskNumParts=0;
- }
- /**********************************************************************************
- **mPendingPartsCount是一个全局变量,用来保存该存储设备的分区数量;这里需要说明一个
- **存储设备识别的顺序:
- **当插入一块5个分区的硬盘,首先会调用handleDiskAdded函数获取该存储设备的事件信息,
- **随后会调用若干次handlePartitionAdded函数来识别该存储设备的多个分区的事件信息,
- **当然,一般5个分区的硬盘肯定有一个扩展节点(因为mbr最多支持4个主分区);
- **调用顺序是这样:
- **handleDiskAdded函数调用1次;
- **handlePartitionAdded函数调用6次;
- **该变量的作用是这样:插入一块硬盘,此时mPendingPartsCount变量为分区数量,
- **开始用handlePartitionAdded函数识别分区,每识别一个分区,
- **mPendingPartsCount自减一次,当mPendingPartsCount==0时,结束该存储设备事件的捕获。
- **********************************************************************************/
- mPendingPartsCount=mDiskNumParts;
- if(mDiskNumParts==0){
- /**********************************************************************************
- **broadcastDiskAdded函数的作用是通知framework,系统插入一块存储设备,源码如下:
- voidDirectVolume::broadcastDiskAdded()
- {
- setState(Volume::State_Idle);
- charmsg[255];
- snprintf(msg,sizeof(msg),"Volume%s%sdiskinserted(%d:%d)",
- getLabel(),getMountpoint(),mDiskMajor,mDiskMinor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,msg,false);
- }
- **********************************************************************************/
- broadcastDiskAdded();
- }else{
- /**********************************************************************************
- **setState函数起到的作用很大,相当于一只信鸽,每次sd卡发现状态改变,该函数马上就将
- **最新的状态广播给framework,后面会详细介绍该函数与framework的通信。
- **********************************************************************************/
- setState(Volume::State_Pending);
- }
- }
随后就是handlePartitionAdded函数了,这个函数要识别插入的设备的所有分区,源码如下:
- voidDirectVolume::handlePartitionAdded(constchar*devpath,NetlinkEvent*evt){
- intmajor=atoi(evt->findParam("MAJOR"));
- intminor=atoi(evt->findParam("MINOR"));
- intpart_num;
- constchar*tmp=evt->findParam("PARTN");
- if(tmp){
- part_num=atoi(tmp);
- }else{
- SLOGW("Kernelblockueventmissing'PARTN'");
- part_num=1;
- }
- if(part_num>mDiskNumParts){
- mDiskNumParts=part_num;
- }
- if(major!=mDiskMajor){
- SLOGE("Partition'%s'hasadifferentmajorthanitsdisk!",devpath);
- return;
- }
- /**********************************************************************************
- **上面就是做一下其他的判断,不重要;
- **MAX_PARTITIONS定义在system/vold/DirectVolume.h文件中,声明如下:
- staticconstintMAX_PARTITIONS=4;
- Android系统支持太有限,谷歌太懒,呵呵,最多就识别4个分区,当然如果有些厂商想多实现
- **分区的识别数量,需要修改源码;
- **我觉得,Android系统是做得不错,但磁盘管理方面不太完善,自从分析修改了vold源码,
- **vold支持得太少,也许谷歌以前只想到应用于手机,要是哥想识别一块10个分区的硬盘,
- **咋办?修改源码咯。。。
- **********************************************************************************/
- if(part_num>MAX_PARTITIONS){
- SLOGE("Dv:partAdd:ignoringpart_num=%d(max:%d)\n",part_num,MAX_PARTITIONS);
- }else{
- /*全局数组,用来存放磁盘分区的此设备号*/
- mPartMinors[part_num-1]=minor;
- }
- /*看到了吧,上面那个函数说到mPendingPartsCount每识别一个分区要自减一次,就在这里*/
- --mPendingPartsCount;
- /*这里就在判断mPendingPartsCount变量了,如果mPendingPartsCount==0时,再向framework广播
- 一次该设备的插入,所以framework总共需要收到磁盘的插入广播2次,才会下发操作命令。*/
- if(!mPendingPartsCount){
- /*判断了磁盘的状态,如果正在格式化,将不做操作*/
- if(getState()!=Volume::State_Formatting){
- broadcastDiskAdded();
- }
- }else{
- }
- }
磁盘在被系统识别完后,可能发生改变,这种改变的例子如下:
在Linux系统,大家格式化硬盘就会使用到了,就是fdisk命令,该命令会修改磁盘的一些分区参数,
当然,fdisk只是把分区信息写到存储设备的第一个设备节点或扩展节点。
fdisk里面有一个操作是修改分区类型id,按“t”就能修改,当修改完成后,保存退出fdisk,磁盘的设备节点
将会重新生成。
这里是Android系统,也可能遇到这种情况,以下是源码:
- voidDirectVolume::handleDiskChanged(constchar*devpath,NetlinkEvent*evt){
- intmajor=atoi(evt->findParam("MAJOR"));
- intminor=atoi(evt->findParam("MINOR"));
- if((major!=mDiskMajor)||(minor!=mDiskMinor)){
- return;
- }
- SLOGI("Volume%sdiskhaschanged",getLabel());
- constchar*tmp=evt->findParam("NPARTS");
- if(tmp){
- mDiskNumParts=atoi(tmp);
- }else{
- SLOGW("Kernelblockueventmissing'NPARTS'");
- mDiskNumParts=0;
- }
- mPendingPartsCount=mDiskNumParts;
- if(getState()!=Volume::State_Formatting){
- if(mDiskNumParts==0){
- /*这里类似于fdisk将删除存储设备的所有分区,这样存储设备的分区数量mDiskNumParts
- 就等于0,此时广播磁盘的空闲状态*/
- setState(Volume::State_Idle);
- }else{
- setState(Volume::State_Pending);
- }
- }
- }
分区的改变:
- voidDirectVolume::handlePartitionChanged(constchar*devpath,NetlinkEvent*evt){
- intmajor=atoi(evt->findParam("MAJOR"));
- intminor=atoi(evt->findParam("MINOR"));
- SLOGD("Volume%s%spartition%d:%dchanged\n",getLabel(),getMountpoint(),major,minor);
- }
以上两个参数基本没涉及到什么重要的内容,看下源码就行。
刚才上面分析了handleDiskAdded和handlePartitionAdded,这两个增加磁盘或分区的函数,当然也需要
对应移除磁盘或分区的函数,是handleDiskRemoved和handlePartitionRemoved函数。
- voidDirectVolume::handleDiskRemoved(constchar*devpath,NetlinkEvent*evt){
- intmajor=atoi(evt->findParam("MAJOR"));
- intminor=atoi(evt->findParam("MINOR"));
- charmsg[255];
- SLOGD("Volume%s%sdisk%d:%dremoved\n",getLabel(),getMountpoint(),major,minor);
- snprintf(msg,sizeof(msg),"Volume%s%sdiskremoved(%d:%d)",
- getLabel(),getMountpoint(),major,minor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,msg,false);
- /*设备移除后,广播通知framework*/
- setState(Volume::State_NoMedia);
- }
移除一块存储设备比较方便,当移除分区涉及的比较多,移除分区需要卸载分区,并且删除设备节点,以下是删除分区的源码:
- voidDirectVolume::handlePartitionRemoved(constchar*devpath,NetlinkEvent*evt){
- intmajor=atoi(evt->findParam("MAJOR"));
- intminor=atoi(evt->findParam("MINOR"));
- charmsg[255];
- intstate;
- SLOGD("Volume%s%spartition%d:%dremoved\n",getLabel(),getMountpoint(),major,minor);
- state=getState();
- if(state!=Volume::State_Mounted&&state!=Volume::State_Shared){
- return;
- }
- if((dev_t)MKDEV(major,minor)==mCurrentlyMountedKdev){
- snprintf(msg,sizeof(msg),"Volume%s%sbadremoval(%d:%d)",
- getLabel(),getMountpoint(),major,minor);
- /*mCurrentlyMountedKdev变量保存着目前正挂载在系统的存储设备的设备号,
- 这里的判断是这样:如果目前正在移除的分区等于挂载的存储设备的设备号,说明
- 该存储设备没有被安全删除,也就是没有先卸载后移除*/
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,msg,false);
- /*卸载挂载在asec目录的分区*/
- if(mVm->cleanupAsec(this,true)){
- SLOGE("FailedtocleanupASEC-unmountwillprobablyfail!");
- }
- /*卸载该分区挂载的所有挂载点,这里为什么用所有来形容了,因为Android
- 系统挂载一个分区的期间,重复挂载在好几个目录,将分区挂载在/mnt/asec目录,也挂载
- 在/mnt/secure/asec目录,也挂载在/mnt/sdcard目录下,总共三次挂载,谷歌不知为什么搞这么复杂?
- 待深究。。*/
- if(Volume::unmountVol(true)){
- SLOGE("Failedtounmountvolumeonbadremoval(%s)",
- strerror(errno));
- }else{
- SLOGD("Crisisaverted");
- }
- }elseif(state==Volume::State_Shared){
- snprintf(msg,sizeof(msg),"Volume%sbadremoval(%d:%d)",
- getLabel(),major,minor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,msg,false);
- /*这种情况是这样:如果手机跟电脑连接在一起,电脑正在使用sd卡,你把sd卡取出,
- 就会广播该错误信息给framework*/
- if(mVm->unshareVolume(getLabel(),"ums")){
- SLOGE("Failedtounsharevolumeonbadremoval(%s)",
- strerror(errno));
- }else{
- SLOGD("Crisisaverted");
- }
- }
- }
这几章介绍了磁盘事件的处理,总算可以告一段落,这些工作就是在main函数中的nm->start()函数负责的,下一篇文章可以分析其他处理函数了,但这些事件的处理起着至关重要的作用,如果没有做这些工作,framework也就根本不理会也不知道底层发生了什么事情。