ANDROID中的VOLD分析

article/2025/10/2 18:34:09

    现在可能很少有人会用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错过了的消息可以用这个特性重新触发。

clip_image002

分析vold的源码,要有一定的C++的基础和设计模式的知识,习惯过程式设计的程序员在读vold时会有很大的困难,不过幸好vold代码不多。另外,与vold相关的大量机密都在libsysutils中,千万不要漏掉这个库。

先看看下图SocketListener这套架构,监听sysfs与其它进程的消息,全仰仗这套框架。

clip_image004

在netLinkListener中,VOLD的重点是OnEvent这个虚接口的实现,而CommandSistener中,VOLD处理的重点则是分发VoldCommand类。VoldCommand是由FrameworkCommand派生出的,而VolumeCmdShareCmd等子类则是各种操作的封装。

先看看对 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;

}

 
 
 

clip_image006

Volume定义了各种磁盘的操作,属于工具类。

clip_image008

要将这些类是如何组织在一起的呢,关键是下面两个工厂类。

 
 

clip_image010

 
 

clip_image012

从上图可以看出,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则只有一个分区


http://chatgpt.dhexx.cn/article/tWC8RyAp.shtml

相关文章

Android -- Vold机制简要分析

Android -- Vold机制简要分析 Vold是用于管理和控制Android外部存储介质的后台进程&#xff0c;这里说的管控&#xff0c;主要包括SD卡的插拔、挂载/卸载和格式化等&#xff1b;它是Android平台外部存储系统的管控枢纽。 Vold的整个控制模块主要由三个类模块构成&#xff1a;Ne…

Android Vold简介(一)

Vold(volume Daemon)&#xff0c;即Volume守护进程&#xff0c;用来管理Android中存储类的热拔插事件&#xff0c;处于Kernel和Framework之间&#xff0c;是两个层级连接的桥梁。先来看一下Vold在Android系统的整体架构。 该图主要包含了Framework和Vold进程的&#xff0c;Ke…

vold

一、Vold工作机制 Vold是Volume Daemon的缩写&#xff0c;它是Android平台中外部存储系统的管控中心&#xff0c;是管理和控制Android平台外部存储设备的后台进程。其功能主要包括&#xff1a;SD卡的插拔事件检测、SD卡挂载、卸载、格式化等。 如上图所示&#xff0c;Vold中的…

Vold工作流程分析学习

一 Vold工作机制分析 vold进程&#xff1a;管理和控制Android平台外部存储设备&#xff0c;包括SD插拨、挂载、卸载、格式化等&#xff1b; vold进程接收来自内核的外部设备消息。 Vold框架图如下&#xff1a; Vold接收来自内核的事件&#xff0c;通过netlink机制。 Netlink 是…

Android vold介绍

目录 1. 前言2. vold概述3. vold初始化|- -vm->start()|- -process_config|- -hardware::configureRpcThreadpool|- -vold::VoldNativeService::start|- -nm->start() 4. StorageManagerService|- -SM与vold建立关联|- - -startService(serviceClass) |- -StorageManager…

<Android开发> Android vold - 第一篇 vold前言简介

本系列主要介绍 Android vold&#xff0c;分为以下篇章 &#xff1c;Android开发&#xff1e; Android vold - 第一篇 vold前言简介 &#xff1c;Android开发&#xff1e; Android vold - 第二篇 vold 的main()函数简介 &#xff1c;Android开发&#xff1e; Android vold - 第…

Vold原理介绍

一、 Vold简介 Android中Vold是volume Daemon&#xff0c;即Volume守护进程&#xff0c;用来管理Android中存储类的热拔插事件。这里的热插拔涉及的场景如&#xff1a; 1. 手机usb以MTP或者传输照片方式插拔PC端后磁盘数据的挂卸载&#xff1b; 2. 设备开关机过程中存储设备各分…

C语言Switch....case用法

概述 C语言switch语句通常用于多个条件判断&#xff0c;根据不同情况执行不同的代码块。它的使用形式如下&#xff1a; switch&#xff08;表达式&#xff09; { case 常量表达式1&#xff1a; 语句序列1 break; case 常量表达式2&#xff1a; 语句序列2 break; ...... default…

MySQL 入门:Case 语句很好用

引言 MySQL CASE 是一个 MySQL 语句查询关键字&#xff0c;它定义了处理循环概念以执行条件集并使用 IF ELSE 返回匹配案例的方式。 MySQL 中的 CASE 是一种控制语句&#xff0c;它验证条件案例集&#xff0c;并在第一个案例满足 else 值时显示值并退出循环。 如果没有找到 T…

