Android 9.0 Vold挂载流程解析(上)

news2024/9/27 19:18:19

Android 9.0 Vold挂载流程解析(上)

  • 前言
  • Android挂载模块整体框架
  • Vold进程main函数详细分析
  • 总结

前言

我们分2篇文章来介绍Android 9.0中存储卡的挂载流程,本篇文章先介绍总体的挂载模块、Vold进程的入口main函数的详细分析,有了这些基础知识,下一篇中我们再详细介绍收到驱动层消息是怎么挂载和卸载存储卡的,还有framework层如果与vold进程通讯交流。

Android挂载模块整体框架

存储卡挂载模块由驱动层、vold进程、framework层、App层这几个模块注册,vold进程通过Socket方式监听驱动层存储卡热插拔事件(Add、Change 、Remove),创建相应的磁盘管理类,管理磁盘的生命周期状态,提供挂载、卸载等功能,并把相应磁盘信息状态通过Binder的方式回调给Framework层,方便App层获取磁盘信息和状态。以下是其整体的模块框架图:
挂载流程框架图

从图中我们知道vold有三个核心的类,NetlinkManager、VolumeManager、VoldNativeService,这三个类在启动vold进程时就会调用其start方法启动,NetlinkManager里创建了Socket连接并交给NetlinkHandler处理通讯,由NetlinkHandler监听驱动层发送的uevent事件,并转发给VolumeManager处理,VolumeManager接受到相应的事件,会创建存储管理的类获取存储卡的信息和状态的并通过VoldNativeService回调给Framework层StorageManagerService处理,StroageManagerService也可以通过binder机制调用VoldNativeSerivice的方法,设置userId,shutdown等,好让vold进程进行相应的处理。StorageManagerService也提供了存储卡操作相关的方法给APP调用,App通过获取StorageManager类间接调用StorageManagerService中的方法。

Vold进程main函数详细分析

我们从Vold进程的main.cpp中入手开始分析
vold进程main.cpp路径:system/vold/main.cpp

int main(int argc, char** argv) {
   atrace_set_tracing_enabled(false);
   //设置日志等级
   setenv("ANDROID_LOG_TAGS", "*:v", 1);
   android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));

   LOG(INFO) << "Vold 3.0 (the awakening) firing up";

   ATRACE_BEGIN("main");

  //打印支持的底层文件系统
   LOG(VERBOSE) << "Detected support for:"
           << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
           << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
           << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "")
           // Mediatek Android Patch Begin
           << (android::vold::IsFilesystemSupported("ntfs") ? " ntfs" : "")
           << (android::vold::IsFilesystemSupported("cifs") ? " cifs" : "");
           // Mediatek Android Patch End

   VolumeManager *vm;
   NetlinkManager *nm;
   //解析参数
   parse_args(argc, argv);

   sehandle = selinux_android_file_context_handle();
   if (sehandle) {
       selinux_android_set_sehandle(sehandle);
   }
//创建/dev/block/vold目录,挂载存储卡了其下有对应的节点信息
   mkdir("/dev/block/vold", 0755);

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

   /* Create our singleton managers */
   //单例模式获取VolumeManager对象
   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);
   }
//设置是否打开VolumeManager中的日志,默认false
   if (android::base::GetBoolProperty("vold.debug", false)) {
       vm->setDebug(true);
   }
//调用其start方法,稍后分析 1
   if (vm->start()) {
       PLOG(ERROR) << "Unable to start VolumeManager";
       exit(1);
   }

   bool has_adoptable;
   bool has_quota;
   bool has_reserved;
//解析fstab文件,该文件描述系统中各种文件系统的信息;我以MTK9669为例分析其fsab文件路径在vendor/etc/fstab.m7642
//稍后详细分析该方法 2
   if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {
       PLOG(ERROR) << "Error reading configuration... continuing anyways";
   }

   ATRACE_BEGIN("VoldNativeService::start");
   //启动与framework通讯的服务
   if (android::vold::VoldNativeService::start() != android::OK) {
       LOG(ERROR) << "Unable to start VoldNativeService";
       exit(1);
   }
   ATRACE_END();

   LOG(DEBUG) << "VoldNativeService::start() completed OK";

   ATRACE_BEGIN("NetlinkManager::start");
   //调用NetlinkManager start 方法    
   //稍后详细分析 3
   if (nm->start()) {
       PLOG(ERROR) << "Unable to start NetlinkManager";
       exit(1);
   }
   ATRACE_END();

   // This call should go after listeners are started to avoid
   // a deadlock between vold and init (see b/34278978 for details)
   //解析的参数设置到属性中
   android::base::SetProperty("vold.has_adoptable", has_adoptable ? "1" : "0");
   android::base::SetProperty("vold.has_quota", has_quota ? "1" : "0");
   android::base::SetProperty("vold.has_reserved", has_reserved ? "1" : "0");

   // Do coldboot here so it won't block booting,
   // also the cold boot is needed in case we have flash drive
   // connected before Vold launched
   coldboot("/sys/block");

   ATRACE_END();
 //将vold进程中主线程加入到线程池中
   android::IPCThreadState::self()->joinThreadPool();
   LOG(INFO) << "vold shutting down";

   exit(0);
}

