Android 存储管理简介

126 阅读6分钟

Android存储管理涉及的三个角色包括Vold、StorageManagerService、MediaProvider.

  • Vold 主要负责接收来自驱动的外部存储设备如U盘的插拔事件,并完成挂载和卷管理功能。

  • StorageManagerService 是system server 进程的一个服务, 主要负责与Vold通信,接收来自Vold 的通知,如挂载是否成功,并发送广播通知应用。

  • MediaProvider 为App 提供访问外部存储接口及权限控制,应用访问/sdcard/Android/data /sdcard/Android/obb 目录以外的目录,都需要经过fuse文件系统,由 MediaProvider 响应这些文件访问。

  1. Vold

在我们开发过程中接触到的vold管理的卷主要有两种:虚拟卷 (EmulatedVolume)、外部存储设备卷。

  • 虚拟卷一般指的是真正访问目录为/data/media/的虚拟卷 ,它的主要作用是给应用进程提供一个虚拟的外部存储空间。

  • 外部存储设备卷就是指比如SD卡上的卷。

vold进程启动

vold进程由init进程启动,以下是启动脚本。

/system/vold/vold.rc

service vold /system/bin/vold \
        --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
        --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
    class core
    ioprio be 2
    writepid /dev/cpuset/foreground/tasks
    shutdown critical
    group root reserved_disk

vold进程启动后执行入口方法:

system/vold/main.cpp


nt main(int argc, char** argv) {

   VolumeManager* vm;
   NetlinkManager* nm;


   mkdir("/dev/block/vold", 0755);

   /* For when cryptfs checks and mounts an encrypted filesystem */
   klog_set_level(6);

   //创建VolumeManager兑现
   /* Create our singleton managers */
   if (!(vm = VolumeManager::Instance())) {
       LOG(ERROR) << "Unable to create VolumeManager";
       exit(1);
   }
   //创建NetlinkManager
   if (!(nm = NetlinkManager::Instance())) {
       LOG(ERROR) << "Unable to create NetlinkManager";
       exit(1);
   }

   if (vm->start()) {
       PLOG(ERROR) << "Unable to start VolumeManager";
        exit(1);
    }

    //VoldNativeService是binder系统服务,名字为"vold"
    ATRACE_BEGIN("VoldNativeService::start");
    if (android::vold::VoldNativeService::start() != android::OK) {
        LOG(ERROR) << "Unable to start VoldNativeService";
        exit(1);
    }
    ATRACE_END();

    LOG(DEBUG) << "VoldNativeService::start() completed OK";
    //开始监听内核发来的uevent
    ATRACE_BEGIN("NetlinkManager::start");
    if (nm->start()) {
        PLOG(ERROR) << "Unable to start NetlinkManager";
        exit(1);
    }
    ... ...

    android::IPCThreadState::self()->joinThreadPool();
    LOG(INFO) << "vold shutting down";

    exit(0);
}

以上main方法中涉及的主要类:

  • VolumeManager

管理虚拟卷mInternalEmulatedVolumes,以及外部存储列表mDisks。

  • NetlinkManager

与kernel 通信,接收kernel 发来的uevent事件,如外部存储设备插拔。

  • VoldNativeService

binder服务,负责给StorageManagerService 提供远程调用接口,如mount() 等。

挂载路径对应

  • 外置存储(虚拟卷):

底层目录:/data/media/userId

上层目录:storage/emulated/userId

挂载目录: /mnt/user/userId/emulated/userId

  • 存储设备(U盘)

底层目录: /dev/block/vold

上层目录:storage/XXXX

挂载目录:/mnt/media_rw/XXXX

挂载流程

  • 虚拟卷创建及挂载
  1. 虚拟卷创建

在用户创建时会创建data/media/userId 虚拟卷,创建流程:

StorageManagerService -> VoldNativeService::onUserStarted->VolumeManager::onUserStarted

void VolumeManager::createEmulatedVolumesForUser(userid_t userId) {
    // Create unstacked EmulatedVolumes for the user
    //以data/media/userId为raw path 创建虚拟卷
    auto vol = std::shared_ptr<android::vold::VolumeBase>(
            new android::vold::EmulatedVolume("/data/media", userId));
    vol->setMountUserId(userId);
    mInternalEmulatedVolumes.push_back(vol);
    vol->create();
... ...
}

