Android存储系统源码走读(二):vold

Android存储系统源码走读(一):StorageManagerService

前言

上文走读了StorageManagerService启动部分的主要逻辑,本篇将从对vold部分代码进行分析。
本文涉及代码:

  • system/vold/Android.bp
  • system/vold/vold.rc
  • system/vold/main.cpp
  • system/vold/VoldNativeService.cpp
  • system/vold/NetlinkManager.cpp
  • system/vold/NetlinkHandler.cpp
  • system/vold/VolumeManager.cpp
  • system/vold/fs/Ext4.cpp
  • system/vold/model/PublicVolume.cpp

存储服务的代码框架

在此之前,我们先看下目前版本(android11)存储服务的代码框架。
对比AndroidO之前版本主要优化了StorageManagerService与vold通信的方式,移除了CommandListener改为了binder通信。旧版本的代码框架可以参考此文:http://gityuan.com/2016/07/23/android-io-arch/

image.png

Android没有使用Linux平台下的udev来处理磁盘,于是Google写了一个类似udev功能的vold,充当了kernel与framework之间的桥梁;

vold的启动

Android.bp将vold.rc打包进init.rc

    init_rc: [
        "vold.rc",
        "wait_for_keymaster.rc",
    ],

vold.rc启动/system/bin/vold

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目录中,vold(main.cpp)将会启动VoldNativeService以及创建VoldManager和NetlinkManager

  • VolumeManager是VoldNativeService功能的执行者
  • NetLinkManager用来监听内核的热插拔事件,通知到vold进程USB设备已经接入了
int main(int argc, char** argv) {
...
    VolumeManager* vm;
    NetlinkManager* nm;
    
    // 初始化VolumeManager,VoldNativeService功能的执行者
    if (!(vm = VolumeManager::Instance())) {
        LOG(ERROR) << "Unable to create VolumeManager";
        exit(1);
    }

    // 初始化NetLinkManager用来监听内核的热插拔事件,通知到vold进程USB设备已经接入了
    if (!(nm = NetlinkManager::Instance())) {
        LOG(ERROR) << "Unable to create NetlinkManager";
        exit(1);
    }
    
    if (vm->start()) {
        PLOG(ERROR) << "Unable to start VolumeManager";
        exit(1);
    }
    
    /**********************************************************************************
    **process_config函数用来解析/etc/vold.fstab的配置文件,从代码可以看出,配置文件的参数以空格和
    **制表格(Tab键)分隔;系统启动起来,分析该配置文件,挂载相应的分区,相当于Linux系统的/etc/fstab文件
    **********************************************************************************/
    bool has_adoptable;
    bool has_quota;
    bool has_reserved;
    if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {
        PLOG(ERROR) << "Error reading configuration... continuing anyways";
    }
    
    ATRACE_BEGIN("VoldNativeService::start");
    if (android::vold::VoldNativeService::start() != android::OK) {
        LOG(ERROR) << "Unable to start VoldNativeService";
        exit(1);
    }
    ATRACE_END();

    // 启动VoldNativeService
    ATRACE_BEGIN("NetlinkManager::start");
    if (nm->start()) {
        PLOG(ERROR) << "Unable to start NetlinkManager";
        exit(1);
    }
    ATRACE_END();
    
    // 应用层往/sys/block目录下的uevent文件写"add\n"指令,触发kernel向上发送Uevent消息,获取设备的当前信息
    coldboot("/sys/block");
...
}

StorageManager与vold的通信

前文的StorageManagerService在connect时获取的就是VoldNativeService的binder proxy
StorageManager与vold建立通信是通过IVoldListener

            // 获取Vold的bp端用于通信
            mVold = IVold.Stub.asInterface(binder);
            try {
                // 关键代码:设置Vold的Listener
                mVold.setListener(mListener);
            } catch (RemoteException e) {
                mVold = null;
                Slog.w(TAG, "vold listener rejected; trying again", e);
            }

在VoldNativeService.cpp是通过setListener函数实现