Shell 编程之 case 语句

一、case 语句 1、case 语句概述 (1)case 语句的作用 使用 case 语句改写 if 多分支可以使脚本结构更加清晰、层次分明。针对变量的不同取 值&#xff0c;执行不同的命令序列。 2、case 语句的结构: case 变量值 in 模式 1) 命令序列 1 ;; 模式 2) 命令序列 2 ;; *…

【shell】case实现简单的系统工具箱

case实现简单的系统工具箱 case实现简单的系统工具箱 #!/usr/bin/bash #system manage #by racon 2020-04-19menu() {cat <<-EOF########################################## h. help ## f. disk partition ## d. filesystem mount ## m. memory ## u. system lo…

SQL CASE语句的使用

SQL CASE语句的使用 CASE是一个控制流语句&#xff0c;其作用与IF-THEN-ELSE语句非常相似&#xff0c;可根据数据选择值。 CASE语句遍历条件并在满足第一个条件时返回值。 因此&#xff0c;一旦条件成立&#xff0c;它将短路&#xff0c;从而忽略后面的子句并返回结果。 正如我…

数据库 case 用法

【转载】:数据库中case when 的用法 CASE WHEN 及 SELECT CASE WHEN的用法Case具有两种格式。简单Case函数和Case搜索函数。 1.简单Case函数 CASE sex WHEN 1 THEN 男 WHEN 2 THEN 女 ELSE 其他 END 2.Case搜索函数 CASE WHEN sex 1 THEN 男 WHEN sex 2 THEN 女 ELSE 其他 …

SQL之CASE WHEN用法详解

简单CASE WHEN函数&#xff1a; CASE SCORE WHEN A THEN 优 ELSE 不及格 END CASE SCORE WHEN B THEN 良 ELSE 不及格 END CASE SCORE WHEN C THEN 中 ELSE 不及格 END 等同于&#xff0c;使用CASE WHEN条件表达式函数实现&#xff1a; CASE WHEN SCORE A THEN 优WHEN SCORE…

switch case语法

文章目录 switch case组合不要拿青龙偃月刀去削苹果case的作用是什么&#xff1f;break的作用是什么&#xff1f;case后面的值有什么要求吗&#xff1f;case语句的排列顺序问题default语句相关问题使用case语句的一些注意事项 switch case组合 基本语法结构 switch(整型常量/…

SQL中case的使用方法

Case具有两种格式。简单Case函数和Case搜索函数。 1.简单Case函数 CASE sex WHEN 1 THEN 男WHEN 2 THEN 女 ELSE 其他 END2.Case搜索函数 CASE WHEN sex 1 THEN 男WHEN sex 2 THEN 女ELSE 其他 END3.简单case函数 VS case搜索函数 这两种方式&#xff0c;可以实现相同的功…

[转载]常用CASE工具介绍

[转载]常用CASE工具介绍转载自&#xff1a;http://www.cnblogs.com/powerlc/archive/2006/01/12/315959.html 一&#xff0c;概述 今天, 代码变得日益简单, 在Model的指导下, 思想, 设计, 分析都变得异常重要。企业业务建模工具, 产品非常多, 特别是在MDA日益流行的今天. Work…

CASE语句的使用方法

CASE语句有两种&#xff1a; 一种是case [column] when&#xff0c;指定了判断条件所在的列。 另一种是case when [column]&#xff0c;因为条件在子句中所以能对任意列进行判断。 本例建立一个员工表&#xff0c;有员工id&#xff08;id&#xff09;&#xff0c;员工姓名&am…

常用CASE工具介绍 ZZ

常用CASE工具介绍 一&#xff0c;概述  今天, 代码变得日益简单, 在Model的指导下, 思想, 设计, 分析都变得异常重要。企业业务建模工具, 产品非常多, 特别是在MDA日益流行的今天. WorkFlow是典型的业务及流程建模。 二&#xff0c;软件开发CASE工具简介   (一)图稿绘制&…

CASE 工具有哪些

CASE 工具 CASE工具设置的软件应用程序。这使用为自动的SDLC活动。 CASE工具所使用的软件项目经理,分析师和工程师开发的软件系统. 有许多CASE工具做软件开发生命周期的各个阶段,如工具,设计工具,项目管理工具,数据库管理工具,文档工具分析. 为了得到所需的结果,CASE工具…