Android磁盘之Vold启动

Android  源码分析  2024年1月3日 am8:08发布11个月前更新 城堡大人
111 0 0

前言

Volume Daemon,简写Vold,用于管理和控制Android平台外部存储设备的后台进程。这些管理或控制包括SD卡的插拔事件检测/SD卡挂载/卸载/格式化等。

记录一下Vold进程启动的源码分析,方便自己查阅。

Android P

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

正文

先看看vold哪里启动的。

这里主要涉及目录的代码

\system\vold

vold.rc

\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

至于何时加载,可以看看《init的启动》,init进程LoadBootScripts会添加/system/etc/init目录下的rc文件进行解析。

main.cpp

main()
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));
    VolumeManager *vm;
    NetlinkManager *nm;
    //解析参数,也就是vold.rc中带的
    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())) {
        exit(1);
    }
    //单例模式获取NetlinkManager对象
    if (!(nm = NetlinkManager::Instance())) {
        exit(1);
    }
    //设置是否打开VolumeManager中的日志,默认false
    if (android::base::GetBoolProperty("vold.debug", false)) {
        vm->setDebug(true);
    }
    //调用其start方法,
    if (vm->start()) {
        exit(1);
    }
    bool has_adoptable;
    bool has_quota;
    bool has_reserved;
    //解析fstab文件,该文件描述系统中各种文件系统的信息;我以MTK9669为例分析其fsab文件路径在vendor/etc/fstab.m7642
    if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {
        PLOG(ERROR) << "Water_x Error reading configuration... continuing anyways";
    }
    //跟StorageManagerService通信
    if (android::vold::VoldNativeService::start() != android::OK) {
        exit(1);
    }
    //调用NetlinkManagerstart方法  
    if (nm->start()) {
        exit(1);
    }
    //解析的参数设置到属性中
    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");
    coldboot("/sys/block");
    //将vold进程中主线程加入到线程池中
    android::IPCThreadState::self()->joinThreadPool();
    exit(0);
}

这里主要做了如下核心工作:

  1. 初始化VolumeManager

  2. 初始化NetlinkManager

  3. 调用VolumeManager.start()

  4. 启动VoldNativeService::start() [跟StorageManagerService通信]

  5. 调用NetlinkManager.start()

下面就按照上面5个继续跟踪。

VolumeManager.cpp

通过单例模式初始化

Instance()
VolumeManager *VolumeManager::Instance() {
    if (!sInstance)
        sInstance = new VolumeManager();
    return sInstance;
}
VolumeManager()
VolumeManager::VolumeManager() {
    mDebug = false;
    mNextObbId = 0;
    mSecureKeyguardShowing = true;
}

NetlinkManager.cpp

这个也是单例模式

Instance()
NetlinkManager *NetlinkManager::Instance() {
    if (!sInstance)
        sInstance = new NetlinkManager();
    return sInstance;
}
NetlinkManager()
NetlinkManager::NetlinkManager() {
    mBroadcaster = NULL;
}

VolumeManager.cpp

start()
int VolumeManager::start() {
	//清楚所有状态
    unmountAll();
    Devmapper::destroyAll();
    Loop::destroyAll();
    CHECK(mInternalEmulated == nullptr);
	//创建/data/media的VolumeBase
    mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
            new android::vold::EmulatedVolume("/data/media"));
    mInternalEmulated->create();
	//更新虚拟磁盘
	updateVirtualDisk();
    return 0;
}

VoldNativeService.cpp

start()
status_t VoldNativeService::start() {
    IPCThreadState::self()->disableBackgroundScheduling(true);
    //publish就是把该服务公开,并添加到ServiceManager,可以让其他客户端查询
    //具体看BinderService.h中publish()的定义
    status_t ret = BinderService<VoldNativeService>::publish();
    if (ret != android::OK) {
        return ret;
    }
    sp<ProcessState> ps(ProcessState::self());
    ps->startThreadPool();
    ps->giveThreadPoolName();
    return android::OK;
}

其中细节,具体看BinderService.h定定义哈

NetlinkManager.cpp

