我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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

前言

我们分2篇文章来介绍Android 9.0中存储卡的挂载流程,本篇文章先介绍总体的挂载模块、Vold进程的入口main函数的详细分析,有了这些基础知识,下一篇中我们再详细介绍收到驱动层消息是怎么挂载和卸载存储卡的,还有framework层如何与vold进程通讯交流。Android 9.0 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);      klog_set_level(6);      //单例模式获取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;//关闭socketout:    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文件内容
第一列class="lazy" data-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;    }        *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层通讯。
Android 9.0 Vold挂载流程解析(下)

来源地址:https://blog.csdn.net/u014795729/article/details/132334459

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android 9.0 Vold 挂载流程分析

在Android 系统中所有的热插拔设备都是通过Vold 进程挂载的。通过kernel–>vold–>StorageManagerService这样的架构去逐级上报热插拔事件。 一、Vold 入口 --> /system/vold/main
2022-06-06

Android图片加载框架最新解析:从源码的角度理解Glide的执行流程

文章目录准备源码开始阅读1、with()2、load()3、into()总结 众所周知Glide是Android开发中普遍使用的图片加载框架,功能非常强大,API非常简便,也是Google官方唯一推荐的图片加载框架。 基本用法,本文不再叙述
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录