binder::Status VoldNativeService::setListener(
        const android::sp& listener) {
    ENFORCE_SYSTEM_OR_ROOT;
    ACQUIRE_LOCK;

    VolumeManager::Instance()->setListener(listener);
    return Ok();
}

到此IVoldListener这个Binder回调就已经建立好了,VoldNativeService就可以像一般回调一样调用VoldListener来通知java层。

vold与内核的通信

vold通过NetLinkManager来建立socket通道,监听内核上报的uevent事件。

NetLink是Linux下用户进程和kernel进行信息交互的一种机制,借助这种机制,用户进程(如Vold/Netd)可以接收来自kernel的一些消息,同时也可以向kernel发送一些控制命令。NetlinkManager就是基于此设计的。Uevent也跟Linux系统有关,它与Linux 的设备文件系统有一定关系;这里,我们可以简单的认为,Uevent就是一个字符串,它描述了外部存储设备插入/拔出、挂载/卸载的状态信息。Vold通过Netlink机制,可以得到这些信息,并进行外部存储设备的管理、控制。

int NetlinkManager::start() {
    struct sockaddr_nl nladdr;
    int sz = 64 * 1024;
    int on = 1;

    memset(&nladdr, 0, sizeof(nladdr));
    nladdr.nl_family = AF_NETLINK;
    nladdr.nl_pid = getpid();
    nladdr.nl_groups = 0xffffffff;

    // 关键代码: 创建地址族为PF_NETLINK的socket,与Kernel进行通信
    if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) < 0) {
        PLOG(ERROR) << "Unable to create uevent socket";
        return -1;
    }

    // When running in a net/user namespace, SO_RCVBUFFORCE will fail because
    // it will check for the CAP_NET_ADMIN capability in the root namespace.
    // Try using SO_RCVBUF if that fails.
    if ((setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) &&
        (setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) < 0)) {
        PLOG(ERROR) << "Unable to set uevent socket SO_RCVBUF/SO_RCVBUFFORCE option";
        goto out;
    }

    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
        PLOG(ERROR) << "Unable to set uevent socket SO_PASSCRED option";
        goto out;
    }

    if (bind(mSock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) {
        PLOG(ERROR) << "Unable to bind uevent socket";
        goto out;
    }

    // 通过NetlinkerHandler处理socket的回调
    mHandler = new NetlinkHandler(mSock);
    if (mHandler->start()) {
        PLOG(ERROR) << "Unable to start NetlinkHandler";
        goto out;
    }

    return 0;

out:
    close(mSock);
    return -1;
}

通过NetlinkerHandler这个类处理socket的回调

void NetlinkHandler::onEvent(NetlinkEvent* evt) {
    VolumeManager* vm = VolumeManager::Instance();
    const char* subsys = evt->getSubsystem();

    if (!subsys) {
        LOG(WARNING) << "No subsystem found in netlink event";
        return;
    }

    if (std::string(subsys) == "block") {
        vm->handleBlockEvent(evt);
    }
}

由VolumeManager处理block相关的event.

驱动设备分为字符设备、块设备、网络设备。对于字符设备按照字符流的方式被有序访问,字符设备也称为裸设备,可以直接读取物理磁盘,不经过系统缓存,例如键盘直接产生中断。而块设备是指系统中能够随机(不需要按顺序)访问固定大小数据片(chunks)的设备,例如硬盘;块设备则是通过系统缓存进行读取。

内核上报的事件由字符串构成,示例如下:

ACTION=add
DEVPATH=/devices/platform/msm_sdcc.2/mmc_host/mmc1/mmc1:c9f2/block/mmcblk0
SUBSYSTEM=block
MAJOR=179
MINOR=0
DEVNAME=mmcblk0
DEVTYPE=disk
NPARTS=3
SEQNUM=1357
void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
    std::lock_guard lock(mLock);

    if (mDebug) {
        LOG(DEBUG) << "----------------";
        LOG(DEBUG) << "handleBlockEvent with action " << (int)evt->getAction();
        evt->dump();
    }

    // 从NetlinkEvent中读取几个参数,包括DEVPATH、DEVTYPE、MAJOR、MINOR
    std::string eventPath(evt->findParam("DEVPATH") ? evt->findParam("DEVPATH") : "");// 设备路径
    std::string devType(evt->findParam("DEVTYPE") ? evt->findParam("DEVTYPE") : "");// 设备类型
    
    

    if (devType != "disk") return;
    // 主次设备号,两者可以描述一个具体设备
    int major = std::stoi(evt->findParam("MAJOR"));
    int minor = std::stoi(evt->findParam("MINOR"));
    // 根据主次设备号创建设备
    dev_t device = makedev(major, minor);

    switch (evt->getAction()) {
        // 设备插入事件
        case NetlinkEvent::Action::kAdd: {
            for (const auto& source : mDiskSources) {
                if (source->matches(eventPath)) {
                    // 识别是U盘还是SD卡
                    // For now, assume that MMC and virtio-blk (the latter is
                    // specific to virtual platforms; see Utils.cpp for details)
                    // devices are SD, and that everything else is USB
                    int flags = source->getFlags();
                    if (major == kMajorBlockMmc || IsVirtioBlkDevice(major)) {
                        flags |= android::vold::Disk::Flags::kSd;
                    } else {
                        flags |= android::vold::Disk::Flags::kUsb;
                    }

                    auto disk =
                        new android::vold::Disk(eventPath, device, source->getNickname(), flags);
                    // 识别完成后调用handleDiskAdded完成挂载
                    handleDiskAdded(std::shared_ptr(disk));
                    break;
                }
            }
            break;
        }
        case NetlinkEvent::Action::kChange: {
            LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";
            handleDiskChanged(device);
            break;
        }
        // 设备移除事件
        case NetlinkEvent::Action::kRemove: {
            handleDiskRemoved(device);
            break;
        }
        default: {
            LOG(WARNING) << "Unexpected block event action " << (int)evt->getAction();
            break;
        }
    }
}

识别完是U盘还是sd卡后调用handleDiskAdded进行挂载
有两个场景需要暂时等待:

  1. 锁屏未解锁,需要等待用户解锁
  2. 需要等待user0启动,因为我们需要在mount Fuse daemon处理磁盘前启用用户。
void VolumeManager::handleDiskAdded(const std::shared_ptr& disk) {
    // 1、锁屏未解锁,需要等待用户解锁 
       2、需要等待user0启动,因为我们需要在mount Fuse daemon处理磁盘前启用用户。
    // For security reasons, if secure keyguard is showing, wait‵‵‵
    // until the user unlocks the device to actually touch it
    // Additionally, wait until user 0 is actually started, since we need
    // the user to be up before we can mount a FUSE daemon to handle the disk.
    bool userZeroStarted = mStartedUsers.find(0) != mStartedUsers.end();
    if (mSecureKeyguardShowing) {
        LOG(INFO) << "Found disk at " << disk->getEventPath()
                  << " but delaying scan due to secure keyguard";
        mPendingDisks.push_back(disk);
    } else if (!userZeroStarted) {
        LOG(INFO) << "Found disk at " << disk->getEventPath()
                  << " but delaying scan due to user zero not having started";
        mPendingDisks.push_back(disk);
    } else {
        disk->create();
        mDisks.push_back(disk);
    }
}

Disk::create()

status_t Disk::create() {
    CHECK(!mCreated);
    mCreated = true;
    // 回调VolumeManager的onDiskCreated 通知磁盘已经创建
    auto listener = VolumeManager::Instance()->getListener();
    if (listener) listener->onDiskCreated(getId(), mFlags);

    if (isStub()) {
        createStubVolume();
        return OK;
    }
    // 读取磁盘的元数据
    readMetadata();
    // 读取磁盘的分区信息
    readPartitions();
    return OK;
}

Disk::readPartitions()

这里就是调用/system/bin/sgdisk工具读取分区信息

sgdisk是Linux下操作GPT分区的工具,就像fdisk是操作MBR分区的工具。
disk对象创建完开始创建volume对象,sd卡USb设备是publicVolume,内置存储是privateVolume