通过以上代码分析我们总结其做了以下几件事:
1.创建/dev/block/vold目录
2.单例模式获取NetlinkManager对象并调用其start方法
3.解析fstab文件
4.调用VoldNativeService::start()方法,与framework通讯
5.单例模式获取VolumeManager对象并调用其start方法

接下来分析NetlinkManager中start方法,路径:system/vold/NetlinkManager.cpp

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;
//创建socket客户端
    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;
    }
//交给NetlinkHander处理通讯
    mHandler = new NetlinkHandler(mSock);
    if (mHandler->start()) {
        PLOG(ERROR) << "Unable to start NetlinkHandler";
        goto out;
    }

    return 0;
//关闭socket
out:
    close(mSock);
    return -1;
}

该方法处理很简单就是建立了Socket并交给其NetlinkHandler处理,调用其start方法,接下来看NetlinkHandler中做了什么
路径:system/vold/NetlinkHandler.cpp

int NetlinkHandler::start() {
   //调用其父类SocketListener中的方法,开始监听服务端中的消息
   //消息会解析成NetlinkEvent对象作为参数并回调onEvent(NetlinkEvent *evt)方法
    return this->startListener();
}

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;
    }
  // 如果subsys是block类型的就调用VolumeManager的handleBlockEvent方法处理
    if (std::string(subsys) == "block") {
        vm->handleBlockEvent(evt);
    }
}


通过以上代码分析NetlinkManager主要与驱动层建立Socket连接,接收存储卡的插拔事件传递给VolumeManager处理。

回到main.cpp中分析下怎么解析fstab文件的,先看下MTK9669中vendor/etc/fstab.m7642中的内容
fstab文件内容
第一列Src,下面方法解析中的rec->blk_device属性,用于匹配解析驱动穿过来的挂载的文件系统路径
第二列mnt_point,挂载点,外部存储卡挂载为auto
第三列类型,文件系统类型,外部存储卡为auto
第四列第五列为mnt_flags、fs_mgr_flags文件系统挂载标志位

static int process_config(VolumeManager* vm, bool* has_adoptable, bool* has_quota,
                          bool* has_reserved) {
    ATRACE_NAME("process_config");
     //解析fstab文件获取fstab结构体对象
    fstab_default = fs_mgr_read_fstab_default();
    if (!fstab_default) {
        PLOG(ERROR) << "Failed to open default fstab";
        return -1;
    }

    /* Loop through entries looking for ones that vold manages */
    *has_adoptable = false;
    *has_quota = false;
    *has_reserved = false;
    //遍历每一行数据
    for (int i = 0; i < fstab_default->num_entries; i++) {
        auto rec = &fstab_default->recs[i];
        //fs_mgr_flags列是否包含了quota
        if (fs_mgr_is_quota(rec)) {
            *has_quota = true;
        }
        //reserved_size是否大于0
        if (rec->reserved_size > 0) {
            *has_reserved = true;
        }
         //fs_mgr_flags列是否有voldmanaged标志
        if (fs_mgr_is_voldmanaged(rec)) {
              //是否是不可移动的
            if (fs_mgr_is_nonremovable(rec)) {
                LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
                continue;
            }

            std::string sysPattern(rec->blk_device);
            std::string nickname(rec->label);
			
         //add by liuxin debug
		   LOG(DEBUG) << "sysPattern="<<rec->blk_device<<",nickname="<<rec->label<<",mountPoint="<<rec->mount_point;
            int flags = 0;
           //fs_mgr_flags是否encryptable
            if (fs_mgr_is_encryptable(rec)) {
                flags |= android::vold::Disk::Flags::kAdoptable;
                *has_adoptable = true;
            }
            //没有主存储卡
            if (fs_mgr_is_noemulatedsd(rec)
                    || android::base::GetBoolProperty("vold.debug.default_primary", false)) {
                flags |= android::vold::Disk::Flags::kDefaultPrimary;
            }

//把解析的参数创建DiskSource对象添加到VolumeManager中
            vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
                    new VolumeManager::DiskSource(sysPattern, nickname, flags)));
        }
    }
    return 0;
}

上面代码解析了fstabe文件设置了has_adoptable、has_quota 、has_reserved属性,并且fs_mgr_flags列是voldmanager解析处理创建DiskSource对象添加到VolumeManager中。
看打印如下:
日志打印
接下来分析VoldNativeService的start()方法,VoldNativeService继承自BinderService,BinderService继承BBinder,所以它是Binder机制中的服务端程序

