android vold 分析,vold流程分析
前言
框架
VOLD分析.png
代码分析
init进程启动vold
Android起来后会解析init.rc,init.rc中启动了vold,代码如下
service vold /system/bin/vold
class core
socket vold stream 0660 root mount //①
ioprio be 2
① 表示系统会创建一个“ /dev/socket/vold”的socket文件,用来和上层通信
main函数
一切都是从main函数开始的,直接看main函数,简略如下:
int main() {
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
//1. 创建VolumeManager的实例,作为大总管管理上下层的消息传递
if (!(vm = VolumeManager::Instance())) {
SLOGE("Unable to create VolumeManager");
exit(1);
};
//2. NetlinkManager用来处理下层的消息
if (!(nm = NetlinkManager::Instance())) {
SLOGE("Unable to create NetlinkManager");
exit(1);
};
//3. 创建CommandListener来监听上层消息
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
//4. 根据flash类型解析根目录下的fstab.*文件
//并将文件中的每一个有效行都新建一个DirectVolume来管理
if (process_config(vm)) {
SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
}
//5. 建立用于底层通信的socket并处理底层netlink消息
if (nm->start()) {
SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
//6. 向/sys/block的uevent文件写入“add”触发内核上报uevent消息
coldboot("/sys/block");
//7. 一切都准备完毕,可以通过/dev/socket/vold与上层通信了
if (cl->startListener()) {
SLOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
//8. 主循环不退出
while(1) {
sleep(1000);
}
SLOGI("Vold exiting");
exit(0);
}
可以看到vold启动很简洁,没有什么难点,接下来分析NetlinkManager、CommandListener和VolumeManager
NetlinkManager
NetlinkManager主要是在start上:
int NetlinkManager::start() {
//1. 创建socket,可以在/proc/net/socket
if ((mSock = socket(PF_NETLINK,
SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}
//设置socket相关参数
if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
goto out;
}
if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
goto out;
}
//绑定相关的地址跟socket一样用就好了
if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
goto out;
}
//这个用来监听socket上报的消息,并处理消息
mHandler = new NetlinkHandler(mSock);
if (mHandler->start()) {
SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
goto out;
}
return 0;
out:
close(mSock);
return -1;
}
NetlinkHandler的继承关系如下(来自深入理解Android 卷I):
图片.png
主要知道,socketlistener已经实现了socket的监控方法,最后NetlinkHandler只需要实现具体的处理函数onEvent就行了:
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();
if (!subsys) {
SLOGW("No subsystem found in netlink event");
return;
}
// 这里会比对上传的事件是“block”还是“usb”,然后调用VolumeManager的handle函数
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt);
#ifdef USE_USB_MODE_SWITCH
}else if(!strcmp(subsys, "usb") || !strcmp(subsys, "scsi_device")) {
SLOGW("subsystem found in netlink event");
MiscManager *mm = MiscManager::Instance();
mm->handleEvent(evt);
#endif
}
}
VolumeManager
其实VM的handle函数就是遍历所有之前解析好的DirectVolume,并调用它们的handle函数
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
const char *devpath = evt->findParam("DEVPATH");
/* Lookup a volume to handle this device */
VolumeCollection::iterator it; bool hit = false;
//这个就是之前解析好的DirectVolume
for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
// 调用DirectVolume的处理函数
if (!(*it)->handleBlockEvent(evt)) {
#ifdef NETLINK_DEBUG
SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
#endif
hit = true;
break;
}
}
if (!hit) {
#ifdef NETLINK_DEBUG
SLOGW("No volumes handled block event for '%s'", devpath);
#endif }}
DirectVolume
代码比较长,挑重点看
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
const char *dp = evt->findParam("DEVPATH");
//遍历路径
for (it = mPaths->begin(); it != mPaths->end(); ++it) {
if (!strncmp(dp, *it, strlen(*it))) {
int action = evt->getAction();
const char *devtype = evt->findParam("DEVTYPE");
//通过action判断是插入还是拔出还是有变更
if (action == NetlinkEvent::NlActionAdd) {
int major = atoi(evt->findParam("MAJOR"));
int minor = atoi(evt->findParam("MINOR"));
char nodepath[255];
snprintf(nodepath,
sizeof(nodepath), "/dev/block/vold/%d:%d",
major, minor);
//创建dev节点
if (createDeviceNode(nodepath, major, minor)) {
SLOGE("Error making device node '%s' (%s)", nodepath,
strerror(errno));
}
//判断是什么类型,物理分区还是逻辑分区;
//如果sd卡只有一个分区,那么上报disk消息
//如果SD卡有多个分区,先上报disk消息,再上报多个partition消息
if (!strcmp(devtype, "disk")) {
handleDiskAdded(dp, evt);
} else {
handlePartitionAdded(dp, evt);
}
/* Send notification iff disk is ready (ie all partitions found) */
if (getState() == Volume::State_Idle) {
char msg[255];
snprintf(msg, sizeof(msg),
"Volume %s %s disk inserted (%d:%d)", getLabel(),
getFuseMountpoint(), mDiskMajor, mDiskMinor);
//通过广播发送消息,还记得之前提供的commandListener嘛?
//这个广播发送者就是它
mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
msg, false);
}
} else if (action == NetlinkEvent::NlActionRemove) {
if (!strcmp(devtype, "disk")) {
#ifdef SUPPORTED_MULTI_USB_PARTITIONS
if (!strcmp(getLabel(),USB_DISK_LABEL))
handlePartitionRemoved(dp, evt);
#endif
handleDiskRemoved(dp, evt);
} else {
handlePartitionRemoved(dp, evt);
}
} else if (action == NetlinkEvent::NlActionChange) {
if (!strcmp(devtype, "disk")) {
handleDiskChanged(dp, evt);
} else {
handlePartitionChanged(dp, evt);
}
} else {
SLOGW("Ignoring non add/remove/change event");
}
return 0;
}
}
errno = ENODEV;
return -1;
}
看看handleDiskAdded都做了什么事情
void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
mDiskMajor = atoi(evt->findParam("MAJOR"));
mDiskMinor = atoi(evt->findParam("MINOR"));
const char *tmp = evt->findParam("NPARTS");
//分区个数,这里应该是0
if (tmp) {
mDiskNumParts = atoi(tmp);
} else {
SLOGW("Kernel block uevent missing 'NPARTS'");
mDiskNumParts = 1;
}
if (mDiskNumParts == 0) {
setState(Volume::State_Idle);
//通过socket发送消息到JAVA层
snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",
getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);
mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,msg, false);
} else {
setState(Volume::State_Pending);
}
}
CommandListener
可以看到,CL向上层发送有设备插入的广播。上层接受到广播后,会进行处理,然后再向CL发送挂载或者卸载命令等。与上层所有的交互都通过socket进行。
先看下CommandListener的继承关系(来自深入理解Android 卷I):
图片.png
可以看到,它也是由socketListener继承而来,我们来看看CommandListener的初始化
CommandListener::CommandListener() :
FrameworkListener("vold") {
//CL模块支持的命令
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd());
registerCmd(new AsecCmd());
registerCmd(new ShareCmd());
registerCmd(new StorageCmd());
registerCmd(new XwarpCmd());}
都是registerCmd,所以说这些CMD一定都有共同的特征,他们都实现了一个runCommand的函数。
registerCmd是在FrameworkListener实现的,他的作用就是把Command保存到一个mCommands的列表中。
在上层来消息的时候,遍历这些Command的runCommand方法。
那CL是怎么发送消息给上层的呢?通过SocketClient的sendMsg方法,就可以跟上层通信了
总结
总得来说vold的代码并不困难,稍加分析就可以理清他的逻辑和思路,这篇文章也是编分析编写,如果有不对的地方,请各位大佬指出
android vold 分析,vold流程分析相关推荐
- 【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )
文章目录 前言 一.ActivityManagerService.attachApplicationLocked 二.ActivityStackSupervisor.attachApplication ...
- c++builder启动了怎么停止_App 竟然是这样跑起来的 —— Android App/Activity 启动流程分析...
在我的上一篇文章: AJie:按下电源键后竟然发生了这一幕 -- Android 系统启动流程分析zhuanlan.zhihu.com 我们分析了系统在开机以后的一系列行为,其中最后一阶段 AMS( ...
- android 屏幕旋转流程,android自动屏幕旋转流程分析.doc
android自动屏幕旋转流程分析.doc android自动屏幕旋转流程分析 在android设置(Settings)中我们可以看到显示(display)下有一个自动屏幕旋转的checkbox, 如 ...
- Android8.0(34)----Android 8.0 Settings流程分析与变动
Android 8.0 Settings流程分析与变动 一,相比Android Settings 7.0 如下图,在7.0的基础上,去掉了7.0新加的侧滑菜单(可能是觉得有点鸡肋吧).多加了一级页面, ...
- Android 手机灭屏流程分析详解
参考地址:https://www.jianshu.com/p/9241f3a91095 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容: 1.前言 2.Pow ...
- android加载efi分区,高通Android UEFI XBL 代码流程分析
高通Android UEFI XBL 代码流程分析 背景 之前学习的lk阶段点亮LCD的流程算是比较经典,但是高通已经推出了很多种基于UEFI方案的启动架构. 所以需要对这块比较新的技术进行学习.在学 ...
- Android 7.0系统启动流程分析
随着Android版本的升级,aosp项目中的代码也有了些变化,本文基于Android 7.0分析Android系统启动流程.当我们按下电源键后,整个Android设备大体经过了一下过程: 今天我们 ...
- Android 8.0 recovery 流程分析
这里主要分析non A/B模式下的recovery流程 A/B模式下的recovery在boot中 后续会不断补充,如果有疏漏或者错误的地方,请指出,共同学习,谢谢! 一.流程分析 首先列出recov ...
- Android 源码 installPackage 流程分析
installPackage 安装流程最终会调用 PackageManagerService 对应方法,当然是使用 binder 机制得以实现跨进程通信. 先来看看 installPackage 流程 ...
- Android开机向导启动流程分析
Android开机向导启动流程 首先来看Android启动流程: 1.Bootloader(系统启动加载器,将Linux加载到RAM): 2.Kernel 3.init进程 4.Zygote(Zygo ...
最新文章
- 基于sqlcmd命令行工具管理SQL server
- ACM模板--邻接矩阵 无向图
- VC2019消息框编程总结
- Pytorch的网络结构可视化:Netron与TensorBoardX
- SAP Spartacus B2B页面Banner Component的路由url是在哪里定义的
- JAVA项目中classpath路径详解
- Java动态代理全面分析
- OmniGraffler软件和激活码
- sublime 插件(持续更新)
- CentOS 安装快速Nginx-1.12.0
- 利用Metaweblog技术的API接口同步到多个博客网站(详细)
- JDK15安装与环境变量配置
- STM32——红外遥控
- 机器学习实战----初识泰坦尼克
- 使用laser_filters屏蔽车架
- 请求通道在等待 00:00:58.9616639 以后答复时超时。增加传递给请求调用的超时值,或者增加绑定上的 SendTimeout 值。分配给此操作的时间可能是更长超时的一部分。...
- dellnas存储服务器型号,Dell Storage NX系列NAS存储
- php的sql什么意思,php sql删除语句是什么
- nginx和ftp搭建图片服务器
- 浅谈MYSQL增量备份
热门文章
- C#读写配置文件(config.ini)入门
- 理论+实验:ELK日志分析系统
- Linux进程信号【信号产生】
- ELK部署+filebeat应用
- CoordConv卷积改进yolov5-6.1
- opencv学习笔记二十一:使用HSV颜色空间实现颜色识别
- 根号算法——暴力美学
- 典型J2EE系统架构图,该图为了自己使用而创建,如有什么不当的地方还请高手们指正,谢谢!
- 如何将本地项目上传git仓库的详细步骤
- 广西师范大学计算机科学与信息工程学院官网,广西师范大学文件-广西师范大学计算机科学与信息工程学院.PDF...