AOSP 12 U盘插拔链路源码全解析(三):Native层 —— vold与NetlinkManager
系列目录第一篇全景图与调用链路概览 | 第二篇内核层—USB驱动与uevent |第三篇Native层—vold与NetlinkManager| 第四篇Framework层(上)—UsbHostManager | 第五篇Framework层(下)—StorageManagerService | 第六篇广播分发与SystemUI响应 | 第七篇应用层—MediaScanner与SAF | 第八篇实战调试与案例分析一、引言上一篇文章我们分析了 Linux 内核如何完成 USB 枚举、绑定 usb-storage 驱动、创建/dev/sda节点最后通过 netlink 发送 uevent。本篇聚焦voldVolume 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_contextu:r:blkid:s0 --blkid_untrusted_contextu:r:blkid_untrusted:s0 \ --blkid_trusted_contextu:r:blkid:s0 \ --fsck_contextu:r:fsck:s0 --fsck_untrusted_contextu:r:fsck_untrusted:s0 \ --fsck_trusted_contextu: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/tasks2.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 分发给 NetlinkHandlerNetlinkHandler解析 uevent 文本按 SUBSYSTEM 路由到 VolumeManagerVolumeManager创建和管理 Disk/Volume 对象响应挂载/卸载等操作CommandListener通过 socket 监听来自 Java 层StorageManagerService的命令Model (Disk/Volume)抽象物理磁盘和逻辑卷封装分区表解析、文件系统检测等三、vold 启动全流程3.1 main() 入口源码路径system/vold/main.cppintmain(intargc,char**argv){// 1. 解析命令行参数blkid/fsck 的 SELinux context// ...// 2. 创建 tmpfs非关键// ...// 3. ★ 创建 VolumeManager 单例VolumeManager*vmVolumeManager::Instance();// 4. ★ 创建 NetlinkManager 单例NetlinkManager*nmNetlinkManager::Instance();// 5. 创建 CommandListener监听来自 Java 层的 Binder 命令CommandListener*clnewCommandListener();// 6. 初始化并启动各模块nm-start();// ★ 创建 netlink socket 并开始监听// 7. 进入事件循环等待命令while(true){// 处理来自 CommandListener 的命令}}3.2 NetlinkManager::start() —— 建立 netlink 通道源码路径system/vold/NetlinkManager.cppintNetlinkManager::start(){structsockaddr_nlnladdr;intsz64*1024;// 接收缓冲区 64KBinton1;memset(nladdr,0,sizeof(nladdr));nladdr.nl_familyAF_NETLINK;nladdr.nl_pidgetpid();// 绑定当前进程 PIDnladdr.nl_groups0xffffffff;// 监听所有 multicast group// ★ 创建 netlink socketif((mSocksocket(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 并开始监听mHandlernewNetlinkHandler(mSock);if(mHandler-start()){PLOG(ERROR)Unable to start NetlinkHandler;return-1;}return0;}三个关键参数解读参数值含义socket()首个参数PF_NETLINK协议族第三个参数NETLINK_KOBJECT_UEVENTnetlink 协议类型专用于内核对象事件nl_groups0xffffffff监听所有 multicast group不遗漏任何子系统的 uevent3.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.cppAOSP 12 移至system/corevoidNetlinkListener::onDataAvailable(){charbuffer[UEVENT_MSG_LEN2];// 通常 64KBintcount;// 从 netlink socket 读取数据countTEMP_FAILURE_RETRY(recv(mSock,buffer,sizeof(buffer),MSG_DONTWAIT));if(count0)return;// ★ 解析 uevent 消息// uevent 格式多段以 \0 结尾的 keyvalue最后以空串结束NetlinkEvent*evtnewNetlinkEvent();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*sbuffer;char*endbuffersize;while(send){if(*s\0)break;// 空串表示消息结束// 解析 keyvaluechar*eqstrchr(s,);if(!eq)break;// 提取并存储参数mParams.add(String8(s,eq-s),String8(eq1));seqstrlen(eq)1;}// ★ 通过 ACTION 字段判断事件类型constchar*actionmParams.findCString(ACTION);if(!strcmp(action,add))mActionNlActionAdd;elseif(!strcmp(action,remove))mActionNlActionRemove;elseif(!strcmp(action,change))mActionNlActionChange;// ★ 提取关键路径mSubsystemmParams.findCString(SUBSYSTEM);mPathmParams.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 ACTIONadd\0 DEVPATH/devices/.../block/sda\0 SUBSYSTEMblock\0 MAJOR8\0 MINOR0\0 DEVNAMEsda\0 DEVTYPEdisk\0 SEQNUM2847\0 \0解析后NetlinkEvent包含mAction NlActionAddmSubsystem blockmParams映射了所有 keyvalue五、NetlinkHandler::onEvent() —— 路由到 VolumeManager源码路径system/vold/NetlinkHandler.cppvoidNetlinkHandler::onEvent(NetlinkEvent*evt){VolumeManager*vmVolumeManager::Instance();constchar*subsystemevt-getSubsystem();// ★ 只处理 block 子系统的 ueventif(!subsystem||strcmp(subsystem,block))return;// 忽略其他子系统net、usb 等// ★ 交给 VolumeManager 处理vm-handleBlockEvent(evt);}注意vold 只关心SUBSYSTEMblock的 uevent。USB 设备的感知SUBSYSTEMusb由 UsbHostManager 的 JNI 层单独处理。六、VolumeManager::handleBlockEvent() —— 设备生命周期管理源码路径system/vold/VolumeManager.cppvoidVolumeManager::handleBlockEvent(NetlinkEvent*evt){constchar*devpathevt-findParam(DEVPATH);constchar*devtypeevt-findParam(DEVTYPE);constchar*devnameevt-findParam(DEVNAME);intmajoratoi(evt-findParam(MAJOR));intminoratoi(evt-findParam(MINOR));switch(evt-getAction()){caseNetlinkEvent::NlActionAdd:if(!strcmp(devtype,disk)){// ★ U 盘整盘设备/dev/sdahandleDiskAdded(devpath,devname,major,minor);}elseif(!strcmp(devtype,partition)){// ★ U 盘分区设备/dev/sda1handlePartitionAdded(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::stringdevpath,conststd::stringdevname,intmajor,intminor){// 1. 生成唯一 eventId原子递增// 2. 创建 Disk 对象autodiskstd::shared_ptrDisk(newDisk(evt,devname,devpath,eventId));// 3. ★ 读取分区表disk-readPartitions();// 4. 加入 mDisks 集合mDisks[disk-getId()]disk;// 5. ★ 通知 Java 层onDiskCreated()for(autolistener:mListeners){listener-onDiskCreated(disk-getId(),disk-getFlags());}// 6. 为每个分区创建 Volume 并通知for(autovol:disk-getVolumes()){mVolumes[vol-getId()]vol;for(autolistener:mListeners){listener-onVolumeCreated(vol-getId(),vol-getType(),disk-getId(),vol-getPartGuid());}}}七、Disk 与分区表解析7.1 Disk::readPartitions()源码路径system/vold/model/Disk.cppstatus_tDisk::readPartitions(){// 构建设备路径std::string pathStringPrintf(/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 headeruint8_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 偏移 4460x1BE处// 每个分区表项 16 字节共 4 个主分区structMbrPartition{uint8_tbootIndicator;// 0x80活动, 0x00非活动uint8_tstartHead;uint8_tstartSector;uint8_tstartCylinder;uint8_tpartitionType;// ★ 0x0BFAT32, 0x07NTFS/exFAT, 0x83Linuxuint8_tendHead;uint8_tendSector;uint8_tendCylinder;uint32_tstartLba;// ★ 分区起始 LBAuint32_tsizeLba;// ★ 分区大小扇区数};for(inti0;i4;i){MbrPartition*p(MbrPartition*)(buffer446i*16);if(p-partitionType0)continue;// 空表项// ★ 为每个分区创建一个 PublicVolumeautovolstd::shared_ptrPublicVolume(newPublic