status_t VoldNativeService::start() {
    IPCThreadState::self()->disableBackgroundScheduling(true);
    //注册当前客户端到binder驱动
    status_t ret = BinderService<VoldNativeService>::publish();
    if (ret != android::OK) {
        return ret;
    }
    //加入到binder线程池
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();
    ps->giveThreadPoolName();
    return android::OK;
}

上述代码主要把当前的binder服务端加入到binder驱动中,方便提供给客户端调用

接下来看第五个步骤VolumeManager的start方法:

int VolumeManager::start() {
    ATRACE_NAME("VolumeManager::start");

    // Always start from a clean slate by unmounting everything in
    // directories that we own, in case we crashed.
    //卸载掉所有的存储卡
    unmountAll();

    Devmapper::destroyAll();
    Loop::destroyAll();

    // Assume that we always have an emulated volume on internal
    // storage; the framework will decide if it should be mounted.
    CHECK(mInternalEmulated == nullptr);
    //创建内部存储卡
    mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
            new android::vold::EmulatedVolume("/data/media"));
    mInternalEmulated->create();

    // Consider creating a virtual disk
    //虚拟存储卡不考虑
    updateVirtualDisk();

    return 0;
}

上面代码很简单,先卸载所有的存储卡,再创建了内部储存卡,其EmulateVolume关于挂载卸载的操作我们在下一篇文章中再介绍了。

总结

本篇文章介绍总体的挂载模块、Vold进程的入口main函数的详细分析,下一篇将介绍收到插拔事件如果管理存储卡信息和状态与Framework层通讯。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/889518.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文心一言最新重磅发布!

8月16日&#xff0c;由深度学习技术及应用国家工程研究中心主办的WAVE SUMMIT深度学习开发者大会2023举办。百度首席技术官、深度学习技术及应用国家工程研究中心主任王海峰以《大语言模型为通用人工智能带来曙光》为题&#xff0c;阐述了大语言模型具备理解、生成、逻辑、记忆…

C++新经典01--函数递归

函数的递归 #include <stdio.h> void diguifunc() {printf("diguifunc()函数执行\n");diguifunc();//自己调用自己 }void main(){diguifunc(); }把程序执行起来&#xff0c;等几秒钟&#xff0c;可以看到&#xff0c;屏幕不断滚动并输出如下内容&#xff1a; …

通过cpolar分享本地电脑上有趣的照片:部署piwigo网页【无公网IP内网穿透】

在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。我们是移动开发领域的优质创作者&#xff0c;同时也是阿里云专家博主。 ✨ 关注我们的主页&#xff0c;探索iOS开发的无限可能&#xff01; &#x1f525;我们与您分享最新的技术洞察和实战经验&#xff0c;助您在移动应…

如何在pycharm中指定GPU

如何在pycharm中指定GPU 作者:安静到无声 个人主页 目录 如何在pycharm中指定GPU打开编辑配置点击环境变量添加GPU配置信息推荐专栏在Pycharm运行程序的时候,有时候需要指定GPU,我们可以采用以下方式进行设置: 打开编辑配置 点击环境变量 添加GPU配置信息 添加名称:CU…

【C++学习手札】一文带你初识C++继承

食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f340;本文前置知识&#xff1a; C类 ♈️今日夜电波&#xff1a;napori—Vaundy 1:21 ━━━━━━️&#x1f49f;──────── 3:23 …

unity物体移动至指定位置

物体坐标与物体移动 世界坐标与局部坐标之间的转换物体移动至指定位置需求思路注意 世界坐标与局部坐标之间的转换 在Unity中&#xff0c;物体的坐标分为局部坐标和世界坐标。 局部坐标是相对于物体的父对象的坐标系&#xff0c;而世界坐标是相对于场景的整体坐标系。 使用tr…

一个DW的计算

一个DW的计算 1- 题目: 已知一个DW1.1 要求: 从DW中取出指定的位的值1.1.1 分析1.1.2 实现1.1.3 简化实现1.1.4 验证 2- 题目: 已知一个DW2.1 要求: 从DW中的指定的P和S,取出指定的位的值2.1.1 分析2.1.2 实现 1- 题目: 已知一个DW 有图中所示一行信息&#xff0c;表示一个DW(…

mktime有时会返回-1使用boost库没有问题

linux获得时间戳 #include <iostream> #include <boost/date_time/posix_time/posix_time.hpp> long long utc8_to_stamp(int date, float time) {struct tm stm;int itime (time);int iY date/10000,iM(date-iY*10000)/100,iDdate%100,iHitime/10000,iMin(itime…

