本文转自android官网,原文地址:https://source.android.google.cn/devices/storage

1、概览

1.1、存储

Android 一直在不断发展,可支持各种存储设备类型和功能。所有 Android 版本均支持配有传统存储(包括便携式存储和内置存储)的设备。便携式存储是指物理介质(如 SD 卡或 USB 设备),用于进行临时数据传输/文件存储。物理介质可以随设备一起保留更长时间,但并非固定在设备上,可以移除。自 Android 1.0 开始,SD 卡已可用作便携式存储;Android 6.0 增加对 USB 的支持。“内置”存储可通过将部分内部存储暴露于模拟层来实现存储,并且从 Android 3.0 开始便已支持此功能。

从 Android 6.0 开始,Android 支持可合并的存储设备,这种存储设备是指可以像内部存储设备那样进行加密和格式化的物理介质(例如 SD 卡或 USB 设备)。移动存储设备可存储各类应用数据。

1.2、权限

采用各种 Android 权限保护对外部存储设备的访问。从 Android 1.0 开始,采用 WRITE_EXTERNAL_STORAGE 权限保护写入访问。从 Android 4.1 开始,采用 READ_EXTERNAL_STORAGE 权限保护读取访问。

从 Android 4.4 开始,外部存储设备上的文件所有者、组和模式根据目录结构合成。这样,应用可在外部存储设备上管理其特定文件包的目录,而无需获得广泛的 WRITE_EXTERNAL_STORAGE 权限。例如,文件包名称为 com.example.foo 的应用现在可以自由访问外部存储设备上的 Android/data/com.example.foo/,没有权限限制。通过将原始存储设备封装在 FUSE 守护进程中,可实现此类合成权限。

1.3、运行时权限

Android 6.0 引入了一种新的运行时权限模式,在该模式中,应用可在运行时根据需要请求功能。由于新模式包含 READ/WRITE_EXTERNAL_STORAGE 权限,因此平台需要动态授予存储访问权限,而不会终止或重新启动已运行的应用。通过维护所有安装存储设备的三个不同视图可实现该模式:

  • /mnt/runtime/default 是向无特殊存储权限的应用以及 adbd 和其他系统组件所在的根命名空间显示。
  • /mnt/runtime/read 是向具有 READ_EXTERNAL_STORAGE 的应用显示
  • /mnt/runtime/write 是向具有 WRITE_EXTERNAL_STORAGE 的应用显示

在 Zygote 进行 fork 操作时,我们会为各运行应用创建装载命名空间,并将相应的初始视图挂载到位。稍后,当授予运行时权限时,vold 将跳转到已运行应用的装载命名空间,并将升级后的视图挂载到位。请注意,权限降级定会导致应用被终止。

用于实现此特性的 setns() 功能至少需要运行 Linux 3.8,但补丁程序已反向移植至 Linux 3.4。PermissionsHostTestCTS 测试可用于验证内核行为是否正确。

在 Android 6.0 中,第三方应用无权访问 sdcard_r 和 sdcard_rw GID。相反,访问通过仅为该应用装载适当的运行时视图来控制。系统会使用 everybody GID 来阻止用户间交互。

2、传统存储设备

Android 支持采用传统存储的设备,它被定义为具有不可变 POSIX 权限类和模式且不区分大小写的文件系统。传统存储设备的概念包括模拟和便携式存储设备。便携式存储设备指的是系统未合并的任何外部存储设备,因此,未经格式化、加密或绑定到特定设备。由于传统的外部存储设备对存储的数据提供最低限度的保护,因此系统代码不应将敏感数据存储到外部存储设备中。具体来说,只能将配置和日志文件存储到可为其提供妥善保护的内部存储设备中。

2.1、多用户外部存储设备

从 Android 4.2 开始,设备可以支持多用户,且外部存储设备必须满足以下限制条件:

  • 每个用户都必须有各自的独立主要外部存储设备,且不得访问其他用户的主要外部存储设备。
  • /sdcard 路径必须根据运行进程的用户身份解析到特定于该用户的正确主要外部存储设备。
  • Android/obb 目录中较大的 OBB 文件的存储可作为优化在多个用户之间共享。
  • 次要外部存储设备不得让应用写入内容,除非在特定于软件包的目录中获得合成的权限。

此功能的默认平台实现利用 Linux 内核命名空间,为每个 Zygote 所派生的进程创建独立的装载表,然后使用绑定装载向私有命名空间提供特定于用户的正确主要外部存储设备。

