• <Android开发> Android vold - 第二篇 vold 的main()函数简介


    本系列主要介绍 Android vold,分为以下篇章
    <Android开发> Android vold - 第一篇 vold前言简介
    <Android开发> Android vold - 第二篇 vold 的main()函数简介
    <Android开发> Android vold - 第三篇 vold 的NetLinkManager类简介

    继前一边vold简介后,我们来看看具体的代码内容。

    4 vold的main()函数
    要了解vold服务是干什么的,对直接的方法就是看你源码到底写了什么内容。而vold服务的入口当然就是main()函数了。先把main()的内容贴出来,再对其内容进行讲解。vold服务的main()函数路径和内容如下。

    路径:LINUX/android/system/vold/main.cpp
    int main(int argc, char** argv) {
    	setenv("ANDROID_LOG_TAGS", "*:v", 1); //设置环境变量
    	android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM)); //初始化log系统
    
    	LOG(INFO) << "Vold 3.0 (the awakening) firing up";
    
    	LOG(VERBOSE) << "Detected support for:"
    		<< (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
    		<< (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
    		<< (android::vold::IsFilesystemSupported("vfat") ? " vfat" : ""); //输出支持的文件系统类型
    
    	VolumeManager *vm; //管理volume
    	CommandListener *cl; //和Framework进行通讯
    	CryptCommandListener *ccl; //运行 和Framework进行通讯 有关命令
    	NetlinkManager *nm; //接收内核消息
    
    	parse_args(argc, argv); //解析主函数参数
    
    	sehandle = selinux_android_file_context_handle(); //获取selinux,权限有关
    	if (sehandle) {
    		selinux_android_set_sehandle(sehandle); //设置selinux
    	}
    
    	//在我们刚刚从init继承的套接字上快速抛出一个CLOEXEC
    	fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);
    	fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);
    	mkdir("/dev/block/vold", 0755); //创建设备节点
    	/*当cryptfs检查并装载加密文件系统时*/
    	klog_set_level(6);
    
    	/* Create our singleton managers */
    	/*创建我们的单例 管理器*/
    	if (!(vm = VolumeManager::Instance())) {
    		LOG(ERROR) << "Unable to create VolumeManager";
    		exit(1);
    		}
    
    	if (!(nm = NetlinkManager::Instance())) {
    		LOG(ERROR) << "Unable to create NetlinkManager";
    		exit(1);
    	}
        //获取属性vold.debug
    	if (property_get_bool("vold.debug", false)) {
    		vm->setDebug(true);
    	}
    
    	cl = new CommandListener();
    	ccl = new CryptCommandListener();
    	vm->setBroadcaster((SocketListener *) cl);
    	nm->setBroadcaster((SocketListener *) cl);
    
    	if (vm->start()) { //卸载原有可能存在的所有磁盘。并新建 "/data/media"/Disk
    		PLOG(ERROR) << "Unable to start VolumeManager"; //卷管理器
    		exit(1);
    	}
    	bool has_adoptable;
    	bool has_quota;
    	//根据配置文件/etc/vold.fstab,初始化VolumeManager
    	if (process_config(vm, &has_adoptable, &has_quota)) {
    		PLOG(ERROR) << "Error reading configuration... continuing anyways";
    	}
    	//启动内核监听
    	if (nm->start()) {
    		PLOG(ERROR) << "Unable to start NetlinkManager";
    		exit(1);
    	}
    
    	/*现在我们起床了,我们可以响应命令了*/
    	if (cl->startListener()) {
    		PLOG(ERROR) << "Unable to start CommandListener";
    		exit(1);
    	}
    
    	if (ccl->startListener()) {
    		PLOG(ERROR) << "Unable to start CryptCommandListener";
    		exit(1);
    	}
    
    	//此调用应在侦听器启动后进行,以避免vold和init之间的死锁(有关详细信息,请参阅b/34278978)
    	property_set("vold.has_adoptable", has_adoptable ? "1" : "0");
    	property_set("vold.has_quota", has_quota ? "1" : "0");
    
    	//在这里进行冷启动,这样就不会阻止启动,如果我们在Vold启动之前连接了闪存驱动器,也需要冷启动
    	coldboot("/sys/block");  //触发内核sysfs发送uevent事件
    	//最终我们将成为监控线程
    	while(1) {
    		pause();
    	}
    
    	LOG(ERROR) << "Vold exiting";
    	exit(0);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    上面是vold服务最开始运行的代码内容,我们来一一看看。

    第3行:设置环境变量,其中"ANDROID_LOG_TAGS"环境变量名;“:v" 表示环境变量的值;1表示替换,即将”ANDROID_LOG_TAGS”环境变量的值替换为":v”。

    第4行:初始化log系统,这样在Vold中可输出log;
    该函数在“LINUX/android/system/core/base/logging.cpp”中实现,这里不在深入讨论log系统。

    第9~11行:通过IsFilesystemSupported()函数判断支持的文件系统类型,并输出;该函数在”LINUX/android/system/vold/Utils.cpp”中实现;

    static const char* kProcFilesystems = "/proc/filesystems";
    bool IsFilesystemSupported(const std::string& fsType) {
      std::string supported;
      if (!ReadFileToString(kProcFilesystems, &supported)) {
         PLOG(ERROR) << "Failed to read supported filesystems";
         return false;
      }
      return supported.find(fsType + "\n") != std::string::npos;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    从该函数可看到, kProcFilesystems 是操作读取的文件,该函数主要是读取文件filesystems,并匹配字符串。
    文件内容如下:
    在这里插入图片描述
    第13行:定义了一个VolumeManager类vm,用来管理volume的;后面会对这个类进行详细解释。

    第14行:定义了一个CommandListener类cl,用来同Framework进行通讯的;后面会对这个类进行详细解释。

    第15行:定义了一个CryptCommandListener类cll,用来同Framework进行通讯,处理有关cmd的;后面会对这个类进行详细解释。

    第16行:定义了一个NetlinkManager类nm,用来接收内核消息的;后面会对这个类进行详细解释。

    第18行:函数parse_args()解析主函数的参数,vold.rc文件启动vold服务时传入了参数;内容如下:

    路径:LINUX/android/system/vold/main.cpp
    static void parse_args(int argc, char** argv) {
      static struct option opts[] = {
        {"blkid_context", required_argument, 0, 'b' },
        {"blkid_untrusted_context", required_argument, 0, 'B' },
        {"fsck_context", required_argument, 0, 'f' },
        {"fsck_untrusted_context", required_argument, 0, 'F' },
      };
      int c;
      while ((c = getopt_long(argc, argv, "", opts, nullptr)) != -1) {
        switch (c) {
        case 'b': android::vold::sBlkidContext = optarg; break;
        case 'B': android::vold::sBlkidUntrustedContext = optarg; break;
        case 'f': android::vold::sFsckContext = optarg; break;
        case 'F': android::vold::sFsckUntrustedContext = optarg; break;
        }
      }
      CHECK(android::vold::sBlkidContext != nullptr);
      CHECK(android::vold::sBlkidUntrustedContext != nullptr);
      CHECK(android::vold::sFsckContext != nullptr);
      CHECK(android::vold::sFsckUntrustedContext != nullptr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    路径:LINUX/android/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
        socket vold stream 0660 root mount
        socket cryptd stream 0660 root mount
        ioprio be 2
        writepid /dev/cpuset/foreground/tasks
        shutdown critical
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    option的结构体如下:

    struct option
    {
      const char *name;
      /*has_arg不能是枚举,因为一些编译器抱怨所有假定它是int的代码中的类型不匹配*/
      int has_arg;
      int *flag;
      int val;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这参数与selinux有关。

    第20行,selinux_android_file_context_handle()获取selinux的handle;

    路径:
    LINUX/android/external/selinux/libselinux/src/android/android_platform.c
    static const struct selinux_opt seopts_file_split[] = {
    { SELABEL_OPT_PATH, "/system/etc/selinux/plat_file_contexts" },
    { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_file_contexts" }
    };
    static const struct selinux_opt seopts_file_rootfs[] = {
    { SELABEL_OPT_PATH, "/plat_file_contexts" },
    { SELABEL_OPT_PATH, "/nonplat_file_contexts" }
    };
    
    struct selabel_handle* selinux_android_file_context_handle(void)
    {
        if (selinux_android_opts_file_exists(seopts_file_split)) {
            return selinux_android_file_context(seopts_file_split,
                                                ARRAY_SIZE(seopts_file_split));
        } else {
            return selinux_android_file_context(seopts_file_rootfs,
                                                ARRAY_SIZE(seopts_file_rootfs));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    static struct selabel_handle* selinux_android_file_context(const struct selinux_opt *opts, unsigned nopts)
    {
        struct selabel_handle *sehandle;
        struct selinux_opt fc_opts[nopts + 1];
        memcpy(fc_opts, opts, nopts*sizeof(struct selinux_opt));
        fc_opts[nopts].type = SELABEL_OPT_BASEONLY;
        fc_opts[nopts].value = (char *)1;
        sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, ARRAY_SIZE(fc_opts));
        if (!sehandle) {
            selinux_log(SELINUX_ERROR, "%s: Error getting file context handle (%s)\n",
                    __FUNCTION__, strerror(errno));
            return NULL;
        }
        if (!compute_file_contexts_hash(fc_digest, opts, nopts)) { //
            selabel_close(sehandle);
            return NULL;
        }
        selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts\n");
        return sehandle;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    设备log如下图:
    在这里插入图片描述
    第21~24行: 在前一句获取到selinux的handle后,则设置android的selinux;

    路径:LINUX/android/external/selinux/libselinux/src/android/android_platform.c
    void selinux_android_set_sehandle(const struct selabel_handle *hndl)
    {
          fc_sehandle = (struct selabel_handle *) hndl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    因为selinux要详细讲解的话 能单独出一篇文章了。这里就不深入探索selinux,这里我们只需知道,vold根据vold.rc传入的参数可获得对应的权限,然后在selinux的handle里能使用对应权限的资源即可。

    第26~27行: fcntl()针对(文件)描述符提供控制。参数fd是被参数cmd操作(如下面的描述)的描述符。针对cmd的值,fcntl能够接受第三个参数int arg;
    第1个参数:其中android_get_control_socket(“vold”):获取对应socket的fd;F_SETFD
    第2个参数:对描述符操作功能

    1. 复制一个现有的描述符(cmd=F_DUPFD).
    2. 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
    3. 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
    4. 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
    5. 获得/设置记录锁(cmd=F_GETLK , F_SETLK或F_SETLKW).

    第3个参数:cmd值的F_GETFD和F_SETFD:

    F_GETFD: 取得与文件描述符fd联合的close-on-exec标志,类似FD_CLOEXEC。如果返回值和FD_CLOEXEC进行与运算结果是0的话,文件保持交叉式访问exec(),否则如果通过exec运行的话,文件将被关闭(arg 被忽略)

    F_SETFD: 设置close-on-exec标志,该标志以参数arg的FD_CLOEXEC位决定,应当了解很多现存的涉及文件描述符标志的程序并不使用常数 FD_CLOEXEC,而是将此标志设置为0(系统默认,在exec时不关闭)或1(在exec时关闭)

    第28行:创建目录文件“/dev/block/vold”,并给予0755的权限;

    第30行:设置klog的等级,即kernel的log等级。

    路径:LINUX/android/system/core/libcutils/klog.cpp
    static int klog_level = KLOG_INFO_LEVEL;
    //设置klog的级别
    void klog_set_level(int level) {
        klog_level = level;
    }
    
    static int __open_klog(void) {
        static const char kmsg_device[] = "/dev/kmsg";
    
        int ret = android_get_control_file(kmsg_device);
        if (ret >= 0) return ret;
        return TEMP_FAILURE_RETRY(open(kmsg_device, O_WRONLY | O_CLOEXEC));
    }
    
    #define LOG_BUF_MAX 512
    
    void klog_writev(int level, const struct iovec* iov, int iov_count) {
        if (level > klog_level) return;
    
        static int klog_fd = __open_klog();
        if (klog_fd == -1) return;
        TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));
    }
    
    void klog_write(int level, const char* fmt, ...) {
        if (level > klog_level) return;
    
        char buf[LOG_BUF_MAX];
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(buf, sizeof(buf), fmt, ap);
        va_end(ap);
    
        buf[LOG_BUF_MAX - 1] = 0;
        struct iovec iov[1];
        iov[0].iov_base = buf;
        iov[0].iov_len = strlen(buf);
        klog_writev(level, iov, 1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    klog_write(0函数调用时,打开(创建文件) /dev/kmsg,并写入log内容。
    第34行:函数VolumeManager::Instance()的内容可知,new VolumeManager()并返回给到vm;路径:LINUX/android/system/vold/VolumeManager.cpp;后面会对这个类进行详解;
    如果new不成功对报如下错误Log:
    LOG(ERROR) << “Unable to create VolumeManager”;

    第39行:函数NetlinkManager::Instance()的内容可知,new NetlinkManager()并返回给到nm;路径:LINUX/android/system/vold/NetlinkManager.cpp;后面会对这个类进行详解;
    如果new不成功对报如下错误Log:
    LOG(ERROR) << “Unable to create NetlinkManager”;

    第44行:函数property_get_bool()是用来获取属性值的,这里是要获取"vold.debug"这个属性值,且期待的返回值是false;如果返回true,则调用vm->setDebug(true);设置为Debug。

    路径:LINUX/android/system/core/libcutils/properties.cpp
    int8_t property_get_bool(const char *key, int8_t default_value) {
        if (!key) {
            return default_value;
        }
        int8_t result = default_value;
        char buf[PROPERTY_VALUE_MAX] = {'\0'};
    
        int len = property_get(key, buf, "");
        if (len == 1) {
            char ch = buf[0];
            if (ch == '0' || ch == 'n') {
                result = false;
            } else if (ch == '1' || ch == 'y') {
                result = true;
            }
        } else if (len > 1) {
            if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
                result = false;
            } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
                result = true;
            }
        }
        return result;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    路径:LINUX/android/system/vold/VolumeManager.cpp
    int VolumeManager::setDebug(bool enable) {
        mDebug = enable;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第48行:new 一个 CommandListener()类给cl,后续分析该类;
    第49行:new 一个 CryptCommandListener()类给cll,后续分析该类;
    第50行:设置vm的广播为cl;在类定义里只有
    void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

    第51行:设置nm的广播为cl;在类定义里只有
    void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

    第54行:vm start,开始管理volume。后面详解。

    第61行:process_config函数是根据配置文件/etc/vold.fstab 或/vendor/etc/fstab.xx 配置vm ;

    路径:LINUX/android/system/vold/main.cpp
    static int process_config(VolumeManager *vm, bool* has_adoptable, bool* has_quota) {
        fstab = fs_mgr_read_fstab_default();  //读配置文件 
        if (!fstab) {
            PLOG(ERROR) << "Failed to open default fstab";
            return -1;
        }
        /*遍历条目,查找vold管理的条目*/
        *has_adoptable = false;
        *has_quota = false;
        for (int i = 0; i < fstab->num_entries; i++) {
            if (fs_mgr_is_quota(&fstab->recs[i])) {
                *has_quota = true;
            }
            if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
                if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
                    LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
                    continue;
                }
                std::string sysPattern(fstab->recs[i].blk_device);
                std::string nickname(fstab->recs[i].label);
                int flags = 0;
                if (fs_mgr_is_encryptable(&fstab->recs[i])) {
                    flags |= android::vold::Disk::Flags::kAdoptable;
                    *has_adoptable = true;
                }
                if (fs_mgr_is_noemulatedsd(&fstab->recs[i])
                        || property_get_bool("vold.debug.default_primary", false)) {
                    flags |= android::vold::Disk::Flags::kDefaultPrimary;
                }
                vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
                        new VolumeManager::DiskSource(sysPattern, nickname, flags)));
            }
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    路径:LINUX/android/system/core/fs_mgr/fs_mgr_fstab.cpp
    /*尝试加载默认fstab 来自/odm/etc、/vendor/etc或/ <的硬件>文件。加载找到的第一个条目,并合并从设备树传入的fstab条目*/
    struct fstab *fs_mgr_read_fstab_default()
    {
        std::string hw;
        std::string default_fstab;
    
        //分别使用不同的fstab路径进行正常引导和恢复引导
        if (access("/sbin/recovery", F_OK) == 0) {
            default_fstab = "/etc/recovery.fstab";
        } else if (fs_mgr_get_boot_config("hardware", &hw)) {  // normal boot
            for (const char *prefix : {"/odm/etc/fstab.","/vendor/etc/fstab.", "/fstab."}) {
                default_fstab = prefix + hw;
                if (access(default_fstab.c_str(), F_OK) == 0) break;
            }
        } else {
            LWARNING << __FUNCTION__ << "(): failed to find device hardware name";
        }
    
        //将从设备树传入的fstab条目与从defaultfstab文件中找到的条目进行组合
        struct fstab *fstab_dt = fs_mgr_read_fstab_dt();
        struct fstab *fstab = fs_mgr_read_fstab(default_fstab.c_str());
    
        return in_place_merge(fstab_dt, fstab);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    加载fstab文件,并与dts中的fstab进行合并,最终保存到fstab,这其中主要遍历几个fstab的位置,包括:/etc/recovery.fstab,/odm/etc/fstab., /vendor/etc/fstab., /fstab.,我们的方案中实际会使用/vendor/etc/fstab.
    fstab是fstab类型,即std::vector,fstab文件最终解析结果会存放到fstab;
    我们可以看到fstab_rec的结构如下,它用于保存fstab中的一条记录

    路径:LINUX/android/system/core/fs_mgr/include_fstab/fstab/fstab.h
    /*条目必须按照在fstab中看到的相同顺序保存。除非明确要求,否则装载点上的查找应始终返回第一个*/
    struct fstab {
        int num_entries;
        struct fstab_rec* recs;
        char* fstab_filename;
    };
    struct fstab_rec {
        char* blk_device;
        char* mount_point;
        char* fs_type;
        unsigned long flags;
        char* fs_options;
        int fs_mgr_flags;
        char* key_loc;
        char* key_dir;
        char* verity_loc;
        long long length;
        char* label;
        int partnum;
        int swap_prio;
        int max_comp_streams;
        unsigned int zram_size;
        uint64_t reserved_size;
        unsigned int file_contents_mode;
        unsigned int file_names_mode;
        unsigned int erase_blk_size;
        unsigned int logical_blk_size;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    process_config函数中for循环将遍历fstab中被解析的每一条记录,为每条记录创建DiskSource,通过vm->addDiskSource存放到mDiskSources。
    在这里插入图片描述
    第65行:nm start,开始启动监听kernel的uevent事件。后面详解。

    第71行:cl的startListener()函数是创建监听FW的socket线程的;后续详解;

    第76行:ccl的startListener()函数也是创建监听FW的socket线程的;后续详解;

    第82~83行:设置属性"vold.has_adoptable" 和 “vold.has_quota”;
    对于has_adoptable 和has_quota 的值,取决于获取fatab配置;
    -> process_config(vm, &has_adoptable, &has_quota)
    -> if (fs_mgr_is_quota(&fstab->recs[i])) {
    *has_quota = true;
    }
    -> return fstab->fs_mgr_flags & MF_QUOTA;

    Quota是Linux的一个重要工具,使用Quota能对某一分区下指定用户或用户组进行磁盘限额。这里要说明的是,限额不是针对用户主目录,而是针对这个分区下的用户或用户组。Quota通过限制用户的blocks或者inodes起到限额的作用。

    ->if (fs_mgr_is_encryptable(&fstab->recs[i])) {
    flags |= android::vold::Disk::Flags::kAdoptable;
    *has_adoptable = true;
    }
    ->return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);

    LINUX/android/system/core/fs_mgr/fs_mgr_priv.h
    该 .h文件对fstab的格式内容进行说明,如下:
    /*fstab具有以下格式:
    *任何以#开头的行都是注释,将被忽略 *忽略任何空行
    *所有其他行必须采用以下格式:
    *
    *
    *<mount_flags>是一个逗号分隔的标志列表,可以传递给mount命令。该列表包括noatime、nosuid、nodev、nodiratime、ro、rw、remount和defaults。
    *是一个逗号分隔的选项列表,由正在装载的文件系统接受。它直接传递到mount而不进行分析
    *
    *是一个逗号分隔的标志列表,用于控制fs_mgr程序的操作。该列表包括“wait”(等待)和“check”(检查),前者将等待文件存在,后者要求fs_mgr在安装文件系统之前在上运行fscheck程序。如果在只读文件系统上指定了检查,则忽略该检查。此外,“可加密”意味着文件系统可以被加密。“可加密”标志_MUST_后跟一个=和一个字符串,该字符串是加密密钥的位置。它可以是包含密钥的文件或分区的路径,也可以是表示密钥位于包含文件系统的分区的最后16K字节中的单词“footer”。
    *
    *当fs_mgr被请求挂载所有文件系统时,它将首先挂载所有没有_NOT_指定check的文件系统(包括只读文件系统并指定check,因为在这种情况下会忽略check),然后它将检查并挂载标记为check的文件系统。
    */

    第86行:触发内核sysfs发送uevent事件;在这里进行冷启动,这样就不会阻止启动,如果我们在Vold启动之前连接了闪存驱动器(如U盘等),也需要冷启动;

    路径:LINUX/android/system/vold/main.cpp
    static void coldboot(const char *path) {
        DIR *d = opendir(path); //打开目录
        if(d) {
            do_coldboot(d, 0); //冷启动该目录
            closedir(d); //关闭目录
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    递归扫描/sys目录下的uevent节点,然后写入字符串“add”,强制触发内核uevent事件
    static void do_coldboot(DIR *d, int lvl) {
        struct dirent *de;
        int dfd, fd;
        dfd = dirfd(d);
        fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
        if(fd >= 0) {
            //往/sys/block目录写入add\n事件
            //往/sys/block发送add uevent事件
            write(fd, "add\n", 4);  
            close(fd);
    }
    // 递归调用do_coldboot(),扫描uevent节点
        while((de = readdir(d))) {
            DIR *d2;
            if (de->d_name[0] == '.')
                continue;
            if (de->d_type != DT_DIR && lvl > 0)
                continue;
            fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
            if(fd < 0)
                continue;
            d2 = fdopendir(fd);
            if(d2 == 0)
                close(fd);
            else {
                do_coldboot(d2, lvl + 1);
                closedir(d2);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    第88~90行:至此,当前main函数的功能就完成,剩下的或都交给了vm、nm、cl、cll了,main()则转为监控线程,就是不干活,但又不能退出;除非异常。
    接下来将会以 U盘设备插入、读取数据、拔出等内容,对vold的流程进行详细的解析。

    vold 模块的介绍的第二篇主要讲解了 vold模块 main()函数具体实现了哪些内容。后续我们再对具体的借口、功能、实现类方法进行详细解析。

  • 相关阅读:
    智慧箱变动环辅控系统
    Hazelcast系列(八):数据结构
    Kotlin基础——接口和类
    Hexagon_V65_Programmers_Reference_Manual(10)
    idea集成tomcat(Smart Tomcate插件安装)
    Springboot毕业设计毕设作品,个人博客系统 开题报告
    lunatic亚毫秒 Web 框架的LiveView实时视图
    【T3】畅捷通T3凭证打印修改边距后,打印效果没有任何变化。
    MongoDB开启远程连接
    【Python】 Flask相关疑问
  • 原文地址:https://blog.csdn.net/qq_39257814/article/details/128180820