status_t VolumeBase::create() {
    CHECK(!mCreated);

    mCreated = true;
    status_t res = doCreate();

    auto listener = getListener();
    //通知StorageManagerService 已经创建
    if (listener) {
        listener->onVolumeCreated(getId(), static_cast<int32_t>(mType), mDiskId, mPartGuid,
                                  mMountUserId);
    }
    //设置状态为未挂载
    setState(State::kUnmounted);
    return res;
}

2. 虚拟卷挂载

binder::Status VoldNativeService::mount(
        const std::string& volId, int32_t mountFlags, int32_t mountUserId,
        const android::sp<android::os::IVoldMountCallback>& callback) {
    ENFORCE_SYSTEM_OR_ROOT;
    CHECK_ARGUMENT_ID(volId);
    ACQUIRE_LOCK;
    //查找对应卷
    auto vol = VolumeManager::Instance()->findVolume(volId);
    if (vol == nullptr) {
        return error("Failed to find volume " + volId);
    }

    vol->setMountFlags(mountFlags);
    vol->setMountUserId(mountUserId);

    vol->setMountCallback(callback);
    //开始挂载
    int res = vol->mount();
    vol->setMountCallback(nullptr);
... ...
}

/system/vold/model/VolumeBase.cpp

status_t VolumeBase::mount() {
    if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
        LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
        return -EBUSY;
    }

    setState(State::kChecking);
    status_t res = doMount();
    setState(res == OK ? State::kMounted : State::kUnmountable);

    if (res == OK) {
        doPostMount();
    }
    return res;
}

/system/vold/model/EmulatedVolume.cpp


status_t EmulatedVolume::doMount() {
  
    if (isFuse && isVisible) {
        LOG(INFO) << "Mounting emulated fuse volume";
        android::base::unique_fd fd;
        int user_id = getMountUserId();
        auto volumeRoot = getRootPath();

        // Make sure Android/ dirs exist for bind mounting
        status_t res = PrepareAndroidDirs(volumeRoot);
        if (res != OK) {
            LOG(ERROR) << "Failed to prepare Android/ directories";
            return res;
        }
       //挂载fuse
        res = MountUserFuse(user_id, getInternalPath(), label, &fd);
        if (res != 0) {
            PLOG(ERROR) << "Failed to mount emulated fuse volume";
            return res;
        }
        // 定义FUSE卸载的lambda函数:出错时清理FUSE挂载
        mFuseMounted = true;
        auto fuse_unmounter = [&]() {
            LOG(INFO) << "fuse_unmounter scope_guard running";
            fd.reset();
            if (UnmountUserFuse(user_id, getInternalPath(), label) != OK) {
                PLOG(INFO) << "UnmountUserFuse failed on emulated fuse volume";
            }
            mFuseMounted = false;
        };
        ... ...
    return OK;
}

MountUserFuse()方法注释比较多,这里就不贴代码了, 主要的工作包括:

  1. 创建fuse挂载目录 /mnt/user/userid,创建直通挂载目录/mnt/pass_through/userid
  2. 如果传参relative_upper_path 是 "emulated",创建符号链接,作用是应用通过统一的 /storage/emulated/userid 访问用户存储,而实际指向 FUSE 或pass_through路径

/mnt/user/userid/self/primary → /storage/emulated/userid

/mnt/pass_through/userid/self/primary → /storage/emulated/userid

  1. 打开驱动fuse 设备

    1.    fuse_fd->reset(open("/dev/fuse", O_RDWR | O_CLOEXEC));
      
  2. 通过 mount 系统将/dev/fuse 挂载到 /mnt/user/userid

  • U盘挂载

插入U盘,vold接收到kernel 发来的uevent后,调用handleDiskAdded,

 void VolumeManager::handleDiskAdded(const std::shared_ptr<android::vold::Disk>& disk) {
     ... ...
     } else {
         disk->create();
         mDisks.push_back(disk);
     }
 }

/system/vold/model/Disk.cpp

status_t Disk::create() {
   //检查是否已经创建了
    CHECK(!mCreated);
    mCreated = true;
    //通知StorageManagerService Disk创建
    auto listener = VolumeManager::Instance()->getListener();
    if (listener) listener->onDiskCreated(getId(), mFlags);

    if (isStub()) {
        createStubVolume();
        return OK;
    }
    readMetadata();
    readPartitions();
    return OK;
}