start()
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) {
        return -1;
    }
	//设置网络套接字属性
    if ((setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) &&
        (setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) < 0)) {
        goto out;
    }
    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
        goto out;
    }
	//绑定服务端
    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
        goto out;
    }
	//创建NetlinkHandler,并交给它处理通讯
    mHandler = new NetlinkHandler(mSock);
	//mHandler执行start()
    if (mHandler->start()) {
        goto out;
    }
    return 0;
out:
	//关闭socket
    close(mSock);
    return -1;
}

这里主要

  1. 创建socket,并绑定

  2. 创建NetlinkHandler,并start()

NetlinkHandler.h

这个目录:system\vold\NetlinkHandler.h

class NetlinkHandler: public NetlinkListener {

public:
    explicit NetlinkHandler(int listenerSocket);
    virtual ~NetlinkHandler();

    int start(void);
    int stop(void);

protected:
    virtual void onEvent(NetlinkEvent *evt);
};

NetlinkHandler.cpp

NetlinkHandler::NetlinkHandler(int listenerSocket) :
                NetlinkListener(listenerSocket) {
}

NetlinkHandler::~NetlinkHandler() {
}

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

int NetlinkHandler::stop() {
    return this->stopListener();
}

// 获取到 kernel 事件 后调用这个函数进行处理
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
	//这里获取VolumeManager
    VolumeManager *vm = VolumeManager::Instance();
    const char *subsys = evt->getSubsystem();
    if (!subsys) {
        return;
    }
	// 如果subsys是block类型的就调用VolumeManager的handleBlockEvent方法处理
    if (std::string(subsys) == "block") {
        vm->handleBlockEvent(evt);
    }
}

这里会把kernel中事件上报到onEvent(),然后这里调用VolumeManager->handleBlockEvent()。

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 卸载

参考文章

  1. Android vold进程一 Vold启动和NetlinkManager数据获取

  2. Android P 储存设备挂载

  3. Android9.0版本Vold服务源码分析

 历史上的今天

  1. 2023: Android 创建1像素的悬浮窗口(0条评论)
  2. 2020: 季羡林:漫谈人生的意义和价值(0条评论)
版权声明 1、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

叔本华:只有经过深思的东西才能成为真知

哪怕是藏书最丰的图书馆,如果书籍放置混乱的话,其实际用处也不及一个收藏不多、但却整理得有条有理的小图书室。同样,大量的知识如果未经自己思想的细心加工处理,其价值也远远逊色于数量更少、但却经过头脑多方反复斟酌的知识。这是因为只有通过把每一真实的知识相互比较,把我们的所知从各个方面和角度融会贯通以后,我...

梁实秋:想我的母亲

我的母亲姓沈,杭州人。世居城内上羊市街。我在幼时曾侍母归宁,时外祖母尚在,年近八十。外祖父入学后,没有更进一步的功名,但是课子女读书甚严。我的母亲教导我们读书启蒙,尝说起她小时苦读的情形。她同我的两位舅父一起冬夜读书,冷得腿脚僵冻,取大竹篓一,实以败絮,三个人伸足其中以取暖。我当时听得惕然心惊,遂不...

王小波:猫

下午,我回家的时候,看到地下室窗口的栅栏上趴着一只洁白的猫。它好象病了。我朝它走去时,它背对着我,低低的伏在那里,肚子紧紧的贴着铁条。我还从来没有见到过猫会这么谨小慎微的趴着,爪子紧紧的扒在铁条上。它浑身都在颤抖,头轻微的摇动着,耳朵在不停的转动,好象在追踪着每一个声响。它听见我的脚步声,每次我的...

Android Zygote进程的启动流程简单分析

前言在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的,因为Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,所有的进程都是直接或者间接地由init进程for...

DisplayMetrics获取宽高不对

前言Android P项目开发时,获取的屏幕高度存在误差,之前项目中获取的高度都是固定的,后面为了适配各种项目,改为了动态获取屏幕高宽。记录于此,方便自己查阅。好记性不如烂笔头正文Android 版本 : Android P获取的屏幕高度错误adb shell wm size#打...

Android悬浮窗实现 使用WindowManager

WindowManager介绍通过Context.getSystemService(Context.WINDOW_SERVICE)可以获得 WindowManager对象。使用WindowManager可以在其他应用最上层,甚至手机桌面最上层显示窗口。调用的是WindowManager继承自...