AOSP 12 U盘插拔链路源码全解析(三):Native层 —— vold与NetlinkManager
2026/6/12 7:26:53 网站建设 项目流程

系列目录:第一篇:全景图与调用链路概览 | 第二篇:内核层—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 启动:

# 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

intmain(intargc,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=newCommandListener();// 6. 初始化并启动各模块nm->start();// ★ 创建 netlink socket 并开始监听// 7. 进入事件循环,等待命令while(true){// 处理来自 CommandListener 的命令}}

3.2 NetlinkManager::start() —— 建立 netlink 通道

源码路径system/vold/NetlinkManager.cpp

intNetlinkManager::start(){structsockaddr_nlnladdr;intsz=64*1024;// 接收缓冲区 64KBinton=1;memset(&nladdr,0,sizeof(nladdr));nladdr.nl_family=AF_NETLINK;nladdr.nl_pid=getpid();// 绑定当前进程 PIDnladdr.nl_groups=0xffffffff;// 监听所有 multicast group// ★ 创建 netlink socketif((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,(structsockaddr*)&nladdr,sizeof(nladdr))<0){PLOG(ERROR)<<"Unable to bind uevent socket";return-1;}// ★ 创建 NetlinkHandler 并开始监听mHandler=newNetlinkHandler(mSock);if(mHandler->start()){PLOG(ERROR)<<"Unable to start NetlinkHandler";return-1;}return0;}

三个关键参数解读:

参数含义
socket()首个参数PF_NETLINK协议族
第三个参数NETLINK_KOBJECT_UEVENTnetlink 协议类型,专用于内核对象事件
nl_groups0xffffffff监听所有 multicast group,不遗漏任何子系统的 uevent

3.3 NetlinkHandler —— 继承自 NetlinkListener

源码路径system/vold/NetlinkHandler.cpp

// NetlinkListener → NetlinkHandler 的继承链classNetlinkListener{intmSock;voidonDataAvailable();// 从 socket 读取原始字节virtualvoidonEvent(NetlinkEvent*evt)=0;// 纯虚函数};classNetlinkHandler:publicNetlinkListener{voidonEvent(NetlinkEvent*evt)override;// 实现 uevent 处理};

四、uevent 接收与解析

4.1 NetlinkListener::onDataAvailable() —— 接收原始数据

源码路径system/core/libsysutils/src/NetlinkListener.cpp(AOSP 12 移至system/core

voidNetlinkListener::onDataAvailable(){charbuffer[UEVENT_MSG_LEN+2];// 通常 64KBintcount;// 从 netlink socket 读取数据count=TEMP_FAILURE_RETRY(recv(mSock,buffer,sizeof(buffer),MSG_DONTWAIT));if(count<0)return;// ★ 解析 uevent 消息// uevent 格式:多段以 \0 结尾的 key=value,最后以空串结束NetlinkEvent*evt=newNetlinkEvent();if(evt->decode(buffer,count)){// ★ 回调子类 onEvent()onEvent(evt);}deleteevt;}

4.2 NetlinkEvent::decode() —— 解析 uevent 文本

// system/core/libsysutils/src/NetlinkEvent.cppboolNetlinkEvent::decode(char*buffer,intsize){char*s=buffer;char*end=buffer+size;while(s<end){if(*s=='\0')break;// 空串表示消息结束// 解析 key=valuechar*eq=strchr(s,'=');if(!eq)break;// 提取并存储参数mParams.add(String8(s,eq-s),String8(eq+1));s=eq+strlen(eq)+1;}// ★ 通过 ACTION 字段判断事件类型constchar*action=mParams.findCString("ACTION");if(!strcmp(action,"add"))mAction=NlActionAdd;elseif(!strcmp(action,"remove"))mAction=NlActionRemove;elseif(!strcmp(action,"change"))mAction=NlActionChange;// ★ 提取关键路径mSubsystem=mParams.findCString("SUBSYSTEM");mPath=mParams.findCString("DEVPATH");returntrue;}

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 = NlActionAdd
  • mSubsystem = "block"
  • mParams映射了所有 key=value

五、NetlinkHandler::onEvent() —— 路由到 VolumeManager

源码路径system/vold/NetlinkHandler.cpp

voidNetlinkHandler::onEvent(NetlinkEvent*evt){VolumeManager*vm=VolumeManager::Instance();constchar*subsystem=evt->getSubsystem();// ★ 只处理 block 子系统的 ueventif(!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

voidVolumeManager::handleBlockEvent(NetlinkEvent*evt){constchar*devpath=evt->findParam("DEVPATH");constchar*devtype=evt->findParam("DEVTYPE");constchar*devname=evt->findParam("DEVNAME");intmajor=atoi(evt->findParam("MAJOR"));intminor=atoi(evt->findParam("MINOR"));switch(evt->getAction()){caseNetlinkEvent::NlActionAdd:if(!strcmp(devtype,"disk")){// ★ U 盘整盘设备(/dev/sda)handleDiskAdded(devpath,devname,major,minor);}elseif(!strcmp(devtype,"partition")){// ★ U 盘分区设备(/dev/sda1)handlePartitionAdded(devpath,devname,major,minor);}break;caseNetlinkEvent::NlActionRemove:if(!strcmp(devtype,"disk")){handleDiskRemoved(major,minor);}elseif(!strcmp(devtype,"partition")){handlePartitionRemoved(major,minor);}break;caseNetlinkEvent::NlActionChange:// 介质变化(如 SD 卡写保护开关)handleDiskChanged(major,minor);break;}}

6.1 handleDiskAdded()

voidVolumeManager::handleDiskAdded(conststd::string&devpath,conststd::string&devname,intmajor,intminor){// 1. 生成唯一 eventId(原子递增)// 2. 创建 Disk 对象autodisk=std::shared_ptr<Disk>(newDisk(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

status_tDisk::readPartitions(){// 构建设备路径std::string path=StringPrintf("/dev/block/vold/%s",mDevName.c_str());// 实际指向 /dev/block/sda// ★ 打开设备,读取分区表android::base::unique_fdfd(open(path.c_str(),O_RDONLY|O_CLOEXEC));// 读取前 4096 字节(足够覆盖 MBR + GPT header)uint8_tbuffer[4096];read(fd,buffer,sizeof(buffer));// ★ 判断分区表类型if(isGpt(buffer)){returnreadGptPartitions(fd,buffer);}else{returnreadMbrPartitions(fd,buffer);}}

7.2 MBR 分区表解析

status_tDisk::readMbrPartitions(intfd,uint8_t*buffer){// MBR 分区表位于扇区 0 偏移 446(0x1BE)处// 每个分区表项 16 字节,共 4 个主分区structMbrPartition{uint8_tbootIndicator;// 0x80=活动, 0x00=非活动uint8_tstartHead;uint8_tstartSector;uint8_tstartCylinder;uint8_tpartitionType;// ★ 0x0B=FAT32, 0x07=NTFS/exFAT, 0x83=Linuxuint8_tendHead;uint8_tendSector;uint8_tendCylinder;uint32_tstartLba;// ★ 分区起始 LBAuint32_tsizeLba;// ★ 分区大小(扇区数)};for(inti=0;i<4;i++){MbrPartition*p=(MbrPartition*)(buffer+446+i*16);if(p->partitionType==0)continue;// 空表项// ★ 为每个分区创建一个 PublicVolumeautovol=std::shared_ptr<PublicVolume>(newPublic

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询