当前位置: 首页 > news >正文

Android Native 之 文件系统挂载

一、文件系统挂载流程概述

二、文件系统挂载流程细节

1、Init启动阶段

众所周知,init进程为android系统的第一个进程,也是native世界的开端,要想让整个android世界能够稳定的运行,文件系统的创建和初始化是必不可少的,这个过程需要在android世界的前面。

//aosp/system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {LOG(INFO) << "init first stage started!";//.....bool created_devices = false;if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {if (!IsRecoveryMode()) {//第一步:创建设备created_devices = DoCreateDevices();if (!created_devices) {LOG(ERROR) << "Failed to create device nodes early";}}StartConsole(cmdline);}//......//第二步:挂载设备if (!DoFirstStageMount(!created_devices)) {LOG(FATAL) << "Failed to mount required partitions early ...";}//.....
}
//aosp/system/core/init/first_stage_mount.cpp
// Public functions公共函数
// Creates devices and logical partitions from storage devices
bool DoCreateDevices() {auto fsm = FirstStageMount::Create();if (!fsm.ok()) {LOG(ERROR) << "Failed to create FirstStageMount: " << fsm.error();return false;}//来创建设备/即初始化磁盘逻辑分区return (*fsm)->DoCreateDevices();
}
// Mounts partitions specified by fstab in device tree.
bool DoFirstStageMount(bool create_devices) {// Skips first stage mount if we're in recovery mode.if (IsRecoveryMode()) {LOG(INFO) << "First stage mount skipped (recovery mode)";return true;}auto fsm = FirstStageMount::Create();if (!fsm.ok()) {LOG(ERROR) << "Failed to create FirstStageMount " << fsm.error();return false;}if (create_devices) {if (!(*fsm)->DoCreateDevices()) return false;}//来进行文件系统的挂载return (*fsm)->DoFirstStageMount();
}
//void SetInitAvbVersionInRecovery() 第三个public函数,看起来是和recovery有关系的

Init进程的通过FirstStageMount::Create()来拿到一个fsm对象,然后依次调用fsm的DoCreateDevices和DoFirstStageMount来初始化挂载文件系统。

1.1 FirstStageMount::Create读取fstab配置表

此步骤主要是读取fstab分区配置表,具体实现逻辑其实移交给了fs_mgr

//aosp/system/core/init/first_stage_mount.cpp
using android::fs_mgr::ReadDefaultFstab;
using android::fs_mgr::ReadFstabFromDt;
Result<std::unique_ptr<FirstStageMount>> FirstStageMount::Create() {//读取fstab配置表,此表配置了各个目录支持的文件系统相关配置auto fstab = ReadFirstStageFstab();if (!fstab.ok()) {return fstab.error();}return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));
}
static Result<Fstab> ReadFirstStageFstab() {Fstab fstab;//从DT里面获取,DT好像跟内核有关系,没有具体研究if (!ReadFstabFromDt(&fstab)) {//读取默认的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;
}

fs_mgr被编译成为静态库lib_fs_mgr,这部分逻辑其实就是读取fstab.ini配置文件并进行解析:

//aosp/system/core/fs_mgr/fs_mgr_fstab.cpp
// Loads the fstab file and combines with fstab entries passed in from device tree.
bool ReadDefaultFstab(Fstab* fstab) {fstab->clear();ReadFstabFromDt(fstab, false /* verbose */);std::string default_fstab_path;// Use different fstab paths for normal boot and recovery boot, respectivelyif ((access("/sbin/recovery", F_OK) == 0) || (access("/system/bin/recovery", F_OK) == 0)) {//recovery模式下读取/etc/recovery.fstabdefault_fstab_path = "/etc/recovery.fstab";} else { //正常模式下读取类似于/odm/etc/fstab.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();
}
// Return the path to the fstab file.  There may be multiple fstab files; the
// one that is returned will be the first that exists of fstab.<fstab_suffix>,
// fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for
// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
// the first stage ramdisk during early boot.  Previously, the first stage
// ramdisk's copy of the fstab had to be located in the root directory, but now
// the system/etc directory is supported too and is the preferred location.
std::string GetFstabPath() {for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {std::string suffix;if (!fs_mgr_get_boot_config(prop, &suffix)) continue;for (const char* prefix : {// late-boot/post-boot locations"/odm/etc/fstab.", "/vendor/etc/fstab.",// early boot locations"/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.","/fstab.", "/first_stage_ramdisk/fstab."}) {std::string fstab_path = prefix + suffix;if (access(fstab_path.c_str(), F_OK) == 0) {return fstab_path;}}}return "";
}

如下Android 14的手机的开机日志,在init阶段来读取fstab配置表的打印:这里的dt没有配置fstab,默认路径没有任何打印,但是可以了解到libfs_mgr的入口

1.2 fstab文件是什么样子的?

 android系统的文件系统相关参数定义被统一放在fstab.in里面,从上面的流程可以了解到fs_mgr会去读取fstab.*文件,并根据此文件配置的内容去逐一挂载所有的分区,那么它到底长什么样子的呢?

首先cat /vendor/etc/fstab.mtxxxx内容如下:

D50:/vendor/etc # cat fsta
fstab.enableswap  fstab.mt6765      fstab.mt8768
D50:/vendor/etc # cat fstab.mt6765
# 1 "vendor/mediatek/proprietary/hardware/fstab/mt6765/fstab.in.mt6765"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 341 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "vendor/mediatek/proprietary/hardware/fstab/mt6765/fstab.in.mt6765" 2
# 145 "vendor/mediatek/proprietary/hardware/fstab/mt6765/fstab.in.mt6765"
system /system ext4 ro wait,avb=vbmeta_system,logical,first_stage_mount,avb_keys=/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey,slotselect
system_ext /system_ext ext4 ro wait,avb=vbmeta_system,logical,first_stage_mount,avb_keys=/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey,slotselectvendor /vendor ext4 ro wait,avb,logical,first_stage_mount,slotselectproduct /product ext4 ro wait,avb,logical,first_stage_mount,slotselect
# 170 "vendor/mediatek/proprietary/hardware/fstab/mt6765/fstab.in.mt6765"
/dev/block/by-name/md_udc /metadata ext4 noatime,nosuid,nodev,discard wait,check,formattable,first_stage_mount/dev/block/by-name/userdata /data f2fs noatime,nosuid,nodev,discard,noflush_merge,reserve_root=134217,resgid=1065,inlinecrypt latemount,wait,check,quota,reservedsize=128M,formattable,resize,,checkpoint=fs,fileencryption=aes-256-xts:aes-256-cts:v2,keydirectory=/metadata/vold/metadata_encryption/dev/block/by-name/protect1 /mnt/vendor/protect_f ext4 noatime,nosuid,nodev,noauto_da_alloc,commit=1,nodelalloc wait,check,formattable
/dev/block/by-name/protect2 /mnt/vendor/protect_s ext4 noatime,nosuid,nodev,noauto_da_alloc,commit=1,nodelalloc wait,check,formattable
/dev/block/by-name/nvdata /mnt/vendor/nvdata ext4 noatime,nosuid,nodev,noauto_da_alloc,commit=1,nodelalloc wait,check,formattable
/dev/block/by-name/nvcfg /mnt/vendor/nvcfg ext4 noatime,nosuid,nodev,noauto_da_alloc,commit=1,nodelalloc wait,check,formattable/dev/block/by-name/persist /mnt/vendor/persist ext4 noatime,nosuid,nodev,noauto_da_alloc,commit=1,nodelalloc wait,check,formattable/devices/platform/externdevice* auto auto defaults voldmanaged=sdcard1:auto,encryptable=userdata/devices/platform/mt_usb* auto vfat defaults voldmanaged=usbotg:auto/dev/block/by-name/frp /persistent emmc defaults defaults/dev/block/by-name/nvram /nvram emmc defaults defaults
/dev/block/by-name/proinfo /proinfo emmc defaults defaults
/dev/block/by-name/lk /bootloader emmc defaults defaults
/dev/block/by-name/lk2 /bootloader2 emmc defaults defaults
/dev/block/by-name/para /misc emmc defaults defaults/dev/block/by-name/boot /boot emmc defaults first_stage_mount,nofail,slotselect
# 210 "vendor/mediatek/proprietary/hardware/fstab/mt6765/fstab.in.mt6765"
/dev/block/by-name/vbmeta_vendor /vbmeta_vendor emmc defaults first_stage_mount,nofail,slotselect
/dev/block/by-name/vbmeta_system /vbmeta_system emmc defaults first_stage_mount,nofail,slotselect,avb=vbmeta/dev/block/by-name/logo /logo emmc defaults defaults
/dev/block/by-name/expdb /expdb emmc defaults defaults
/dev/block/by-name/seccfg /seccfg emmc defaults defaults/dev/block/by-name/tee1 /tee1 emmc defaults defaults
/dev/block/by-name/tee2 /tee2 emmc defaults defaults/dev/block/by-name/scp1 /scp1 emmc defaults defaults
/dev/block/by-name/scp2 /scp2 emmc defaults defaults/dev/block/by-name/sspm_1 /sspm_1 emmc defaults defaults
/dev/block/by-name/sspm_2 /sspm_2 emmc defaults defaults/dev/block/by-name/md1img /md1img emmc defaults defaults
/dev/block/by-name/md1dsp /md1dsp emmc defaults defaults
/dev/block/by-name/md1arm7 /md1arm7 emmc defaults defaults
/dev/block/by-name/md3img /md3img emmc defaults defaults/dev/block/by-name/gz1 /gz1 emmc defaults defaults
/dev/block/by-name/gz2 /gz2 emmc defaults defaults/dev/block/by-name/spmfw /spmfw emmc defaults defaults/dev/block/by-name/boot_para /boot_para emmc defaults defaults
/dev/block/by-name/odmdtbo /odmdtbo emmc defaults defaults
/dev/block/by-name/dtbo /dtbo emmc defaults defaults/dev/block/by-name/vbmeta /vbmeta emmc defaults defaults
D50:/vendor/etc #

如上格式,此文件可以解析如下三部分

那么我们在源代码是如何配置的呢?MTK可以参考如下逻辑,后文详细解读各大配置参数

1.3 FirstStageMount::DoCreateDevices

//aosp/system/core/init/first_stage_mount.cpp
bool FirstStageMount::DoCreateDevices() {if (!InitDevices()) return false;// Mount /metadata before creating logical partitions, since we need to// know whether a snapshot merge is in progress.auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {//从fstab配置表中寻找/metadata分区的信息,此分区很重要存储了一些元数据和秘钥相关的东西return entry.mount_point == "/metadata";});if (metadata_partition != fstab_.end()) {//首先需要挂载/metadata分区,因为它太重要了if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {// Copies DSU AVB keys from the ramdisk to /metadata.// Must be done before the following TrySwitchSystemAsRoot().// Otherwise, ramdisk will be inaccessible after switching root.//它为什么重要,就是因为拷贝AVB Key到这个目录,详细的待后续研究CopyDsuAvbKeys();}}//创建逻辑分区if (!CreateLogicalPartitions()) return false;return true;
}

流程1:如上逻辑首先挂载了/metadata分区,为什么要先挂载它?

流程2:/metadata分区挂载流程对应日志:注意这里调用了metadata_partition函数传递了参数,所以只挂载了一个分区

流程3:创建逻辑分区,那么何为逻辑分区?从下面日志来看个人理解它类似与PC的C盘来区别于其他磁盘,因此这里的逻辑分区通常为system/vendor几个目录

如上日志对应逻辑代码如下:

1.4 FirstStageMount::DoFirstStageMount

//aosp/system/core/init/first_stage_mount.cpp
bool FirstStageMount::DoFirstStageMount() {if (!IsDmLinearEnabled() && fstab_.empty()) {// Nothing to mount.LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";return true;}//挂载分区if (!MountPartitions()) return false;    return true;
}

这里的主要流程还是去调用MountPartitions()去挂载分区,注意这里不像metadata哪里传递了参数,因此这里是根据fstab表去挂载所有其他分区,代码如下:

2、fstab文件参数解读

3、fs_mgr挂载分区

接着init的FirstStageMount::MountPartition通过fstab表来挂载单个分区,如下逻辑,在对底层设备块相关初始化之后通过fs_mgr来进行单个分区的挂载。

//aosp/system/core/init/first_stage_mount.cpp
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts, Fstab::iterator* end) {// Sets end to begin + 1, so we can just return on failure below.if (end) {*end = begin + 1;}if (!fs_mgr_create_canonical_mount_point(begin->mount_point)) {return false;}//跟底层设备块有关系,暂时没有深入研究if (begin->fs_mgr_flags.logical) {if (!fs_mgr_update_logical_partition(&(*begin))) {return false;}if (!block_dev_init_.InitDmDevice(begin->blk_device)) {return false;}}if (!SetUpDmVerity(&(*begin))) {PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'";return false;}//核心代码,通过fs_mgr去进行挂载bool mounted = (fs_mgr_do_mount_one(*begin) == 0);// Try other mounts with the same mount point.Fstab::iterator current = begin + 1;for (; current != fstab_.end() && current->mount_point == begin->mount_point; current++) {if (!mounted) {// blk_device is already updated to /dev/dm-<N> by SetUpDmVerity() above.// Copy it from the begin iterator.current->blk_device = begin->blk_device;mounted = (fs_mgr_do_mount_one(*current) == 0);}}if (erase_same_mounts) {current = fstab_.erase(begin, current);}if (end) {*end = current;}return mounted;
}
//aosp/system/core/fs_mgr/fs_mgr.cpp
// wrapper to __mount() and expects a fully prepared fstab_rec,
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {// First check the filesystem if requested.if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {LERROR << "Skipping mounting '" << entry.blk_device << "'";}auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;//步骤1:挂载前的准备工作,其实就是解析fstab分区配置的各种参数int ret = prepare_fs_for_mount(entry.blk_device, entry, mount_point);// Wiped case doesn't require to try __mount below.if (ret & FS_STAT_INVALID_MAGIC) {return FS_MGR_DOMNT_FAILED;}//步骤2:正式进行文件分区的挂载ret = __mount(entry.blk_device, mount_point, entry);if (ret) {ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;}return ret;
}

如上代码进行总结如下:

  • init最后通过libfs_mgr最后调用了fs_mgr.cpp来进行文件分区的挂载
  • 首先通过prepare_fs_for_mount来解析fstab里面配置的一系列参数
  • 最后通过__mount来进行文件分区的挂载

3.1 挂载前的准备工作prepare_fs_for_mount

总结如上代码逻辑,主要做了如下几个步骤:

  • tune_quota:Enable/disable quota support on the filesystem if needed
  • resize_fs:重置文件系统
  • check_fs:校验文件系统
  • tune_reserved_size:ext4支持
  • tune_encrypt:ext4支持
  • tune_verity:ext4支持
  • tune_casefold:ext4支持
  • tune_metadata_csum:ext4支持

3.2 挂载文件分区流程__mount

相关文章:

Android Native 之 文件系统挂载

一、文件系统挂载流程概述 二、文件系统挂载流程细节 1、Init启动阶段 众所周知&#xff0c;init进程为android系统的第一个进程&#xff0c;也是native世界的开端&#xff0c;要想让整个android世界能够稳定的运行&#xff0c;文件系统的创建和初始化是必不可少的&#xff…...

常用word python matlab快捷键

这里写自定义目录标题 WordMatlabpythonlinuxWord Matlab 1 结构体 字符串成员做索引,必须()类似python* 解包作用,转化字符串到属性类型 如果属性名存入列表 a = [“para1”] 比如stru1.para1 = [‘c’,‘d’]; 那么若要用a中para1来索引,必须要加圆括号; ==》 X Strut…...

MySQL------存储引擎和用户和授权

9.存储引擎 1.两种引擎 MyISAM和InnoDB 2.两种区别 1.事务&#xff1a; MyISAM不支持事务 2.存储文件: innodb : frm、ibd MyISAM: frm、MYD、MYI 3.数据行锁定: MyISAM不支持 4.全文索引: INNODB不支持&#xff0c;所以MYISAM做select操作速度很快 5.外键约束: MyISAM…...

react拖曳组件react-dnd的简单封装使用

分享原因 由于项目中需要使用拖曳组件(需求:全局&#xff0c;跨组件&#xff0c;跨数据)&#xff0c;我选择了react-dnd 概念 React DnD 是一组 React 高阶组件&#xff0c;我们在使用的时候只需要将目标元素进行包裹&#xff0c;就可以实现目标元素具有拖动或接受拖动的功能。…...

Excel中COUNTIF用法解析

COUNTIF 是 Excel 中一个非常实用的函数&#xff0c;用于统计满足某个条件的单元格数量。它的基本语法如下&#xff1a; 基本语法 COUNTIF(范围, 条件) 范围&#xff1a;需要统计的单元格区域&#xff0c;例如 A1:A10 或整列 A:A。 条件&#xff1a;用于判断哪些单元格需要被…...

Uniapp 页面返回不刷新?两种方法防止 onShow 触发多次请求!

目录 前言1. 变量&#xff08;不生效&#xff09;2. 延迟&#xff08;生效&#xff09; 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 在 Uniapp 中&#xff0c;使用 onShow() 钩子来监听页面显示&#xff0…...

《论数据湖技术及其应用》审题技巧 - 系统架构设计师

论题写作框架 一、考点概述 “数据湖技术及其应用”这一论题主要考察的是软件测试工程师对于前沿数据存储与处理技术的理解及其在软件开发项目中的实际应用能力。具体而言&#xff0c;该论题涵盖了以下几个核心考点&#xff1a; 软件项目管理与开发经验 &#xff1a;要求考生…...

C++蓝桥杯基础篇(八)

片头 嗨~小伙伴们&#xff0c;大家好&#xff01;今天我们一起来学习C蓝桥杯基础篇&#xff08;八&#xff09;&#xff0c;练习相关字符串的习题&#xff0c;准备好了吗&#xff1f;Are you ready? Lets go! 第1题 字符串中的数字个数 这道题&#xff0c;我们用字符数组或者…...

AI 实战 - pytorch框架基于retinaface实现face检测

pytorch框架基于retinaface实现face检测 简介模型结构MobileNet-0.25SSH结构Head结构 Anchor编解码 环境开发环境 数据简介 训练测试参考 简介 RetinaFace是在RetinaNet基础上引申出来的人脸检测框架&#xff0c;所以大致结构和RetinaNet非常像。 主要改进&#xff1a;1.Mobi…...

如何在PHP中实现API版本管理:保持向后兼容性

如何在PHP中实现API版本管理&#xff1a;保持向后兼容性 在现代Web开发中&#xff0c;API&#xff08;应用程序编程接口&#xff09;是连接前端和后端的关键桥梁。随着业务需求的不断变化&#xff0c;API的版本管理变得尤为重要。良好的版本管理策略不仅能够确保新功能的顺利引…...

Docker Compose企业示例

利用容器编排完成haproxy和nginx负载均衡架构实施 1.mkdir docker.test 2.touch haproxy.yml 3.mkdir /var/lib/docker/volumes/conf 4.dnf install haproxy -y --downloadonly --downloaddir/xixi&#xff1a;下载内容到/xixi目录下 5. rpm2cpio haproxy-2.4.22-4.el9.x8…...

TMS320F28P550SJ9学习笔记6:SCI所有寄存器__结构体寄存器方式配置 SCI通信初始化__库函数发送测试

继续学习如何使用结构体寄存器的方式配置这款单片机的外设&#xff0c;这里配置SCI通信的初始化 但SCI gpio 的初始化还是调用的库函数比较方便&#xff0c;它的发送部分页调用了库函数 有关收发方面的逻辑&#xff0c;我会在之后重新自己写一次 文章提供测试代码讲解、完整…...

详细探索如何用脚本实现M小ySQL一键安装与配置,提升运维效率!

以下是基于脚本实现MySQL一键安装与配置的详细方案&#xff0c;涵盖Linux主流系统&#xff08;CentOS/Ubuntu&#xff09;及Windows环境&#xff0c;结合自动化部署与高可用性扩展&#xff0c;旨在提升运维效率&#xff1a; 一、Linux系统&#xff08;CentOS 7.x&#xff09;一…...

无人机推流/RTMP视频推拉流:EasyDSS无法卸载软件的原因及解决方法

视频推拉流/直播点播EasyDSS平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务&#xff0c;在应用场景中可实现视频直播、点播、转码、管理、录像、检索、时移回看等。此外&#xff0c;平台还支持用户自行上传视频文件&#xff0c;也可将上传的点播…...

增删改查 数据下载 一键编辑 删除

index 首页 <template><div class"box"><el-card :style"{ width: treeButton ? 19.5% : 35px, position: relative, transition: 1s }"><el-tree v-if"treeButton" :data"treeData" :props"defaultPro…...

【Go学习实战】03-2-博客查询及登录

【Go学习实战】03-2-博客查询及登录 读取数据库数据初始化数据库首页真实数据分类查询分类查询测试 文章查询文章查询测试 分类文章列表测试 登录功能登录页面登录接口获取json参数登录失败测试 md5加密jwt工具 登录成功测试 文章详情测试 读取数据库数据 因为我们之前的数据都…...

回溯算法(C/C++)

目录 一、组合问题 组合 组合剪枝 组合总和 III​编辑 组合总和​编辑 组合总和 II 电话号码的字母组合​编辑 二、分割问题 分割回文串 复原 IP 地址 三、集合问题 子集 子集 II 非递减子序列 四、排列问题 全排列 全排列 II 五、棋盘问题 N 皇后 课程&#x…...

物联网智慧农业一体化解决方案-可继续扩展更多使用场景

在智慧农业中,从种子、施肥、灌溉、锄地、农具管理、日常照料到蔬菜档案管理,以及与客户、供应商、市场的对接,可以通过物联网(IoT)、大数据、人工智能(AI)、区块链和云计算等技术,构建一个从生产到销售的全流程数字化、智能化农业生态系统。以下是实现方案和技术路径的…...

Jackson 详解

目录 前言 Jackson 是 Java 生态中最流行的 JSON 处理库之一&#xff0c;广泛应用于 RESTful API、数据存储和传输等场景。它提供了高效、灵活的 JSON 序列化和反序列化功能&#xff0c;支持注解、模块化设计和多种数据格式&#xff08;如 XML、YAML&#xff09;。本文将详细介…...

游戏引擎学习第143天

仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾并规划今天的内容 目前&#xff0c;我们正在进行声音混合的开发。我们已经写好了声音混合器&#xff0c;并且已经实现了一些功能&#xff0c;比如声音流播放和音量插值。过去一周我们做了很多工作&#xff0c;进展非常快。不…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...