Android-vold源码分析之handleBlockEvent(5)

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

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

void handleDiskAdded(const char *devpath, NetlinkEvent *evt); void handleDiskRemoved(const char *devpath, NetlinkEvent *evt); void handleDiskChanged(const char *devpath, NetlinkEvent *evt); void handlePartitionAdded(const char *devpath, NetlinkEvent *evt); void handlePartitionRemoved(const char *devpath, NetlinkEvent *evt); void handlePartitionChanged(const char *devpath, NetlinkEvent *evt);
以下是精简版的handleBlockEvent函数:

if (action == NetlinkEvent::NlActionAdd) { ... if (!strcmp(devtype, "disk")) { handleDiskAdded(dp, evt); } else { handlePartitionAdded(dp, evt); } } else if (action == NetlinkEvent::NlActionRemove) { if (!strcmp(devtype, "disk")) { handleDiskRemoved(dp, evt); } else { handlePartitionRemoved(dp, evt); } } else if (action == NetlinkEvent::NlActionChange) { if (!strcmp(devtype, "disk")) { handleDiskChanged(dp, evt); } else { handlePartitionChanged(dp, evt); } }
这样看起来就比较清楚每个函数的作用了,贴源码其实是比较直接的方法,程序员对代码都比较敏感,一看就明白意思,好,开始分析。

void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) { mDiskMajor = atoi(evt->findParam("MAJOR")); mDiskMinor = atoi(evt->findParam("MINOR")); const char *tmp = evt->findParam("NPARTS"); if (tmp) { mDiskNumParts = atoi(tmp); } else { SLOGW("Kernel block uevent missing '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,系统插入一块存储设备,源码如下: void DirectVolume::broadcastDiskAdded() { setState(Volume::State_Idle); char msg[255]; snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)", getLabel(), getMountpoint(), mDiskMajor, mDiskMinor); mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted, msg, false); } **********************************************************************************/ broadcastDiskAdded(); } else { /********************************************************************************** **setState函数起到的作用很大,相当于一只信鸽,每次sd卡发现状态改变,该函数马上就将 **最新的状态广播给framework,后面会详细介绍该函数与framework的通信。 **********************************************************************************/ setState(Volume::State_Pending); } }
随后就是handlePartitionAdded函数了,这个函数要识别插入的设备的所有分区,源码如下:

void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); int part_num; const char *tmp = evt->findParam("PARTN"); if (tmp) { part_num = atoi(tmp); } else { SLOGW("Kernel block uevent missing 'PARTN'"); part_num = 1; } if (part_num > mDiskNumParts) { mDiskNumParts = part_num; } if (major != mDiskMajor) { SLOGE("Partition '%s' has a different major than its disk!", devpath); return; } /********************************************************************************** **上面就是做一下其他的判断,不重要; **MAX_PARTITIONS定义在system/vold/DirectVolume.h文件中,声明如下: static const int MAX_PARTITIONS = 4; Android系统支持太有限,谷歌太懒,呵呵,最多就识别4个分区,当然如果有些厂商想多实现 **分区的识别数量,需要修改源码; **我觉得,Android系统是做得不错,但磁盘管理方面不太完善,自从分析修改了vold源码, **vold支持得太少,也许谷歌以前只想到应用于手机,要是哥想识别一块10个分区的硬盘, **咋办?修改源码咯。。。 **********************************************************************************/ if (part_num > MAX_PARTITIONS) { SLOGE("Dv:partAdd: ignoring part_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系统,也可能遇到这种情况,以下是源码:

void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); if ((major != mDiskMajor) || (minor != mDiskMinor)) { return; } SLOGI("Volume %s disk has changed", getLabel()); const char *tmp = evt->findParam("NPARTS"); if (tmp) { mDiskNumParts = atoi(tmp); } else { SLOGW("Kernel block uevent missing '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); } } }
分区的改变:

void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor); }
以上两个参数基本没涉及到什么重要的内容,看下源码就行。
刚才上面分析了handleDiskAdded和handlePartitionAdded,这两个增加磁盘或分区的函数,当然也需要
对应移除磁盘或分区的函数,是handleDiskRemoved和handlePartitionRemoved函数。

void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); char msg[255]; SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor); snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)", getLabel(), getMountpoint(), major, minor); mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved, msg, false); /*设备移除后,广播通知framework*/ setState(Volume::State_NoMedia); }
移除一块存储设备比较方便,当移除分区涉及的比较多,移除分区需要卸载分区,并且删除设备节点,以下是删除分区的源码:

void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) { int major = atoi(evt->findParam("MAJOR")); int minor = atoi(evt->findParam("MINOR")); char msg[255]; int state; SLOGD("Volume %s %s partition %d:%d removed\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 %s bad removal (%d:%d)", getLabel(), getMountpoint(), major, minor); /*mCurrentlyMountedKdev变量保存着目前正挂载在系统的存储设备的设备号, 这里的判断是这样:如果目前正在移除的分区等于挂载的存储设备的设备号,说明 该存储设备没有被安全删除,也就是没有先卸载后移除*/ mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, msg, false); /*卸载挂载在asec目录的分区*/ if (mVm->cleanupAsec(this, true)) { SLOGE("Failed to cleanup ASEC - unmount will probably fail!"); } /*卸载该分区挂载的所有挂载点,这里为什么用所有来形容了,因为Android 系统挂载一个分区的期间,重复挂载在好几个目录,将分区挂载在/mnt/asec目录,也挂载 在/mnt/secure/asec目录,也挂载在/mnt/sdcard目录下,总共三次挂载,谷歌不知为什么搞这么复杂? 待深究。。*/ if (Volume::unmountVol(true)) { SLOGE("Failed to unmount volume on bad removal (%s)", strerror(errno)); } else { SLOGD("Crisis averted"); } } else if (state == Volume::State_Shared) { snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)", getLabel(), major, minor); mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, msg, false); /*这种情况是这样:如果手机跟电脑连接在一起,电脑正在使用sd卡,你把sd卡取出, 就会广播该错误信息给framework*/ if (mVm->unshareVolume(getLabel(), "ums")) { SLOGE("Failed to unshare volume on bad removal (%s)", strerror(errno)); } else { SLOGD("Crisis averted"); } } }
这几章介绍了磁盘事件的处理,总算可以告一段落,这些工作就是在main函数中的nm->start()函数负责的,下一篇文章可以分析其他处理函数了,但这些事件的处理起着至关重要的作用,如果没有做这些工作,framework也就根本不理会也不知道底层发生了什么事情。
下一篇继续磁盘操作部分。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个基于Matlab实现Vold-Kalman滤波器的代码示例: ``` function [xhat,Phat] = vold_kalman(y,u,x0,P0,Q,R,dt) % Vold-Kalman滤波器 % 输入: % y - 观测数据 % u - 控制数据 % x0 - 初始状态 % P0 - 初始协方差矩阵 % Q - 过程噪声协方差矩阵 % R - 观测噪声协方差矩阵 % dt - 时间步长 % 输出: % xhat - 状态估计值 % Phat - 协方差估计矩阵 % 系统模型 f = @(x, u) [x(1) + u(1)*cos(x(3))*dt; x(2) + u(1)*sin(x(3))*dt; x(3) + u(2)*dt]; h = @(x) [atan2(x(2),x(1)); sqrt(x(1)^2 + x(2)^2)]; % 初始化滤波器 xhat = x0; Phat = P0; % 迭代 for i = 1:length(y) % 预测 uhat = u(:,i); [xhat, Fx, Fu, Q] = ekf_predict(f, xhat, uhat, Phat, Q, dt); % 更新 yhat = y(:,i); [xhat, Phat, H, R] = ekf_update(h, xhat, yhat, Phat, R); % 存储结果 X(:,i) = xhat; P_all(:,:,i) = Phat; end end function [xhat,Fx,Fu,Q] = ekf_predict(f,x,u,P,Q,dt) % 扩展卡尔曼滤波器预测 % 输入: % f - 状态方程 % x - 状态估计值 % u - 控制输入 % P - 协方差估计矩阵 % Q - 过程噪声协方差矩阵 % dt - 时间步长 % 输出: % xhat - 预测状态估计值 % Fx - 状态转移矩阵 % Fu - 控制输入矩阵 % Q - 更新后的过程噪声协方差矩阵 % 计算状态转移矩阵和控制输入矩阵 [Fx,Fu] = jacobian(f,x,u); % 预测状态估计值和协方差矩阵 xhat = f(x,u); P = Fx*P*Fx' + Fu*Q*Fu'; % 更新过程噪声协方差矩阵 Q = diag([0.01,0.01,0.01]); % TODO: 根据实际问题调整 end function [xhat,P,H,R] = ekf_update(h,x,y,P,R) % 扩展卡尔曼滤波器更新 % 输入: % h - 观测方程 % x - 状态估计值 % y - 观测值 % P - 协方差估计矩阵 % R - 观测噪声协方差矩阵 % 输出: % xhat - 更新后的状态估计值 % P - 更新后的协方差估计矩阵 % H - 观测矩阵 % R - 更新后的观测噪声协方差矩阵 % 计算观测矩阵 H = jacobian(h,x); % 计算观测噪声协方差矩阵 R = diag([0.1,0.1]); % TODO: 根据实际问题调整 % 计算卡尔曼增益 K = P*H'/(H*P*H' + R); % 更新状态估计值和协方差矩阵 xhat = x + K*(y - h(x)); P = (eye(size(P)) - K*H)*P; end function [F,J] = jacobian(f,x,u) % 计算函数f(x,u)关于x和u的雅可比矩阵 % 输入: % f - 目标函数 % x - 自变量x % u - 自变量u % 输出: % F - 状态转移矩阵 % J - 控制输入矩阵 % 计算状态转移矩阵 n = length(x); Fx = zeros(n,n); for i = 1:n Fx(:,i) = gradient(f(x,u),x(i)); end F = eye(n) + Fx; % 计算控制输入矩阵 if nargin > 2 m = length(u); Fu = zeros(n,m); for i = 1:m Fu(:,i) = gradient(f(x,u),u(i)); end J = Fu; end end ``` 这个代码示例中实现了Vold-Kalman滤波器的预测和更新步骤,同时也使用了扩展卡尔曼滤波器(EKF)来处理非线性系统。需要根据具体问题调整系统模型和协方差矩阵,以获得更好的滤波效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值