ANDROID中的VOLD分析
现在可能很少有人会用mknod这个命令了,也很少有使用它的机会,但就在几年前,这还是一项linux工程师的必备技能,在制作文件系统前或加载新的驱动前,我们必须小心翼翼的创建设备节点。
不需要使用mknod并不是他消失了,而是我们有了更好更智能的方法。
linux对于热插拔的支持并不是生来就有的,而是经历了一个复杂而有戏剧性的过程,全球linux爱好者用脚投出了他们保贵的一票,udev最终成为事实上的标准。
在android中,取代udev的是vold,我们这里不去过多的讨论为什么android不继续使用udev,但要知道vold的机制和udev是一样的,理解了udev,也就理解了vold。android一出生就没有尊守传统linux的许多标准,当然也不能指望udev能很好的服务于android。android社区的选择是别起炉灶,为android定做一套udev,这就是vold了。
无论是udev还是vold,都是基于sysfs的,sysfs为内核与用户层的通讯提供了一种全新的方式,并将这种方式加以规范。
kernel层能检测到有新的设备接入,并能为之加载相应的驱动,但如何通知用户层呢?这就是sysfs的工作,内核中的sysfs机制要求当有新的驱动加载时给用户层发送相应的event.但这些event只尽告知的义务,具体怎么处理,这就是vold(或者udev)的事了。
对于用户层而言,我们无需关心sysfs的细节,只要知道sysfs能向用户层提供什么就行了。
首先,我们要知道如何接收来自内核的event.
Netlink socket大家应该不会陌生吧,socket这套东西不仅能用于网络间的通讯,也用能用于进程间的通讯,像这种内核态与用户沟通的活,自然也少不了它。
下面的内容摘自vold(NetlinkManager.cpp)
if ((mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) { SLOGE("Unable to create uevent socket: %s", strerror(errno)); return -1; } if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) { SLOGE("Unable to set uevent socket options: %s", strerror(errno)); return -1; } if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { SLOGE("Unable to bind uevent socket: %s", strerror(errno)); return -1; } |
这就是监听sysfs的uevent的socket的关键设置,有网络编程背影的人很容易理解上面这段代码。
下面紧接着的问题就是这个socket通路会给我们什么消息:
我们进入/sys/block/mmcblk0(也可以是/sys/block下的其它目录),执行:
cat *
MAJOR=179 MINOR=0 DEVNAME=mmcblk0 DEVTYPE=disk PHYSDEVPATH=/class/mmc_host/mmc0/mmc0:1234 PHYSDEVBUS=mmc PHYSDEVDRIVER=mmcblk NPARTS=1 ...... |
我们通过socket从内核处到的envent中所包含的信息也与此相似,是一个包含这些信息的文本,可能是如下格式的
add@/block/PHYSDEVDRIVER=mmcblk PHYSDEVPATH=/class/mmc_host/mmc0/mmc0:1234 DEVNAME=mmcblk0 MAJOR=179 MINOR=0 PHYSDEVDRIVER=mmcblk ...... |
sysfs传上来的是一个多行的文本(这点要特别注意,并不只是add@/block/PHYSDEVDRIVER=mmcblk
这一行),vold要对这个多行文档进行解析,然后决定怎么做。vold会把解协出来的消息再通可别一个vold的socket传到其它的进程,同时接收其它进程的反馈。
向sysfs目录(或子目录)下面的uevent文件写入”add/n”字符也会触发内核上发这些uevent,相当于重新执行了一次热插拔。
例:echo "add" > /sys/block/mmcblk0/uevent
系统启动时vold错过了的消息可以用这个特性重新触发。
分析vold的源码,要有一定的C++的基础和设计模式的知识,习惯过程式设计的程序员在读vold时会有很大的困难,不过幸好vold代码不多。另外,与vold相关的大量机密都在libsysutils中,千万不要漏掉这个库。
先看看下图SocketListener这套架构,监听sysfs与其它进程的消息,全仰仗这套框架。
在netLinkListener中,VOLD的重点是OnEvent这个虚接口的实现,而CommandSistener中,VOLD处理的重点则是分发VoldCommand类。VoldCommand是由FrameworkCommand派生出的,而VolumeCmd,ShareCmd等子类则是各种操作的封装。
先看看对 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; } if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); } else if (!strcmp(subsys, "switch")) { vm->handleSwitchEvent(evt); } else if (!strcmp(subsys, "battery")) { } else if (!strcmp(subsys, "power_supply")) { } } |
|
这里主要是通过基类解析的uevent消息分别调用不同的处理。如果看了上面的图还有人问NetlinkHandler::onEvent是在什么时候调用的,那就要补一下C++了,这不是一两句话能说得清楚的。 |
VoldCommand主要是实现对runcommand动作的封装,在 FrameworkListener会根据收到的消息选择相应的派生类。
bool FrameworkListener::onDataAvailable(SocketClient *c) { char buffer[255]; int len; if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) { SLOGE("read() failed (%s)", strerror(errno)); return errno; } else if (!len) return false; int offset = 0; int i; for (i = 0; i < len; i++) { if (buffer[i] == '/0') { dispatchCommand(c, buffer + offset); offset = i + 1; } } return true; } |
|
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) { FrameworkCommandCollection::iterator i; int argc = 0; char *argv[FrameworkListener::CMD_ARGS_MAX]; char tmp[255]; char *p = data; char *q = tmp; bool esc = false; bool quote = false; int k; memset(argv, 0, sizeof(argv)); memset(tmp, 0, sizeof(tmp)); while(*p) { if (*p == '//') { if (esc) { *q++ = '//'; esc = false; } else esc = true; p++; continue; } else if (esc) { if (*p == '"') *q++ = '"'; else if (*p == '//') *q++ = '//'; else { cli->sendMsg(500, "Unsupported escape sequence", false); goto out; } p++; esc = false; continue; } if (*p == '"') { if (quote) quote = false; else quote = true; p++; continue; } *q = *p++; if (!quote && *q == ' ') { *q = '/0'; argv[argc++] = strdup(tmp); memset(tmp, 0, sizeof(tmp)); q = tmp; continue; } q++; } argv[argc++] = strdup(tmp); #if 0 for (k = 0; k < argc; k++) { SLOGD("arg[%d] = '%s'", k, argv[k]); } #endif if (quote) { cli->sendMsg(500, "Unclosed quotes error", false); goto out; } for (i = mCommands->begin(); i != mCommands->end(); ++i) { FrameworkCommand *c = *i; if (!strcmp(argv[0], c->getCommand())) { if (c->runCommand(cli, argc, argv)) {//调用派生类的接口。 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno)); } goto out; } } cli->sendMsg(500, "Command not recognized", false); out: int j; for (j = 0; j < argc; j++) free(argv[j]); return; } |
Volume定义了各种磁盘的操作,属于工具类。
要将这些类是如何组织在一起的呢,关键是下面两个工厂类。
从上图可以看出,VolumeManager和NetlinkManager将整 个系统组织在一 起,NetlinkManager负责翻译sysfs的uevent事件并传递给其它的进程,VolumeManager则负责接收其它进程反馈的消息,并分发给VoldCommand类作相应的处理。
最后,我们分析一下vold是如何初始化这些类的:
int main() { VolumeManager *vm; CommandListener *cl; NetlinkManager *nm; SLOGI("Vold 2.1 (the revenge) firing up"); mkdir("/dev/block/vold", 0755); /* Create our singleton managers */ if (!(vm = VolumeManager::Instance())) {//实例化 SLOGE("Unable to create VolumeManager"); exit(1); }; if (!(nm = NetlinkManager::Instance())) {//实例化 SLOGE("Unable to create NetlinkManager"); exit(1); }; cl = new CommandListener(); //创建vold socket,用于向其它进程转发解析的sysfs event,并接收其进程的命令。 vm->setBroadcaster((SocketListener *) cl); nm->setBroadcaster((SocketListener *) cl); if (vm->start()) { SLOGE("Unable to start VolumeManager (%s)", strerror(errno)); exit(1); } if (process_config(vm)) { //解析配置 SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno)); } if (nm->start()) {//创建监听sysfs的socket SLOGE("Unable to start NetlinkManager (%s)", strerror(errno)); exit(1); } coldboot("/sys/block"); //冷启动,vold错过了一些uevent,重新触发。向sysfs的uevent文件写入”add/n” 字符也可以触发sysfs事件,相当执行了一次热插拔。 /* * Switch uevents are broken. * For now we manually bootstrap * the ums switch */ { FILE *fp; char state[255]; /* * Now that we're up, we can respond to commands */ if (cl->startListener()) { //监听上层的反馈 SLOGE("Unable to start CommandListener (%s)", strerror(errno)); exit(1); } // Eventually we'll become the monitoring thread while(1) { sleep(1000); } SLOGI("Vold exiting"); exit(0); } |
etc/ vold.fstab的配置文件
例:(每一行的结束不能有空格等任何字符,vold对这个地方的处理有bug.)
dev_mount sdcard /data/disk auto /block/sda |
格式是:
type label mount_point part sysfs_path sysfs_path
sysfs_path可以有多个,但最后不要有空格,否则会解析错误
part指定分区个数,如果是auto则只有一个分区
ANDROID中的VOLD分析相关推荐
- Android中AppWidget的分析与应用:AppWidgetProvider .
from: http://blog.csdn.net/thl789/article/details/7887968 本文从开发AppWidgetProvider角度出发,看一个AppWidgetPrv ...
- android中SELINUX规则分析和语法简介
点击打开链接 1. SELINUX是可以理解为一种Android上面的安全机制,是有美国国家安全局和一些公司设计的一个针对linux的安全加强系统 我们可以通过配置SELINUX的相关policy,来 ...
- Android中Parcel的分析以及使用
简单点来说:Parcel就是一个存放读取数据的容器, android系统中的binder进程间通信(IPC)就使用了Parcel类来进行客户端与服务端数据的交互,而且AIDL的数据也是通过Parcel ...
- Android中图片压缩分析(上)
此文章首发:https://mp.weixin.qq.com/s/QZ-XTsO7WnNvpnbr3DWQmg 一.前言 在 Android 中进行图片压缩是非常常见的开发场景,主要的压缩方法有两种: ...
- Android中Bitmap的分析与使用
下面总结一下Bitmap的分析与使用 Bitmap的创建 创建Bitmap的时候,Java不提供new Bitmap()的形式去创建,而是通过BitmapFactory中的静态方法去创建,如:Bitm ...
- android中hprof文件分析
Hprof基本概念 hprof最初是由J2SE支持的一种二进制堆转储格式,hprof文件保存了当前java堆上所有的内存使用信息,能够完整的反映虚拟机当前的内存状态. 格式 Hprof文件由Fixed ...
- Android中Touch事件分析--解决HorizontalScrollView滑动和按钮事件触发问题
1).按下按钮,不滑动,触发按钮功能 (2).按下按钮,滑动触发滑动事件 这里的按下包含长按和短按情况 首先要解决这个问题需要明白Android中的Touch事件是如何进行处理的,这里有一篇文章:An ...
- Android中应用安装分析
#1 安装方式 1 安装系统APK和预制APK时,通过PMS的构造函数中安装,即第一次开机时安装应用,没有安装界面. 2 网络下载安装,通过应用商店等,即调用PackageManager.instal ...
- Android中Parcel的分析和使用
http://www.360doc.com/content/13/0419/12/9171956_279433672.shtml
最新文章
- 决策树-特征属性选择划分
- 笔记-项目沟通管理-沟通表达方式
- 经典C语言程序100例之七九
- 知己知彼 防范攻击:网络攻击步骤与黑客攻击原理
- CCNET配置文件配置工具
- 推荐算法三视角:矩阵,图,时间线
- mysql存储过程语法 if_mysql存储过程语法 if
- python环境变量的配置_python基础教程-第一讲-带你进入python的世界
- python可以做什么-Python简直就是万能的,你用Python都做过哪些事?
- 每周荐书:大数据、深度学习、架构(评论送书)
- idea 导入maven项目
- Hadoop学习笔记(二):MapReduce的进度和状态
- 标准C语言程序设计第七版pdf,C语言程序设计课程标准.PDF
- 【面试】造价工程师面试试题汇总
- 华为笔记本触摸板使用技巧
- CC(标准)版D碟收藏指南(三)
- ICPC焦作站(E、F)+思维+树上dp
- kotlin 中关键字 lateinit
- 今日学习的关于笔记本电脑和origin绘图的一些
- 广二师的计算机专业好不,惠州学院专业_广东第二师范学院和惠州学院非师范专业哪个比较好...
热门文章
- 我,是一个培训班出来的程序员
- 【java】springboot项目启动数据加载内存中的三种方法
- 搭建CTF-AWD训练平台
- 超市商品管理系统设计
- 【ctrl+space】键位解除占用+永久删除微软拼音输入法
- 云原生CI/CD:tekton/pipeline之认证篇
- 计算机ab级ppt,计算机二级MS_Office考试PPT题型汇总附答案
- 有趣现象:同一个java文件中有2个类,一个public,一个无类修饰符,各有一个main函数,谁在前先执行谁!
- 计算机工程与科学是sci,系统科学与系统工程有哪些sci期刊
- Adobe 系列软件中英文版本的切换