系列目录 :第一篇:全景图与调用链路概览 | 第二篇:内核层---USB驱动与uevent | 第三篇:Native层---vold与NetlinkManager | 第四篇:Framework层(上)---UsbHostManager | 第五篇:Framework层(下)---StorageManagerService | 第六篇:广播分发与SystemUI响应 | 第七篇:应用层---MediaScanner与SAF | 第八篇:实战调试与案例分析
一、引言
上一篇文章我们分析了 Linux 内核如何完成 USB 枚举、绑定 usb-storage 驱动、创建 /dev/sda 节点,最后通过 netlink 发送 uevent。
本篇聚焦 vold(Volume Daemon)------用户态第一个感知 U 盘插入的系统进程。它的任务链是:
接收 uevent → 解析设备信息 → 创建 Disk 对象
→ 读取分区表 → 创建 PublicVolume 对象
→ 通知 Java 层 → 响应挂载请求 → 执行 mount(2)
vold 是整个存储子系统的 Native 中枢,稳定性和健壮性直接影响用户是否能正常使用 U 盘。
二、vold 架构全景
2.1 进程模型
vold 是一个 单进程多模块 的 C++ 守护进程,由 init 启动:
init
# system/vold/vold.rc
service vold /system/bin/vold \
--blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
--blkid_trusted_context=u:r:blkid:s0 \
--fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0 \
--fsck_trusted_context=u:r:fsck:s0
class core
socket vold stream 0660 root mount
socket cryptd stream 0660 root mount
socket vold_unsolicited stream 0660 root mount
ioprio be 2
writepid /dev/cpuset/foreground/tasks
2.2 五大核心模块
┌─────────────────────────────────────────────────────────────┐
│ vold (Volume Daemon) │
│ │
│ ┌─────────────────┐ ┌──────────────────────────────┐ │
│ │ NetlinkManager │ │ VolumeManager │ │
│ │ │ │ │ │
│ │ - 创建 netlink │ │ - handleBlockEvent() │ │
│ │ socket │ │ - mountVolume() │ │
│ │ - 接收 uevent │───►│ - unmountVolume() │ │
│ │ - 分发给 Handler │ │ - formatVolume() │ │
│ └─────────────────┘ │ - 管理 Disk/Volume 集合 │ │
│ └──────────────┬───────────────┘ │
│ │ │
│ ┌──────────────────────┐ ┌───────────┴───────────────┐ │
│ │ CommandListener │ │ Model Objects │ │
│ │ │ │ │ │
│ │ - 监听 /dev/socket/ │ │ Disk │ │
│ │ vold socket │ │ ├── PublicVolume │ │
│ │ - 处理来自 Java 层的 │ │ ├── PrivateVolume │ │
│ │ Binder 命令 │ │ └── EmulatedVolume │ │
│ └──────────────────────┘ └────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Utils Layer │ │
│ │ - blkid / fsck / mkfs / mount / fstrim │ │
│ │ - SELinux context 切换 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
| 模块 | 功能 |
|---|---|
| NetlinkManager | 监听内核 netlink socket,将 uevent 分发给 NetlinkHandler |
| NetlinkHandler | 解析 uevent 文本,按 SUBSYSTEM 路由到 VolumeManager |
| VolumeManager | 创建和管理 Disk/Volume 对象,响应挂载/卸载等操作 |
| CommandListener | 通过 socket 监听来自 Java 层(StorageManagerService)的命令 |
| Model (Disk/Volume) | 抽象物理磁盘和逻辑卷,封装分区表解析、文件系统检测等 |
三、vold 启动全流程
3.1 main() 入口
源码路径 :system/vold/main.cpp
cpp
int main(int argc, char** argv) {
// 1. 解析命令行参数(blkid/fsck 的 SELinux context)
// ...
// 2. 创建 tmpfs(非关键)
// ...
// 3. ★ 创建 VolumeManager 单例
VolumeManager* vm = VolumeManager::Instance();
// 4. ★ 创建 NetlinkManager 单例
NetlinkManager* nm = NetlinkManager::Instance();
// 5. 创建 CommandListener(监听来自 Java 层的 Binder 命令)
CommandListener* cl = new CommandListener();
// 6. 初始化并启动各模块
nm->start(); // ★ 创建 netlink socket 并开始监听
// 7. 进入事件循环,等待命令
while (true) {
// 处理来自 CommandListener 的命令
}
}
3.2 NetlinkManager::start() ------ 建立 netlink 通道
源码路径 :system/vold/NetlinkManager.cpp
cpp
int NetlinkManager::start() {
struct sockaddr_nl nladdr;
int sz = 64 * 1024; // 接收缓冲区 64KB
int on = 1;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = getpid(); // 绑定当前进程 PID
nladdr.nl_groups = 0xffffffff; // 监听所有 multicast group
// ★ 创建 netlink socket
if ((mSock = socket(PF_NETLINK,
SOCK_DGRAM | SOCK_CLOEXEC,
NETLINK_KOBJECT_UEVENT)) < 0) {
PLOG(ERROR) << "Unable to create uevent socket";
return -1;
}
// 设置接收缓冲区大小
if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
PLOG(ERROR) << "Unable to set uevent socket SO_RCVBUFFORCE";
}
// 绑定到本进程
if (bind(mSock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
PLOG(ERROR) << "Unable to bind uevent socket";
return -1;
}
// ★ 创建 NetlinkHandler 并开始监听
mHandler = new NetlinkHandler(mSock);
if (mHandler->start()) {
PLOG(ERROR) << "Unable to start NetlinkHandler";
return -1;
}
return 0;
}
三个关键参数解读:
| 参数 | 值 | 含义 |
|---|---|---|
socket() 首个参数 |
PF_NETLINK |
协议族 |
| 第三个参数 | NETLINK_KOBJECT_UEVENT |
netlink 协议类型,专用于内核对象事件 |
nl_groups |
0xffffffff |
监听所有 multicast group,不遗漏任何子系统的 uevent |
3.3 NetlinkHandler ------ 继承自 NetlinkListener
源码路径 :system/vold/NetlinkHandler.cpp
cpp
// NetlinkListener → NetlinkHandler 的继承链
class NetlinkListener {
int mSock;
void onDataAvailable(); // 从 socket 读取原始字节
virtual void onEvent(NetlinkEvent *evt) = 0; // 纯虚函数
};
class NetlinkHandler : public NetlinkListener {
void onEvent(NetlinkEvent *evt) override; // 实现 uevent 处理
};
四、uevent 接收与解析
4.1 NetlinkListener::onDataAvailable() ------ 接收原始数据
源码路径 :system/core/libsysutils/src/NetlinkListener.cpp(AOSP 12 移至 system/core)
cpp
void NetlinkListener::onDataAvailable() {
char buffer[UEVENT_MSG_LEN + 2]; // 通常 64KB
int count;
// 从 netlink socket 读取数据
count = TEMP_FAILURE_RETRY(
recv(mSock, buffer, sizeof(buffer), MSG_DONTWAIT));
if (count < 0) return;
// ★ 解析 uevent 消息
// uevent 格式:多段以 \0 结尾的 key=value,最后以空串结束
NetlinkEvent* evt = new NetlinkEvent();
if (evt->decode(buffer, count)) {
// ★ 回调子类 onEvent()
onEvent(evt);
}
delete evt;
}
4.2 NetlinkEvent::decode() ------ 解析 uevent 文本
cpp
// system/core/libsysutils/src/NetlinkEvent.cpp
bool NetlinkEvent::decode(char* buffer, int size) {
char* s = buffer;
char* end = buffer + size;
while (s < end) {
if (*s == '\0') break; // 空串表示消息结束
// 解析 key=value
char* eq = strchr(s, '=');
if (!eq) break;
// 提取并存储参数
mParams.add(String8(s, eq - s), String8(eq + 1));
s = eq + strlen(eq) + 1;
}
// ★ 通过 ACTION 字段判断事件类型
const char* action = mParams.findCString("ACTION");
if (!strcmp(action, "add")) mAction = NlActionAdd;
else if (!strcmp(action, "remove")) mAction = NlActionRemove;
else if (!strcmp(action, "change")) mAction = NlActionChange;
// ★ 提取关键路径
mSubsystem = mParams.findCString("SUBSYSTEM");
mPath = mParams.findCString("DEVPATH");
return true;
}
4.3 实际 uevent 消息示例
内核发来的原始消息(64KB buffer 中的文本):
add@/devices/platform/soc/a600000.usb/.../host0/target0:0:0/0:0:0:0/block/sda\0
ACTION=add\0
DEVPATH=/devices/.../block/sda\0
SUBSYSTEM=block\0
MAJOR=8\0
MINOR=0\0
DEVNAME=sda\0
DEVTYPE=disk\0
SEQNUM=2847\0
\0
解析后 NetlinkEvent 包含:
mAction = NlActionAddmSubsystem = "block"mParams映射了所有 key=value
五、NetlinkHandler::onEvent() ------ 路由到 VolumeManager
源码路径 :system/vold/NetlinkHandler.cpp
cpp
void NetlinkHandler::onEvent(NetlinkEvent* evt) {
VolumeManager* vm = VolumeManager::Instance();
const char* subsystem = evt->getSubsystem();
// ★ 只处理 block 子系统的 uevent
if (!subsystem || strcmp(subsystem, "block"))
return; // 忽略其他子系统(net、usb 等)
// ★ 交给 VolumeManager 处理
vm->handleBlockEvent(evt);
}
注意 :vold 只关心
SUBSYSTEM=block的 uevent。USB 设备的感知(SUBSYSTEM=usb)由 UsbHostManager 的 JNI 层单独处理。
六、VolumeManager::handleBlockEvent() ------ 设备生命周期管理
源码路径 :system/vold/VolumeManager.cpp
cpp
void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
const char* devpath = evt->findParam("DEVPATH");
const char* devtype = evt->findParam("DEVTYPE");
const char* devname = evt->findParam("DEVNAME");
int major = atoi(evt->findParam("MAJOR"));
int minor = atoi(evt->findParam("MINOR"));
switch (evt->getAction()) {
case NetlinkEvent::NlActionAdd:
if (!strcmp(devtype, "disk")) {
// ★ U 盘整盘设备(/dev/sda)
handleDiskAdded(devpath, devname, major, minor);
} else if (!strcmp(devtype, "partition")) {
// ★ U 盘分区设备(/dev/sda1)
handlePartitionAdded(devpath, devname, major, minor);
}
break;
case NetlinkEvent::NlActionRemove:
if (!strcmp(devtype, "disk")) {
handleDiskRemoved(major, minor);
} else if (!strcmp(devtype, "partition")) {
handlePartitionRemoved(major, minor);
}
break;
case NetlinkEvent::NlActionChange:
// 介质变化(如 SD 卡写保护开关)
handleDiskChanged(major, minor);
break;
}
}
6.1 handleDiskAdded()
cpp
void VolumeManager::handleDiskAdded(const std::string& devpath,
const std::string& devname,
int major, int minor) {
// 1. 生成唯一 eventId(原子递增)
// 2. 创建 Disk 对象
auto disk = std::shared_ptr<Disk>(
new Disk(evt, devname, devpath, eventId));
// 3. ★ 读取分区表
disk->readPartitions();
// 4. 加入 mDisks 集合
mDisks[disk->getId()] = disk;
// 5. ★ 通知 Java 层:onDiskCreated()
for (auto& listener : mListeners) {
listener->onDiskCreated(disk->getId(), disk->getFlags());
}
// 6. 为每个分区创建 Volume 并通知
for (auto& vol : disk->getVolumes()) {
mVolumes[vol->getId()] = vol;
for (auto& listener : mListeners) {
listener->onVolumeCreated(
vol->getId(), vol->getType(),
disk->getId(), vol->getPartGuid());
}
}
}
七、Disk 与分区表解析
7.1 Disk::readPartitions()
源码路径 :system/vold/model/Disk.cpp
cpp
status_t Disk::readPartitions() {
// 构建设备路径
std::string path = StringPrintf("/dev/block/vold/%s", mDevName.c_str());
// 实际指向 /dev/block/sda
// ★ 打开设备,读取分区表
android::base::unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
// 读取前 4096 字节(足够覆盖 MBR + GPT header)
uint8_t buffer[4096];
read(fd, buffer, sizeof(buffer));
// ★ 判断分区表类型
if (isGpt(buffer)) {
return readGptPartitions(fd, buffer);
} else {
return readMbrPartitions(fd, buffer);
}
}
7.2 MBR 分区表解析
cpp
status_t Disk::readMbrPartitions(int fd, uint8_t* buffer) {
// MBR 分区表位于扇区 0 偏移 446(0x1BE)处
// 每个分区表项 16 字节,共 4 个主分区
struct MbrPartition {
uint8_t bootIndicator; // 0x80=活动, 0x00=非活动
uint8_t startHead;
uint8_t startSector;
uint8_t startCylinder;
uint8_t partitionType; // ★ 0x0B=FAT32, 0x07=NTFS/exFAT, 0x83=Linux
uint8_t endHead;
uint8_t endSector;
uint8_t endCylinder;
uint32_t startLba; // ★ 分区起始 LBA
uint32_t sizeLba; // ★ 分区大小(扇区数)
};
for (int i = 0; i < 4; i++) {
MbrPartition* p = (MbrPartition*)(buffer + 446 + i * 16);
if (p->partitionType == 0) continue; // 空表项
// ★ 为每个分区创建一个 PublicVolume
auto vol = std::shared_ptr<PublicVolume>(
new Public