• 安卓FirstStageMount阶段解析【连载】(一)创建设备Create


    最近在分析安卓的FirstStageMount阶段,这部分代码量还是挺大的,而且不容易理解,所以记录一下自己学习的心得和成果。本文都是基于Android S来分析的。

    源码地址如下:Android S源码

    1.引言

    根据安卓的启动流程,从Kernel进入到Init进程后,首先会执行FirstStageMain,里面又分为第一阶段挂在DoFirstStageMount和第二部分selinux_setup配置,这个系列的文章会分析DoFirstStageMount所有的流程;

    // 路径:/system/core/init/first_stage_init.cpp
    int FirstStageMain(int argc, char** argv) {
    	// ...
        if (!DoFirstStageMount(!created_devices)) {
            LOG(FATAL) << "Failed to mount required partitions early ...";
        }
      	// ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.开始DoFirstStageMount

    // 路径: /system/core/init/first_stage_mount.cpp
    // 在设备树中挂载由fstab文件指定的分区
    bool DoFirstStageMount(bool create_devices) {
    	// -----------------------------第一部分-----------------------------
        auto fsm = FirstStageMount::Create();
        if (!fsm.ok()) {
            LOG(ERROR) << "Failed to create FirstStageMount " << fsm.error();
            return false;
        }
    	
    	// ...这边是判断之前是否创建过devices,可以打印Log看一下,显然之前没有创建过,所以这里是False
        if (create_devices) {
            if (!(*fsm)->DoCreateDevices()) return false;
        }
    	
    	// -----------------------------第二部分-----------------------------
        return (*fsm)->DoFirstStageMount();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    可以看到DoFirstStageMount的做了两件事,分别为:

    • 调用FirstStageMount类的Create方法,返回一个参数fsm【大概率跟FirstStageMount有关,因为第二部分调用了它的DoFirstStageMount方法】
    • 第二部分,调用FirstStageMount类的DoFirstStageMount方法

    本文先讲解第一部分Create方法

    3.FirstStageMount::Create()

    // 路径:/system/core/init/first_stage_mount.cpp
    Result<std::unique_ptr<FirstStageMount>> FirstStageMount::Create() {
    	// 读取第一阶段的Fstab文件
        auto fstab = ReadFirstStageFstab();
        if (!fstab.ok()) {
            return fstab.error();
        }
    	// AVB2应该返回一个由fstab转译的独占指针【这里也说名了第二部分的fsm指针是啥】
        if (IsDtVbmetaCompatible(*fstab)) {
            return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));
        } else {
            return std::make_unique<FirstStageMountVBootV1>(std::move(*fstab));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3.1 ReadFirstStageFstab

    // 路径: /system/core/init/first_stage_mount.cpp
    static Result<Fstab> ReadFirstStageFstab() {
        Fstab fstab;
        if (!ReadFstabFromDt(&fstab)) {
            if (ReadDefaultFstab(&fstab)) {
                fstab.erase(std::remove_if(fstab.begin(), fstab.end(),
                                           [](const auto& entry) {
                                               return !entry.fs_mgr_flags.first_stage_mount;
                                           }),
                            fstab.end());
            } else {
                return Error() << "failed to read default fstab for first stage mount";
            }
        }
        return fstab;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    从函数的字面意思,我们大致推断:

    • 先从设备树文件中读取Fstab文件【ReadFstabFromDt】
      • 如果失败了, 就去读取默认的Fstab文件;并且删除其中 fs_mgr_flags中没有first_stage_mount不为1的,也就是这个参数没有设置

    3.2 ReadFstabFromDt

    // 路径: /system/core/fs_mgr/fs_mgr_fstab.cpp
    bool ReadDefaultFstab(Fstab* fstab) {
        fstab->clear();
        // 调用ReadFstabFromDt(fstab, false)重载函数
        ReadFstabFromDt(fstab, false /* verbose */);
    
        std::string default_fstab_path;
        // Use different fstab paths for normal boot and recovery boot, respectively
        if (access("/system/bin/recovery", F_OK) == 0) {
            default_fstab_path = "/etc/recovery.fstab";
        } else {  // normal boot
            default_fstab_path = GetFstabPath();
        }
    
        Fstab default_fstab;
        if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
            for (auto&& entry : default_fstab) {
                fstab->emplace_back(std::move(entry));
            }
        } else {
            LINFO << __FUNCTION__ << "(): failed to find device default fstab";
        }
    
        return !fstab->empty();
    }
    -------------------------------------------------------------------------------------
    // Returns fstab entries parsed from the device tree if they exist
    bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
    	// 调用ReadFstabFromDt()重载函数
        std::string fstab_buf = ReadFstabFromDt();
        if (fstab_buf.empty()) {
            if (verbose) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
            return false;
        }
    
        std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
            fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
                     fstab_buf.length(), "r"), fclose);
        if (!fstab_file) {
            if (verbose) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
            return false;
        }
    
        if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
            if (verbose) {
                LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
                       << fstab_buf;
            }
            return false;
        }
    
        SkipMountingPartitions(fstab, verbose);
    
        return true;
    }
    -------------------------------------------------------------------------------------
    std::string ReadFstabFromDt() {
    	// ... 
    	// 拼接设备树地址 + "/fstab"
        std::string fstabdir_name = get_android_dt_dir() + "/fstab";
        std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
        if (!fstabdir) return {};
    
        dirent* dp;
        // 每一个元素fstab_dt_entries is .
        std::vector<std::pair<std::string, std::string>> fstab_dt_entries;
        while ((dp = readdir(fstabdir.get())) != NULL) {
            // skip over name, compatible and .
            if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
    
            // 创建fstab_entry,格式为:       \n
            // 将fstab文件中key-value键值对放到fstab_entry中
            std::vector<std::string> fstab_entry;
            std::string file_name;
            std::string value;
            // skip a partition entry if the status property is present and not set to ok
            file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
            if (ReadDtFile(file_name, &value)) {
                if (value != "okay" && value != "ok") {
                    LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
                    continue;
                }
            }
    
            file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
            if (!ReadDtFile(file_name, &value)) {
                LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
                return {};
            }
            fstab_entry.push_back(value);
    
            std::string mount_point;
            file_name =
                android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
            if (ReadDtFile(file_name, &value)) {
                LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
                mount_point = value;
            } else {
                mount_point = android::base::StringPrintf("/%s", dp->d_name);
            }
            fstab_entry.push_back(mount_point);
    
            file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
            if (!ReadDtFile(file_name, &value)) {
                LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
                return {};
            }
            fstab_entry.push_back(value);
    
            file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
            if (!ReadDtFile(file_name, &value)) {
                LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
                return {};
            }
            fstab_entry.push_back(value);
    
            file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
            if (!ReadDtFile(file_name, &value)) {
                LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
                return {};
            }
            fstab_entry.push_back(value);
            // 将fstab_entry添加到fstab_dt_entries中
            fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " "));
        }
    
        // 根据挂载点mount_point对fstab_dt_entries进行排序,确保/vendor在/vendor/xxx前
        std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),
                  [](const auto& a, const auto& b) { return a.first < b.first; });
    
    	// 返回fstab中读取的值,放到fstab_result中返回
        std::string fstab_result;
        for (const auto& [_, dt_entry] : fstab_dt_entries) {
            fstab_result += dt_entry + "\n";
        }
        return fstab_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
    • 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
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137

    调用栈分别为:bool ReadDefaultFstab(Fstab* fstab) -> bool ReadFstabFromDt(Fstab* fstab, bool verbose)->std::string ReadFstabFromDt() ,所以我们选择从后往前看

    3.2.1 std::string ReadFstabFromDt()

    这个函数会去读取fstab文件中的值,然后返回一个字符串,这个字符串以\n分割
    每一个\n分割的是不同的一个fstab_entry,其格式为 \n

    3.2.2 bool ReadFstabFromDt(Fstab* fstab, bool verbose)

    bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
    	// 获取从ReadFstabFromDt读取到的FstEntry字符串
        std::string fstab_buf = ReadFstabFromDt();
    
    
        std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
            fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
                     fstab_buf.length(), "r"), fclose);
    	
    	// 调用ReadFstabFile
        if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
            if (verbose) {
                LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
                       << fstab_buf;
            }
            return false;
        }
    
        SkipMountingPartitions(fstab, verbose);
    
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.2.3 ReadFstabFile

    bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
        ssize_t len;
        size_t alloc_len = 0;
        char *line = NULL;
        const char *delim = " \t";
        char *save_ptr, *p;
        Fstab fstab;
    
        while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
            /* if the last character is a newline, shorten the string by 1 byte */
            if (line[len - 1] == '\n') {
                line[len - 1] = '\0';
            }
    
            /* Skip any leading whitespace */
            p = line;
            while (isspace(*p)) {
                p++;
            }
            /* ignore comments or empty lines */
            if (*p == '#' || *p == '\0')
                continue;
    
            FstabEntry entry;
    
            if (!(p = strtok_r(line, delim, &save_ptr))) {
                LERROR << "Error parsing mount source";
                goto err;
            }
            entry.blk_device = p;
    
            if (!(p = strtok_r(NULL, delim, &save_ptr))) {
                LERROR << "Error parsing mount_point";
                goto err;
            }
            entry.mount_point = p;
    
            if (!(p = strtok_r(NULL, delim, &save_ptr))) {
                LERROR << "Error parsing fs_type";
                goto err;
            }
            entry.fs_type = p;
    
            if (!(p = strtok_r(NULL, delim, &save_ptr))) {
                LERROR << "Error parsing mount_flags";
                goto err;
            }
    
            ParseMountFlags(p, &entry);
    
            // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
            if (proc_mounts) {
                p += strlen(p);
            } else if (!(p = strtok_r(NULL, delim, &save_ptr))) {
                LERROR << "Error parsing fs_mgr_options";
                goto err;
            }
    
            ParseFsMgrFlags(p, &entry);
    
            if (entry.fs_mgr_flags.logical) {
                entry.logical_partition_name = entry.blk_device;
            }
    
            fstab.emplace_back(std::move(entry));
        }
    
        if (fstab.empty()) {
            LERROR << "No entries found in fstab";
            goto err;
        }
    
        /* If an A/B partition, modify block device to be the real block device */
        if (!fs_mgr_update_for_slotselect(&fstab)) {
            LERROR << "Error updating for slotselect";
            goto err;
        }
        free(line);
        *fstab_out = std::move(fstab);
        return true;
    
    err:
        free(line);
        return false;
    }
    
    • 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

    这个函数主要的作用我们可以分析一下:

    • 将前面3.2.1的fstabEntry字符串正式映射成FstabEntry 对象,然后将其放在Fstab
    • 最后还要判断一下是不是包含A/B分区的,如果是AB分区的,调用fs_mgr_update_for_slotselect
      • 如果是AB分区fs_mgr_update_for_slotselect添加逻辑分区的后缀
    // 路径:/system/core/fs_mgr/fs_mgr_slotselect.cpp
    bool fs_mgr_update_for_slotselect(Fstab* fstab) {
        std::string ab_suffix;
    
        for (auto& entry : *fstab) {
        	// slot_select 为非0 且 slot_select_other 为非0【这种是错的,不存在这种两个都是非0的,检查dts文件吧】
            if (!entry.fs_mgr_flags.slot_select && !entry.fs_mgr_flags.slot_select_other) {
                continue;
            }
    
            if (ab_suffix.empty()) {
            	// 获取后缀
                ab_suffix = fs_mgr_get_slot_suffix();
                // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.
                if (ab_suffix.empty()) return false;
            }
    
            const auto& update_suffix =
                    entry.fs_mgr_flags.slot_select ? ab_suffix : other_suffix(ab_suffix);
            entry.blk_device = entry.blk_device + update_suffix;
            // Entry逻辑分区的名字 += update_suffix
            // 如果是AB分区的话应该是 system_a或者vendor_a之类的
            entry.logical_partition_name = entry.logical_partition_name + update_suffix;
        }
        return true;
    }
    
    • 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

    3.2.4 bool ReadDefaultFstab(Fstab* fstab)

    bool ReadDefaultFstab(Fstab* fstab) {
        fstab->clear();
        // 调用ReadFstabFromDt(fstab, false)重载函数
        ReadFstabFromDt(fstab, false /* verbose */);
    
        std::string default_fstab_path;
        // Use different fstab paths for normal boot and recovery boot, respectively
        if (access("/system/bin/recovery", F_OK) == 0) {
            default_fstab_path = "/etc/recovery.fstab";
        } else {  // normal boot
            default_fstab_path = GetFstabPath();
        }
    
    	// 添加正常启动normal boot的路径中的Fst文件
        Fstab default_fstab;
        if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
            for (auto&& entry : default_fstab) {
                fstab->emplace_back(std::move(entry));
            }
        } else {
            LINFO << __FUNCTION__ << "(): failed to find device default fstab";
        }
    
        return !fstab->empty();
    }
    
    • 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

    4.总结

    这里画一张流程图
    2022.8.1 22:59身体不适,等身体好了再画

  • 相关阅读:
    Leetcode232.用栈实现队列
    云服务器租用价格表概览_阿里云腾讯云华为云
    Git问题汇总
    原来我真的不懂Spring
    Radare2 框架介绍及使用
    字符串的简单介绍和字符串的大小比较
    sql分段统计
    打印编译程序固件的环境信息
    string类的常用接口说明
    【从零开始学习 SystemVerilog】2.4、SystemVerilog 数据类型—— Enumeration(枚举)
  • 原文地址:https://blog.csdn.net/stephen_curry300/article/details/126110983