readPartitions()中调用createPublicVolume()创建 PublicVolume

 void Disk::createPublicVolume(dev_t device) {
     auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
     if (mJustPartitioned) {
         LOG(DEBUG) << "Device just partitioned; silently formatting";
         vol->setSilent(true);
         vol->create();
         vol->format("auto");
         vol->destroy();
         vol->setSilent(false);
     }
 
     mVolumes.push_back(vol);
     vol->setDiskId(getId());
     //这里调用IVoldListener.onVolumeCreated 通知StorageManagerService卷创建
     vol->create();
 }

StorageManagerService收到 onVolumeCreated 后开始触发vold挂载流程,最后主要挂载流程在PublicVolume.doMount中。


 status_t PublicVolume::doMount() {
    ... ...
      //设置内核直接挂载的原始路径:/mnt/media_rw/stableName
      setInternalPath(mRawPath);
      if (isVisible) {
          //设置应用访问路径,应用通过 Environment.getExternalStorageDirectory() 获取
          setPath(StringPrintf("/storage/%s", stableName.c_str()));
      } else {
          setPath(mRawPath);
      }
     ... ...
      
      bool isFuse = base::GetBoolProperty(kPropFuse, false);
      if (isFuse) {
          // We need to mount FUSE *after* sdcardfs, since the FUSE daemon may depend
          // on sdcardfs being up.
          LOG(INFO) << "Mounting public fuse volume";
          android::base::unique_fd fd;
          int user_id = getMountUserId();
           //挂载fuse
          int result = MountUserFuse(user_id, getInternalPath(), stableName, &fd);
  
          if (result != 0) {
              LOG(ERROR) << "Failed to mount public fuse volume";
              doUnmount();
              return -result;
          }
  
          mFuseMounted = true;
          auto callback = getMountCallback();
          if (callback) {
              bool is_ready = false;
              //回调StorageManagerService.onVolumeChecking
              callback->onVolumeChecking(std::move(fd), getPath(), getInternalPath(), &is_ready);
              if (!is_ready) {
                  LOG(ERROR) << "Failed to complete public volume mount";
                  doUnmount();
                  return -EIO;
              }
          }
      ... ...
      }
  
      return OK;
  }
  1. 首先设置内核直接挂载的原始路径:/mnt/media_rw/stableName和应用访问路径/storage/stableName
  2. MountUserFuse 作用是将fuse 挂载点/storage/XXX关联底层原始挂载点/mnt/media_rw/XXX
  3. fuse挂载后回调StorageManagerService.onVolumeChecking通知上层,如果上层不抛异常,则挂载完成
  1. StorageManagerService

StorageManagerService是system server中的一个系统服务,主要负责:

  1. 与vold 通信,通知vold 用户创建、挂载等,同时接收vold 发来的信息如新增disk、挂载是否成功等
  2. 与MediaProvider 通信,告知volume 挂载状态,然后MediaProvider开始创建Fuse Deamon
  3. APP通过StorageManagerService可以获得可用的卷,StorageManagerService通过发送广播(如Intent.ACTION_MEDIA_MOUNTED)通知应用存储设备挂载状态。

20250910-101603.jpg

  1. MediaProvider

Fuse机制中的 Fuse Deamon 所在进程就是 MediaProvider, Fuse Deamon会拦截应用对/storage/emualated/userid文件操作,进行权限校验,然后往data/media 路径读写。

Fuse 原理简介

  1. 没有使用Fuse 机制文件写入流程

20250910-101350.jpg

  1. 经过fuse 拦截后的文件写入流程

应用发起写入/storage/emualated/userid → 内核拦截(dev/fuse 挂载点)→ FUSE Daemon 接收请求 → 调用MediaProvider校验权限 → 校验成功 → 写入data/media/userid 。

挂载后 onVolumeChecking 流程

MediaProvider 权限校验流程

Fuse deamon 收到fuse 驱动发来的文件操作请求后,会调用MediaProviderWrapper::IsOpenAllowed()判断是否有权限,MediaProvider 权限校验流程如下:

aosp/vendor/ecarx/external/exfat/libexfat

  1. 参考文章

Android vold (卷管理) 传记

Android存储系统成长记

FUSE 透传