IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 安卓FirstStageMount阶段解析【连载】(一)创建设备Create -> 正文阅读

[移动开发]安卓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 ...";
    }
  	// ...
}

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();
}

可以看到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));
    }
}

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;
}

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

  • 先从设备树文件中读取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 <mount point, the line format in fstab file>.
    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,格式为:<dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\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;
}

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

3.2.1 std::string ReadFstabFromDt()

这个函数会去读取fstab文件中的值,然后返回一个字符串,这个字符串以\n分割
每一个\n分割的是不同的一个fstab_entry,其格式为<dev> <mnt_point> <type> <mnt_flags> <fsmgr_flags>\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;
}

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;
}

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

  • 将前面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;
}

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();
}

4.总结

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

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:55:38  更:2022-08-06 10:57:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 4:52:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码