前言

之前介绍了U盘的挂载,那当然不能少了U盘的卸载。这里大概的记录一下,方便自己查阅。

Android P

这里很多都是网上的,我就走走流程。

正文

VolumeManager.cpp

handleBlockEvent()
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
    //申请一个lock
    std::lock_guard<std::mutex> lock(mLock);
    //在事件属性参数中查找设备的路径;
    std::string eventPath(evt->findParam("DEVPATH")?evt->findParam("DEVPATH"):"");
    //在事件属性参数中查找设备的类型;
    std::string devType(evt->findParam("DEVTYPE")?evt->findParam("DEVTYPE"):"");
    //判断是否是“disk”类型,磁盘设备,否的话,直接返回不做任何处理
    if (devType != "disk") return;
    //上报的数据中获取主设备号;
    int major = std::stoi(evt->findParam("MAJOR"));
    //上报的数据中获取次设备号;
    int minor = std::stoi(evt->findParam("MINOR"));
    //makedev()作用是将主次设备号联合得到具体可访问的设备对象索引;
    dev_t device = makedev(major, minor);
    //判断事件的动作类型
    switch (evt->getAction()) {
    case NetlinkEvent::Action::kAdd: {
        //循环判断mDiskSources,这个mDiskSources 在vold的main()函数时就已经配置好的。所以mDiskSources是固定的一些数据。 
        //在main()函数中调用了process_config()函数对VolumeManager::DiskSource进行赋值。具体是检索配置fstab文件,进行add操作。
        for (const auto& source : mDiskSources) {
            //从mDiskSources表获取的一个成员DiskSource类型的source,
            //调用该成员的matches函数对 第11行获取的设备路径eventPath,进行匹配字符串比较。
            if (source->matches(eventPath)) {
                // For now, assume that MMC and virtio-blk (the latter is
                // emulator-specific; see Disk.cpp for details) devices are SD,
                // and that everything else is USB
                int flags = source->getFlags();
                //判断主设备号是否是mmc的设备;
                if (major == kMajorBlockMmc
                    //判断是否是模拟器运行的;
                    || (android::vold::IsRunningInEmulator()
                    //判断主设备号必须在 block设备范围内
                    && major >= (int) kMajorBlockExperimentalMin
                    && major <= (int) kMajorBlockExperimentalMax)) {
                    //将该磁盘Disk的flag标志为kSd
                    flags |= android::vold::Disk::Flags::kSd;
                } else {
                    //将该磁盘Disk的flag标志为kUsb
                    flags |= android::vold::Disk::Flags::kUsb;
                }
                auto disk = new android::vold::Disk(eventPath, device,
                        source->getNickname(), flags);
                handleDiskAdded(std::shared_ptr<android::vold::Disk>(disk));
                break;
            }
        }
        break;
    }
    case NetlinkEvent::Action::kChange: {
        handleDiskChanged(device);
        break;
    }
    case NetlinkEvent::Action::kRemove: {
        handleDiskRemoved(device);
        break;
    }
    default: {
        break;
    }
    }
}

这里是event事件处理中心,主要处理disk类型事件。

以上注册都是摘抄,部分参考文章忘记附上了。

上面分别有三种状态

  1. kAdd 挂载

  2. kChange

  3. kRemove 卸载

我们这里分析U盘的卸载,跟一下流程。

handleDiskRemoved()
void VolumeManager::handleDiskRemoved(dev_t device) {
    //获取vm管理的mDisks表头
    //已经挂载成功的disk
    auto i = mDisks.begin();
    //判断mDisks表是否遍历结束;
    while (i != mDisks.end()) {
        //判断从mDisks表取得到的一个Disk的设备 与当前Uevent上报的设备是否一致;
        if ((*i)->getDevice() == device) {
            //判断设备比较一样后,将该Disk 从mDisks表里移除;
            (*i)->destroy();
            //删除释放到从mDisks表里移除的Disk;
            i = mDisks.erase(i);
        } else {
            ++i;
        }
    }
    //pending的disk 
    auto j = mPendingDisks.begin();
    while (j != mPendingDisks.end()) {
        if ((*j)->getDevice() == device) {
            j = mPendingDisks.erase(j);
        } else {
            ++j;
        }
    }
}

上面任务

  1. 从挂载的磁盘列表中移除当前磁盘,并执行destroy()

  2. 从Pending的磁盘列表中移除当前磁盘

我们主要关注第一个列表,已经挂载成功的列表中查询并destroy。

Disk.cpp

destroy()
status_t Disk::destroy() {
    CHECK(mCreated);
    //销毁所有volume
    destroyAllVolumes();
    mCreated = false;
    //通知StorageManagerService
    auto listener = VolumeManager::Instance()->getListener();
    if (listener) listener->onDiskDestroyed(getId());
    return OK;
}
destroyAllVolumes()
void Disk::destroyAllVolumes() {
    for (const auto& vol : mVolumes) {
        vol->destroy();
    }
    mVolumes.clear();
}

根据挂载流程中,mVolumes添加的是PublicVolume。

PublicVolume没有重写destroy(),找它父类VolumeBase。

VolumeBase.cpp

status_t VolumeBase::destroy() {
    CHECK(mCreated);
    //根据磁盘状态执行不同的需求,如果挂载成功了,就需要unmounted
    if (mState == State::kMounted) {
        unmount();
        setState(State::kBadRemoval);
    } else {
        setState(State::kRemoved);
    }
    auto listener = getListener();
    if (listener) listener->onVolumeDestroyed(getId());
    status_t res = doDestroy();
    mCreated = false;
    LOG(ERROR) << "Water_x VolumeBase  destroy 2";
    return res;
}

这里主要任务

  1. 判断磁盘状态,如果挂载就需要卸载,然后刷新状态

  2. 通知StorageManagerService磁盘卸载了

  3. 调用PublicVolume.doDestroy()

这里已知挂载成功过,也就是此时状态State::kMounted,因此需要unmount()

unmount()
status_t VolumeBase::unmount() {
    if (mState != State::kMounted) {
        return -EBUSY;
    }
    //设置正在退出状态
    setState(State::kEjecting);
    //不是太理解,上面已经变量过,难道考虑多分区?
    for (const auto& vol : mVolumes) {
        if (vol->destroy()) {
            LOG(WARNING) << getId() << " failed to destroy " << vol->getId()
                    << " stacked above";
        }
    }
    mVolumes.clear();
    //执行doUnmount()
    status_t res = doUnmount();
    setState(State::kUnmounted);
    return res;
}

对于for循环destroy(),不是很理解,这里猜测多分区或插入了Hub的考虑。具体我没有深入,这里不讨论哈。

最后走了doUnmount(),这个虚函数,子类实现了。

PublicVolume.cpp

doUnmount()
status_t PublicVolume::doUnmount() {
    //kill真正使用路径的进程
    KillProcessesUsingPath(getPath());
    //强制性卸载
    ForceUnmount(mFuseDefault);
    ForceUnmount(mFuseRead);
    ForceUnmount(mFuseWrite);
    ForceUnmount(mRawPath);
    if (mFusePid > 0) {
        kill(mFusePid, SIGTERM);
        TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
        mFusePid = 0;
    }
    rmdir(mFuseDefault.c_str());
    rmdir(mFuseRead.c_str());
    rmdir(mFuseWrite.c_str());
    rmdir(mRawPath.c_str());
    mFuseDefault.clear();
    mFuseRead.clear();
    mFuseWrite.clear();
    mRawPath.clear();
    return OK;
}

里面就懒得继续了跟了,就是卸载磁盘大致流程。

参考文章

相关文章

暂无评论

none
暂无评论...