学习笔记十七:node节点选择器,亲和性

node节点选择器&#xff0c;污点、容忍度、亲和性 node节点选择器nodeName&#xff0c;指定pod节点运行在哪个具体node上nodeSelector&#xff1a;指定pod调度到具有哪些标签的node节点上 亲和性node节点亲和性使用requiredDuringSchedulingIgnoredDuringExecution硬亲和性使用…

Linux编程库

1、Linux编程库介绍&#xff1a; 编程库就是指始终可以被多个Linux软件项目重复使用的代码集。 使用编程库有两个主要的优点&#xff1a; 可以简化编程&#xff0c;实现代码重复使用&#xff0c;进而减小应用程序的大小。可以直接使用比较稳定的代码。 Linux下的库文件分为共…

rabbitmq容器启动后修改连接密码

1、进入容器 docker exec -it rabbitmq bash 2、查看当前用户列表 rabbitmqctl list_users 3、修改密码 rabbitmqctl change_password [username] ‘[NewPassword]’ 4、修改后退出容器 ctrlpq 5、退出容器后即可生效&#xff0c;不需要重启容器

三分之一的英国大学生被欺诈

根据NatWest的一项新研究&#xff0c;去年英国大学三分之一的学生在网上遭遇欺诈。 今年5月&#xff0c;这家高街银行委托咨询公司RedBrick对来自63个城镇的3000多名英国大学生进行了调查。 尽管三分之一的受访者表示他们在过去的12个月里遇到过诈骗&#xff0c;但没有统计数…

动手学深度学习-pytorch版本(二):线性神经网络

参考引用 动手学深度学习 1. 线性神经网络 神经网络的整个训练过程&#xff0c;包括: 定义简单的神经网络架构、数据处理、指定损失函数和如何训练模型。经典统计学习技术中的线性回归和 softmax 回归可以视为线性神经网络 1.1 线性回归 回归 (regression) 是能为一个或多个…

【06 英语语法:时态、语态、虚拟语气】

时态、语态、虚拟语气 1. 时态和语态1.1 时态: 4个时间*4个状态 &#xff08;时间&#xff1a;现在、过去、将来、过去将来&#xff1b;状态&#xff1a;一般、进行、完成、完成进行&#xff09;⑴ 16 时态 详解表⑵ 主从句的 时态搭配⑶ 常用的 不规则动词变化 1.2 语态&#…

url下载地址含非法字符下载失败

示例下载链接&#xff1a;https://666666.shei.org.cn:2023/20230105/0ac280a3-498b-45d8-830c-a788475a8022/2023817-F2666666很六 &#xff08;改&#xff09;.doc java.lang.IllegalArgumentException: Illegal character in path at index 96: https://666666.shei.org.c…

android resoure资源图片颜色值错乱

最近androidstudio开发&#xff0c;添加一些颜色值或者drawable资源文件时&#xff0c;运行app,颜色值或者图片对应不上&#xff0c;暂时找不到原因&#xff0c;望告知。 暂时解决方法&#xff1a;

IT 运营管理中的根本原因分析(RCA)

全球数字化的兴起造成了一种情况&#xff0c;即组织在很大程度上依赖于其IT基础架构&#xff0c;就像我们依赖神经系统一样。我们可以将其等同于神经系统&#xff0c;因为IT基础架构可以实现有效控制&#xff0c;协调所有功能&#xff0c;并确保高效&#xff0c;顺利地完成每项…

代码随想录算法训练营第60天|动态规划part17| 647. 回文子串、516.最长回文子序列、动态规划总结篇

代码随想录算法训练营第60天&#xff5c;动态规划part17&#xff5c; 647. 回文子串、516.最长回文子序列、动态规划总结篇 647. 回文子串 647. 回文子串 思路&#xff1a; 暴力解法 两层for循环&#xff0c;遍历区间起始位置和终止位置&#xff0c;然后还需要一层遍历判断…

vue2.0/vue3.0学习笔记——2022.08.16

vue2&#xff08;查漏补缺&#xff09; 一、vue基础 内置指令&#xff08;查漏补缺&#xff09; 1、v-text 更新元素的textContent 2、v-html 更新元素的innerHtml 3、v-cloak 防止闪现&#xff0c;与css配合: [v-cloak] {dispaly: none} 4、v-once 在初次动态渲染厚&#x…

1N4007S 整流二极管 1A 1000V A-405

前两天二极管生产厂家东沃电子科普过1N4007和1N4007G这两种普通塑封整流二极管&#xff0c;查看“STD-1N4001 Thru 1N4007 (DO-41) Datasheet”和“STD-1N4001G Thru 1N4007G (DO-41) Datasheet”产品手册可知&#xff0c;1N4007和1N4007G参数除了结电容和芯片尺寸不一样以外&a…