启动时,系统会在 EMULATED_STORAGE_SOURCE(隐藏于应用中)装载一个模拟的外部存储设备 FUSE 守护进程。在 Zygote 派生之后,它会将特定于用户的相应子目录从 FUSE 守护进程下绑定装载到 EMULATED_STORAGE_TARGET,以便外部存储设备路径正确解析应用。由于应用缺少其他用户存储的可访问装载点,它们只能供启动的用户访问存储设备。

该实现还使用共享的子树内核功能将装载事件从默认的根命名空间传播到应用命名空间中,从而确保 ASEC 容器和 OBB 装载等功能继续正常运行。它通过将 rootfs 装载为共享模式,然后在每个 Zygote 命名空间都创建好后重新将其装载为从属模式来实现。

2.2、多个外部存储设备

从 Android 4.4 开始,多个外部存储设备通过 Context.getExternalFilesDirs()Context.getExternalCacheDirs() 和 Context.getObbDirs() 提供给开发者。

通过这些 API 提供的外部存储设备必须是设备的半永久部件(如电池盒中的 SD 卡插槽)。开发者希望存储在这些位置的数据可供长期使用。因此,瞬态存储设备(如 USB 大容量存储驱动器)不应通过这些 API 提供。

WRITE_EXTERNAL_STORAGE 权限必须仅向在设备上的主要外部存储设备授予写入权限。不允许应用写入次要外部存储设备,除非在特定于软件包的目录中获得合成的权限。以这种方式限制写入可确保系统在应用被卸载时将文件清理干净。

2.3、USB 媒体支持

Android 6.0 支持只需短时间内连接到设备的便携式存储设备,如 U 盘。当用户插入新的便携式设备时,该平台会显示一条通知,以让用户复制或管理相应设备上的内容。

在 Android 6.0 中,任何未合并的设备均被视为便携式设备。由于便携式存储设备只能短时间连接到设备,因此 Android 平台会避免执行媒体扫描之类的繁重操作。第三方应用必须通过存储访问框架与便携式存储设备中的文件进行交互;出于隐私和安全考虑,明确禁止直接访问。

3、可合并的存储设备

Android 一直都支持外部存储配件(如 SD 卡),但由于这些配件存在预期的无常性,以及传统外部存储设备只受最低限度的数据保护,因此这些配件一直以来仅限于进行简单的文件存储。Android 6.0 推出了合并外部存储媒介(使其可以像内部存储设备一样使用)的功能。

注意:在运行 Android 7.0-8.1 的设备上,文件级加密 (FBE) 无法用于可合并的存储设备。在使用 FBE 的设备上,必须将新添加的存储媒介(例如 SD 卡)用作传统存储设备。

运行 Android 9 及更高版本的设备可以使用可合并的存储设备和 FBE。

当合并外部存储媒介时,系统将对其进行格式化和加密处理,以便一次只在一台 Android 设备上使用。由于该媒介与合并它的 Android 设备紧密关联,因此可以安全地为所有用户存储应用和私密数据。

当用户将新的存储媒介(如 SD卡)插入到可合并的位置时,Android 会询问他们想要如何使用该媒介。他们可以选择合并该媒介,这样的话,系统会对该媒介进行格式化和加密处理,或者也可以继续按原样将其用于简单的文件存储。如果他们选择合并媒介,平台会询问是否将主要共享存储内容(通常装载在 /sdcard 上)迁移到新合并的媒介上,从而腾出宝贵的内部存储空间。不同于因使用 MBR 而限制为 2TB 的传统存储设备,可合并的存储设备使用 GPT,因而文件存储空间上限约为 9ZB。

只有当开发者通过 android:installLocation 属性指示提供支持时,才能将应用放置在合并的存储媒介上。新安装的受支持的应用将自动放置在具有最多可用空间的存储设备上,用户可以在“设置”应用中在存储设备之间移动支持的应用。移动到已合并媒介的应用在媒介弹出时被记住,并在重新插入媒介时返回弹出前的状态。

3.2、安全性

平台为每个合并的设备随机生成加密密钥,该密钥存储在 Android 设备的内部存储设备上。这样可以有效地使得合并的媒介与内部存储设备一样安全。密钥与合并的设备(基于合并的分区 GUID)相关联。合并的设备使用通过 aes-cbc-essiv:sha256 算法和 128 位密钥大小配置的 dm-crypt 进行加密。