static const char* kSgdiskPath = "/system/bin/sgdisk";

status_t Disk::readPartitions() {
    int maxMinors = getMaxMinors();
    if (maxMinors < 0) {
        return -ENOTSUP;
    }

    destroyAllVolumes();

    // Parse partition table

    std::vector cmd;
    cmd.push_back(kSgdiskPath);
    cmd.push_back("--android-dump");
    cmd.push_back(mDevPath);

    std::vector output;
    // 这里就是调用/system/bin/sgdisk工具读取分区信息
    status_t res = ForkExecvp(cmd, &output);
    if (res != OK) {
        LOG(WARNING) << "sgdisk failed to scan " << mDevPath;

        auto listener = VolumeManager::Instance()->getListener();
        if (listener) listener->onDiskScanned(getId());

        mJustPartitioned = false;
        return res;
    }

    Table table = Table::kUnknown;
    bool foundParts = false;
    for (const auto& line : output) {
        auto split = android::base::Split(line, kSgdiskToken);
        auto it = split.begin();
        if (it == split.end()) continue;

        if (*it == "DISK") {
            if (++it == split.end()) continue;
            if (*it == "mbr") {‵‵‵
                table = Table::kMbr;
            } else if (*it == "gpt") {
                table = Table::kGpt;
            } else {
                LOG(WARNING) << "Invalid partition table " << *it;
                continue;
            }
        } else if (*it == "PART") {
            foundParts = true;

            if (++it == split.end()) continue;
            int i = 0;
            if (!android::base::ParseInt(*it, &i, 1, maxMinors)) {
                LOG(WARNING) << "Invalid partition number " << *it;
                continue;
            }
            dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);

            if (table == Table::kMbr) {
                if (++it == split.end()) continue;
                int type = 0;
                if (!android::base::ParseInt("0x" + *it, &type)) {
                    LOG(WARNING) << "Invalid partition type " << *it;
                    continue;
                }

                switch (type) {
                    case 0x06:  // FAT16
                    case 0x07:  // HPFS/NTFS/exFAT
                    case 0x0b:  // W95 FAT32 (LBA)
                    case 0x0c:  // W95 FAT32 (LBA)
                    case 0x0e:  // W95 FAT16 (LBA)
                    case 0x83:  // Linux EXT4/F2FS/...
                        createPublicVolume(partDevice);
                        break;
                }
            } else if (table == Table::kGpt) {
                if (++it == split.end()) continue;
                auto typeGuid = *it;
                if (++it == split.end()) continue;
                auto partGuid = *it;

                if (android::base::EqualsIgnoreCase(typeGuid, kGptBasicData)) {
                    createPublicVolume(partDevice);
                } else if (android::base::EqualsIgnoreCase(typeGuid, kGptAndroidExpand)) {
                    createPrivateVolume(partDevice, partGuid);
                }
            }
        }
    }

    // Ugly last ditch effort, treat entire disk as partition
    if (table == Table::kUnknown || !foundParts) {
        LOG(WARNING) << mId << " has unknown partition table; trying entire device";

        std::string fsType;
        std::string unused;
        if (ReadMetadataUntrusted(mDevPath, &fsType, &unused, &unused) == OK) {
            createPublicVolume(mDevice);
        } else {
            LOG(WARNING) << mId << " failed to identify, giving up";
        }
    }

    auto listener = VolumeManager::Instance()->getListener();
    // 通知上层设备开始扫描
    if (listener) listener->onDiskScanned(getId());

    mJustPartitioned = false;
    return OK;
}

Disk::createPublicVolume

void Disk::createPublicVolume(dev_t device) {
    auto vol = std::shared_ptr(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());
    vol->create();
}

在StorageManagerService接收到哦onVolumeCreated回调后,会调用VoldNativeService::mount进行真正的挂载。挂载前会调用不同文件系统对应的fsck工具进行磁盘校验。

你可能感兴趣的:(Android存储系统源码走读(二):vold)