前言

框架

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流程分析相关推荐

  1. 【Android 启动过程】Activity 启动源码分析 ( ActivityThread 流程分析 二 )

    文章目录 前言 一.ActivityManagerService.attachApplicationLocked 二.ActivityStackSupervisor.attachApplication ...

  2. c++builder启动了怎么停止_App 竟然是这样跑起来的 —— Android App/Activity 启动流程分析...

    在我的上一篇文章: AJie:按下电源键后竟然发生了这一幕 -- Android 系统启动流程分析​zhuanlan.zhihu.com 我们分析了系统在开机以后的一系列行为,其中最后一阶段 AMS( ...

  3. android 屏幕旋转流程,android自动屏幕旋转流程分析.doc

    android自动屏幕旋转流程分析.doc android自动屏幕旋转流程分析 在android设置(Settings)中我们可以看到显示(display)下有一个自动屏幕旋转的checkbox, 如 ...

  4. Android8.0(34)----Android 8.0 Settings流程分析与变动

    Android 8.0 Settings流程分析与变动 一,相比Android Settings 7.0 如下图,在7.0的基础上,去掉了7.0新加的侧滑菜单(可能是觉得有点鸡肋吧).多加了一级页面, ...

  5. Android 手机灭屏流程分析详解

    参考地址:https://www.jianshu.com/p/9241f3a91095 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容: 1.前言 2.Pow ...

  6. android加载efi分区,高通Android UEFI XBL 代码流程分析

    高通Android UEFI XBL 代码流程分析 背景 之前学习的lk阶段点亮LCD的流程算是比较经典,但是高通已经推出了很多种基于UEFI方案的启动架构. 所以需要对这块比较新的技术进行学习.在学 ...

  7. Android 7.0系统启动流程分析

    随着Android版本的升级,aosp项目中的代码也有了些变化,本文基于Android 7.0分析Android系统启动流程.当我们按下电源键后,整个Android设备大体经过了一下过程:  今天我们 ...

  8. Android 8.0 recovery 流程分析

    这里主要分析non A/B模式下的recovery流程 A/B模式下的recovery在boot中 后续会不断补充,如果有疏漏或者错误的地方,请指出,共同学习,谢谢! 一.流程分析 首先列出recov ...

  9. Android 源码 installPackage 流程分析

    installPackage 安装流程最终会调用 PackageManagerService 对应方法,当然是使用 binder 机制得以实现跨进程通信. 先来看看 installPackage 流程 ...

  10. Android开机向导启动流程分析

    Android开机向导启动流程 首先来看Android启动流程: 1.Bootloader(系统启动加载器,将Linux加载到RAM): 2.Kernel 3.init进程 4.Zygote(Zygo ...

最新文章

  1. 基于sqlcmd命令行工具管理SQL server
  2. ACM模板--邻接矩阵 无向图
  3. VC2019消息框编程总结
  4. Pytorch的网络结构可视化:Netron与TensorBoardX
  5. SAP Spartacus B2B页面Banner Component的路由url是在哪里定义的
  6. JAVA项目中classpath路径详解
  7. Java动态代理全面分析
  8. OmniGraffler软件和激活码
  9. sublime 插件(持续更新)
  10. CentOS 安装快速Nginx-1.12.0
  11. 利用Metaweblog技术的API接口同步到多个博客网站(详细)
  12. JDK15安装与环境变量配置
  13. STM32——红外遥控
  14. 机器学习实战----初识泰坦尼克
  15. 使用laser_filters屏蔽车架
  16. 请求通道在等待 00:00:58.9616639 以后答复时超时。增加传递给请求调用的超时值,或者增加绑定上的 SendTimeout 值。分配给此操作的时间可能是更长超时的一部分。...
  17. dellnas存储服务器型号,Dell Storage NX系列NAS存储
  18. php的sql什么意思,php sql删除语句是什么
  19. nginx和ftp搭建图片服务器
  20. 浅谈MYSQL增量备份

热门文章

  1. C#读写配置文件(config.ini)入门
  2. 理论+实验:ELK日志分析系统
  3. Linux进程信号【信号产生】
  4. ELK部署+filebeat应用
  5. CoordConv卷积改进yolov5-6.1
  6. opencv学习笔记二十一:使用HSV颜色空间实现颜色识别
  7. 根号算法——暴力美学
  8. 典型J2EE系统架构图,该图为了自己使用而创建,如有什么不当的地方还请高手们指正,谢谢!
  9. 如何将本地项目上传git仓库的详细步骤
  10. 广西师范大学计算机科学与信息工程学院官网,广西师范大学文件-广西师范大学计算机科学与信息工程学院.PDF...