合并设备的磁盘布局紧密对应内部数据分区,包括 SELinux 标签等。当在 Android 设备上支持多用户时,合并的存储设备也通过与内部存储设备相同的隔离级别支持多用户。

由于合并的存储设备的内容与合并该设备的 Android 设备密切相关,加密密钥不应可以从父设备中进行提取,因此该存储设备无法装载到其他位置。

内容模式的默认加密算法是 aes-256-xts,而文件名的默认加密算法是 aes-256-heh。您可以通过分别更改属性 ro.crypto.volume.contents_mode 和 ro.crypto.volume.filenames_mode 的值(更改方式为在 device.mk 中设置 PRODUCT_PROPERTY_OVERRIDES)来更改这些设置。

如果您的内核不支持 HEH 文件名加密,您可以通过将以下内容添加到 device.mk 来改用 CTS 模式:

PRODUCT_PROPERTY_OVERRIDES += \
ro.crypto.volume.filenames_mode=aes-256-cts

3.4、性能和稳定性

应该只考虑合并位于稳定位置(如电池盒内或防护盖后面的插槽)的外部存储媒介,以避免意外的数据丢失或损坏。尤其是,绝不应该考虑合并连接到手机或平板电脑的 USB 设备。一种常见的例外情况是连接到电视类设备的外部 U 盘,因为整个电视机通常安装在一个稳定的位置。

当用户合并新的存储设备时,平台将运行基准测试,并将其性能与内部存储设备进行比较。如果所合并设备的速度明显慢于内部存储设备,则平台将警告用户体验可能会受到影响。此基准根据常用 Android 应用的实际 I/O 行为得出。目前,AOSP 实现只会在超出单个阈值时警告用户,但是设备制造商可以进一步做出调整,例如如果存储卡运行非常缓慢,则完全拒绝合并。

合并的设备必须使用支持 POSIX 权限和扩展属性(如 ext4 或 f2fs)的文件系统进行格式化。为了获得最佳性能,建议基于闪存的存储设备使用 f2fs 文件系统。

在执行周期性空闲维护时,平台将向合并的媒介发出 FI_TRIM(就像对待内部存储设备那样)。目前的 SD 卡规范不支持 DISCARD 命令;不过,内核会回退到使用 ERASE 命令,SD 卡固件可以选择使用该命令来实现优化目的。

3.5、修正双重加密

在 Android 8.x 及更低版本中,可合并的存储设备不支持 FBE。带有可合并的存储设备的所有现有设备都使用全盘加密(FDE)。在 Android 9 中,可合并的存储设备支持 FBE。但在默认情况下,文件内容已进行双重加密,因为可合并的存储设备具有 FDE 和 FBE 层。默认情况下,这两个层都会加密文件内容,这会降低设备性能。要解决双重加密问题并提高设备性能,请执行以下操作:

  1. 将这些补丁程序添加到内核中。
  2. 要使用 vold 传达此项更改,请将以下内容添加到 device.mk 中:
PRODUCT_PROPERTY_OVERRIDES += ro.crypto.allow_encrypt_override=true

如果您设置了此项内容,但内核修补程序不存在,则可合并的存储设备将无法工作,并且 vold 日志将包含一条错误消息(提示它无法创建 dm 设备)。

注意:请勿使用 OTA 更新更改此标记,因为这会更改可合并的存储设备的磁盘格式。

3.6、测试

要测试可合并的存储设备是否正常工作,请运行此 CTS 测试:

cts-tradefed run commandAndExit cts-dev \-m CtsAppSecurityHostTestCases \-t android.appsecurity.cts.AdoptableHostTest

要在设备没有内置插槽或正使用 USB 连接器实现有效的 adb 连接时验证 U 盘和 SD 卡的行为,请使用:

adb shell sm set-virtual-disk true

4、设备配置

外部存储空间由 vold init 服务和 StorageManagerService 系统服务共同管理。外部实体存储卷的装载由 vold 处理:通过执行分阶段操作准备好媒体,然后再将其提供给应用。

注意:在 Android 8.0 中,MountService 类已更名为 StorageManagerService

4.1、文件映射

对于 Android 4.2.2 及更早版本,特定于设备的 vold.fstab 配置文件定义从 sysfs 设备到文件系统装载点的映射,每行都遵循以下格式:

dev_mount <label> <mount_point> <partition> <sysfs_path> [flags]
  • label:卷的标签。
  • mount_point:要装载卷的文件系统路径。
  • partition:分区编号(从 1 开始);如果是第一个可用分区,则为“auto”。
  • sysfs_path:可以提供此装载点的设备的一个或多个 sysfs 路径。这些路径用空格分开,且必须都以 / 开头。
  • flags:可选的逗号分隔标记列表,不能包含 /。可能的值包括 nonremovable 和 encryptable

对于 Android 4.3 及更高版本,init、vold 和 recovery 所使用的各种 fstab 文件在 /fstab.<device> 文件中进行统一。对于由 vold 管理的外部存储卷,条目应采用以下格式:

<src> <mnt_point> <type> <mnt_flags> <fs_mgr_flags>
  • src:sysfs(通常在 /sys 下装载)下可以提供装载点的设备的路径。路径必须以 / 开头。
  • mount_point:要装载卷的文件系统路径。
  • type:卷上的文件系统类型。如果是外部卡,则通常为 vfat
  • mnt_flagsVold 会忽略此字段,应将其设置为 defaults
  • fs_mgr_flagsVold 会忽略此字段中不包含 voldmanaged= 标记的统一的 fstab 中的任何行。该标记必须后跟描述卡的标签,以及分区号或字词 auto。例如:voldmanaged=sdcard:auto。其他可能的标记有 nonremovableencryptable=sdcardnoemulatedsd 和 encryptable=userdata

4.2、配置详情

框架层级以及更高层级的外部存储交互通过 StorageManagerService 来处理。由于 Android 6.0 中进行了配置更改(例如移除了 storage_list.xml 资源叠加层),因此配置详情分成了两类。

4.2.1、Android 5.x 及更低版本

设备专属的 storage_list.xml 配置文件(通常通过 frameworks/base 叠加层提供)定义存储设备的属性和限制。<StorageList> 元素包含一个或多个 <storage> 元素,其中一个元素应被标记为主元素。<storage> 属性包括:

  • mountPoint:此装载的文件系统路径。
  • storageDescription:描述此装载的字符串资源。
  • primary:如果此装载是主要外部存储,则为 true。
  • removable:如果此装载包含可移动媒体(如物理 SD 卡),则为 true。
  • emulated:如果此装载由可能使用 FUSE 守护进程的内部存储模拟和支持,则为 true。
  • mtp-reserve:MTP 应为免费存储预留的存储 MB 数。仅在装载被标记为模拟时使用。
  • allowMassStorage:如果此装载可通过 USB 大容量存储设备共享,则为 true。
  • maxFileSize:最大文件大小(以 MB 为单位)。

设备可以通过模拟由内部存储支持的文件系统(不区分大小写,无需权限)来提供外部存储。system/core/sdcard 中的 FUSE 守护进程提供一个可能的实现,可添加为特定于设备的 init.rc 服务:

# virtual sdcard daemon running as media_rw (1023)
service sdcard /system/bin/sdcard <source_path> <dest_path> 1023 1023class late_start

其中,source_path 为提供支持的内部存储,dest_path 为目标装载点。

配置特定于设备的 init.rc 脚本时,必须将 EXTERNAL_STORAGE 环境变量定义为主要外部存储的路径。/sdcard 路径也必须通过符号链接解析到同一位置。如果设备在平台更新之间调整外部存储的位置,则应创建符号链接,以便旧的路径继续发挥作用。

4.2.2、Android 6.0及以上

目前,存储子系统的配置集中在特定于设备的 fstab 文件中,并且移除了一些历史静态配置文件/变量,以支持更多动态行为:

  • storage_list.xml 资源叠加层已被移除,框架已不再使用该叠加层。现在,存储设备在被 vold 检测到时动态配置。
  • EMULATED_STORAGE_SOURCE/TARGET 环境变量已被移除,Zygote 已不再使用这些变量来配置特定于用户的装载点。相反,用户分离现在由特定于用户的 GID 强制执行,主要共享存储由 vold 在运行时装载到位。
    • 开发者可以根据其使用情形继续动态或静态构建路径。在路径中包含 UUID 可识别每个卡,以便为开发者提供更清晰的位置。(例如,/storage/ABCD-1234/report.txt 明显是与 /storage/DCBA-4321/report.txt不同的文件。)
  • 硬编码的 FUSE 服务已从特定于设备的 init.rc 文件中移除,在需要时将从 vold 动态派生。

除了这些配置更改之外,Android 6.0 还包含可合并的存储设备的概念。对于 Android 6.0 设备,任何未被合并的物理媒体都被视为便携式设备。

可合并的存储设备

要在 fstab 中表示可合并的存储设备,请在 fs_mgr_flags 字段中使用 encryptable=userdata 属性。典型定义如下:

/devices/platform/mtk-msdc.1/mmc_host*           auto      auto     defaults
voldmanaged=sdcard1:auto,encryptable=userdata

合并存储设备时,该平台会擦除内容并写入定义两个分区的 GUID 分区表:

  • 一个较小的空 android_meta 分区,预留以备将来使用的。分区类型 GUID 为 19A710A2-B3CA-11E4-B026-10604B889DCF。
  • 一个较大的 android_ext 分区,使用 dm-crypt 加密并使用 ext4 或 f2fs(取决于内核功能)格式化。分区类型 GUID 为 193D1EA4-B3CA-11E4-B075-10604B889DCF。

便携式存储设备

在 fstab 中,具有 voldmanaged 属性的存储设备默认被视为便携式设备,除非定义了其他属性(如 encryptable=userdata)。例如,典型的 USB OTG 设备的定义如下:

/devices/*/xhci-hcd.0.auto/usb*             auto            auto    defaultsvoldmanaged=usb:auto

该平台在装载之前使用 blkid 检测文件系统类型,用户可以选择在文件系统不受支持时将媒体格式化。

5、更快地获得存储统计信息

在早期版本的 Android 中,系统会遍历特定应用拥有的所有文件以测量磁盘使用情况。此手动测量过程可能需要几分钟的计算时间,然后才能在“设置”中向用户显示结果。

此外,清除缓存数据文件的内部算法仅查看所有应用的修改时间。这使得恶意应用可以通过将修改时间设置在遥远的未来以使其不当地拥有高于其他应用的优先级,从而降低整体用户体验。

为了提升这些体验,Android 8.0 会询问是否利用 ext4 文件系统的“配额”支持来几乎即时地返回磁盘使用情况统计信息。此配额功能还可以防止任何单个应用使用超过 90% 的磁盘空间或 50% 的索引节点,从而提高系统的稳定性。

5.1、实现

配额功能是 installd 默认实现的一部分。 在特定文件系统上启用配额功能后,installd 会自动使用该功能。如果在所测量的块设备上未启用或不支持配额功能,则系统将自动且透明地恢复手动计算方式。

要在特定块设备上启用配额支持,请执行以下操作:

  1. 启用 CONFIG_QUOTACONFIG_QFMT_V2 和 CONFIG_QUOTACTL 内核选项。
  2. 将 quota 选项添加到 fstab 文件中的 userdata 分区:
/dev/block/platform/soc/624000.ufshc/by-name/userdata   /data
ext4    noatime,nosuid,nodev,barrier=1,noauto_da_alloc
latemount,wait,check,formattable,fileencryption=ice,quota

您可以在现有设备上安全地启用或停用 fstab 选项。在更改 fstab 选项后的第一次启动过程中,fsmgr 会强制执行 fsck 传递以更新所有配额数据结构,这可能会导致首次启动时间稍长。后续启动不会受到影响。

配额支持仅在 ext4 和 Linux 3.18 或更高版本上进行了测试。如果在其他文件系统或者较旧的内核版本上启用,设备制造商将负责测试和检查统计信息的正确性。

不需要特殊硬件支持。

5.2、验证

StorageHostTest 下包含 CTS 测试,它们可使用用于测量磁盘使用情况的公共 API。无论是否启用了配额支持,这些 API 都应返回正确的值。

5.3、调试

测试应用通过为空间大小使用唯一的质数来仔细分配磁盘空间区域。调试这些测试时,请使用此质数来确定任何差异的原因。例如,如果增量为 11MB 的测试失败了,请检查 Utils.useSpace() 方法以查看 11MB blob 是否存储在 getExternalCacheDir() 中。

还有一些可能对调试有用的内部测试,但它们可能需要停用安全检查才能通过:

runtest -x frameworks/base/services/tests/servicestests/ \src/com/android/server/pm/InstallerTest.java
adb shell /data/nativetest64/installd_utils_test/installd_utils_test
adb shell /data/nativetest64/installd_cache_test/installd_cache_test
adb shell /data/nativetest64/installd_service_test/installd_service_test

android下存储设备的使用相关推荐

  1. android设备udid,Android下获取设备唯一标识(UDID, DeviceID)

    android下获取设备唯一标识原本非常简单(至少不会像iOS一样禁用这个,禁用那个),但是由于设备的多样性需要考虑的东西也对应复杂起来. 先附上完整代码 protected static final ...

  2. Android外设存储设备的访问及测试

    在Android中如何访问外设存储设备?如:Nand 和 Flash卡.我们对7寸2.3版本和10寸2.1版本的Android进行了测试.测试代码和结果如下: 测试代码: Application Em ...

  3. android获取存储设备根目录,浅谈android获取存储目录(路径)的几种方式和注意事项...

    通常, 我们创建文件/目录, 或者存储图片什么的, 我们都需要拿到手机的存储路径, 现在我们就来看一下获取手机存储路径的几种方式(作为工具类方法调用即可): 第一种: 获取 /storage/emul ...

  4. android设备udid,Android下获取设备唯一标识(UDID, DeviceID...)

    http://blog.csdn.net/langresser_king/article/details/9373051 1.正常情况下可以通过((TelephonyManager) s_instan ...

  5. android获取存储设备根目录,Android ndk获取手机内部存储卡的根目录方法

    如下所示: jclass envcls = env->FindClass("android/os/Environment"); //获得类引用 if (envcls == n ...

  6. android模拟打印机服务,Android下的POS打印机调用的简单实现

    本文基于GP58系列,它可以兼容ESC/POS指令集,对EPSON的打印机通用. Android下的设备调试,如果设备提供了驱动,按照厂家的驱动调试即可:设备未提供驱动,只能按照通用的方法进行调试.这 ...

  7. Android打印机--没有设备驱动sdk,自己实现USB打印功能

    Android下的设备调试,如果设备提供了驱动,按照厂家的驱动调试即可:设备未提供驱动,只能按照通用的方法进行调试. 对于智能POS.收银机以及其他打印设备,如果厂商不提供打印相关sdk,那么打印功能 ...

  8. Linux系统卸载USB存储设备失败导致kworker进程CPU占用异常问题的解决办法

    问题描述 U盘或移动硬盘umount弹出设备失败,后台会出现一个kworker进程,疯狂占用CPU资源,其中: top命令直接可以看到kworker进程: htop命令默认隐藏了内核进程,需要在按F2 ...

  9. 【Android 逆向】Android 系统文件分析 ( 外部存储设备文件 | sbin 命令程序目录 | dev 字符设备目录 )

    文章目录 一.外部存储设备文件 二.sbin 命令程序目录 三.dev 字符设备目录 一.外部存储设备文件 /sdcard 文件是一个链接 , 相当于快捷方式 , 其实际的目录是 /storage/s ...

最新文章

  1. 程序员语言也有鄙视链!某美团程序员爆料:筛选简历时,用go语言的基本不看!网友:当韭菜还当出优越感了!...
  2. 苹果汽车高管几乎流失殆尽,一年损失7人,新年又有人被Meta挖走
  3. $.ajax() 方法的理解
  4. 高性能的关键:Spring MVC的异步模式
  5. 二叉树的基本操作_二叉树的遍历
  6. Dell R710服务器磁盘恢复数据库一例(记录)
  7. 数据库系统故障相关知识笔记
  8. php pdo mysql类源码_php pdo数据库类(提取自微擎的pdo方式处理数据库类库)
  9. nginx配置url重定向-反向代理
  10. 计算机组成原理疑难知识点
  11. 斯托克斯定理,de Rham上同调习题
  12. 一个很好用的移动端Lightbox特效插件(一)
  13. java创建文件的路径_JAVA如何指定路径创建文件?
  14. 如何压缩PDF文件、图片转PDF、PDF合并拆分!!!!
  15. 改变磁盘格式gpt_改变游戏规则或结束游戏? 准备好参加GPT-3
  16. 牛客练习赛101 B-荒神在此
  17. ionic中android的返回键
  18. (转)《一定要争气》
  19. Spring自定义AOP切面
  20. 让你的发动机与NXP Kinetis汽车套件一起运行---凯利讯半导体

热门文章

  1. php 8进制,PHP中的进制转换
  2. 简单模拟快递单号查询页面展示
  3. 简历重点stat法则
  4. 罗辑思维时间的朋友2021跨年演讲:长大以后
  5. java 计算体积_Java抽象类计算体积
  6. 文献管理软件Zotero
  7. 数学学习与研究杂志数学学习与研究杂志社数学学习与研究编辑部2023年第3期目录
  8. DBM、GDBM与C语言跨平台代码研究
  9. ZUC祖冲之密码算法
  10. QT+ffmpeg+SDL2播放视频流