自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

DaHuangXiao的博客

个人笔记

  • 博客(85)
  • 收藏
  • 关注

原创 vmalloc简单学习

vmalloc 主要用于给内核从高端内存中以页为单位分配大块内存,对应关系如下所示,主要分配 896M~1G 的物理地址空间vmalloc 的执行流程简述为:分配小块内存。实例化vmalloc内存分配器的核心数据描述符 vmap_area|vm_struct|pages,为 vmalloc 的工作提供内存基础。分配合适的虚拟内存区间 hole 从空闲红黑树找到合适大小的hole,隔离分配给 vmalloc分配物理内存。调用alloc_page系列函数,从 pcp 或者 buddy 0 阶每次

2022-05-27 13:45:25 956

原创 slub分配器学习系列之linux5.10

前言前一篇文章对 linux5.10 的 slab 分配器底层实现进行了探究与学习。进一步地,本篇文章将对 linux5.10 的 slub 分配器进行探究,对比看看两者的实现有何不同,做了哪些"必要的"改进。slub 分配器关于 slub 分配器的基本原理与 slab 分配器类似,只是相较于 slab 更快更直接以及更简单,主要对涉及的结构体进行学习,再以 kmalloc 为入口开始探究,并对比一下初始化过程的不同主要数据结构struct kmem_cache { struct kmem_ca

2022-05-25 13:23:39 373

原创 arm架构linux内核调试实战

前言偶然间,发现了一个很好用的仓,可以十分方便地进行 arm 架构的 linux 内核调试,该仓地址如下 https://github.com/cc-droid/v-kernel-qemu ,对应的解析视频在 《linux内核源码分析 【0-00】从零搭建分析调试环境》本篇笔记旨在记录一下对于该仓的使用流程,并且对其进行了一些小调整环境搭建Ubuntu安装VMware + Ubuntu20.04 ,这部分略过前置配置安装 sshd,git,vimsudo apt-get install op

2022-05-23 17:14:41 911

原创 slab分配器学习系列之linux5.10

前言前一篇文章对 linux2.6 的 slab 分配器底层实现进行了探究与学习。进一步地,本篇文章将对 linux5.10 的 slab 分配器进行探究,对比 2.6 看看两者的实现有何不同,做了哪些"必要的"改进。slab 分配器关于 slab 分配器的基本原理就不赘述了,直接从涉及的结构体进行学习,再以 kmalloc 为入口开始探究,并对比一下初始化过程的不同主要数据结构struct kmem_cache { struct array_cache __percpu *cpu_cache;

2022-05-19 14:17:09 323

原创 slab分配器学习系列之linux2.6

前言前文通过存储桶了解了 slab 分配器最简单的实现方式,在后续的 linux 版本中,slab 分配器被正式提出,并用于解决内核中小样本分配问题。笔者打算先从 2.6.18 入手进行 slab 分配器的学习主要参考资料为 《深入Linux内核架构》物理页并不是内存管理的最小单位,为了减少内部碎片,提出了 slab 分配机制。该机制其实是内存桶的复杂版,主要入口函数为 kmalloc 。其核心思想与内存桶类似,即在物理页上进一步划分连续的相同大小对象,并对此进行管理。在此使用 《简说Linux之sl

2022-05-18 16:22:21 376

原创 slab分配器学习系列之存储桶

前言打算对 slab 分配器进行学习,除了了解其实现原理,还要弄清其优点与不可替代性,并进行多个版本的对比,感受 slab 分配器的发展和优化。本篇笔记打算从 linux 0.12 版本开始学习,该版本没有实现 slab 分配器,但依然有类似的实现,其为存储桶。存储桶类似于 slab 分配器的简单版,很多问题都没考虑到,但用于感受 slab 分配器是很有必要的。这部分内容主要参考了 《Linux 内核完全注释》的 15.7 节存储桶概念首先,内存管理原本泛指对物理页的管理,但由于大部分结构体都很小

2022-05-18 14:08:13 344

原创 vscode 自定义格式化之 openharmony 代码规范

前言个人比较懒,但又必须遵守 openharmony 代码规范,因此通过修改 vscode 格式化方式来满足需求,openharmony 代码规范官方地址为 https://gitee.com/openharmony/docs/blob/master/zh-cn/contribute/%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81.mdvscode 自定义格式化经比较,vscode 中 google 的格式化模板与 openharmony C++ 代码规范要求最为接近,只是缩

2022-05-01 10:01:56 1536

原创 Linux 文件系统学习之 EROFS 源码阅读笔记

operation初始化先看看 erofs 的 inode_operation 和 file_operation 以及 address_mapping_operation 分别初始化为结构体static struct file_system_type erofs_fs_type = { .owner = THIS_MODULE, .name = "erofs", .init_fs_context = erofs_init_fs_context, .kill_

2022-04-21 19:57:40 3190

原创 linux多版本文件系统接口对比梳理

前言从 2.6 到 4.9 再而 5.10 , 文件系统的接口都发生了变化,为了理清它们之间的关系,打算写一篇笔记记录一下mount2.6sys_mount -> do_mount --- do_new_mount --- do_kern_mount --- vfs_kern_mount --- fs_type->get_sbstatic struct file_system_type ext2_fs_type

2022-04-21 18:52:29 334

原创 再次梳理linux文件系统

前言由于最近在学习文件系统相关知识。为了巩固这部分知识,想办法对其进行实践。本篇文章主要记录一下如何自定义一个文件系统。主要参考了《Linux内核探秘》——提供文件系统原理及框架说明《一个简单地文件系统》——提供较新内核的简单文件系统实现代码。全篇文章以它为例编译文件系统自定义的文件系统可以作为一个模块,通过 insmod 的方式注册到内核中。类似编译一个驱动模块,编译一个文件系统模块需要:文件系统相关代码Makefile文件系统相关代码后续再进行说明,首先关注 Makefile 。

2022-04-20 20:25:36 1011

原创 Linux内核对比学习系列(6)——文件系统

前言最近给自己定了个小目标,把 erofs 整明白。在此之前,需要将 linux 内核关于文件系统的内容进行梳理。本篇博客主要记录一下自己对文件系统的理解,并对比 linux 0.12 与 2.6 版本之间关于文件系统的实现上有何不同简述文件管理流程尽管 2.6 版本的实现相较于 0.12 复杂很多,但不同实现的本质还是不变的。以块设备为例,主要围绕如何管理文件并存储与块设备中。总述每个进程的 task_struct 维护一个文件描述符表,文件描述符表的每个元素为一个文件结构体 file。而文件描

2022-04-19 10:31:47 864

原创 Linux内核对比学习系列(5)——内存管理

前言内存管理是内核设计十分重要的一环,0.12 版本的内存管理相对简单,理解比较容易。但 2.6 版本的内存管理使用了额外的很多数据结构,需要花些时间查资料进行理解。本篇内容主要对 2.6 版本内核的内存管理方法进行记录。内存管理简述内存管理主要指的物理内存的管理,与用户空间中内存分配无关。如何将物理内存高效且有序地分配是内存管理需要考虑的问题。对于一块物理内存地址,为了有序地进行分配,最先想到的方法是将其按一定大小进行划分,如划分为物理页,每个页4kb。那么每次申请地时候,可以按页进行分配。对于每一

2022-04-12 20:48:33 974

原创 浅谈OpenHarmony之appspawn应用孵化器组件

前言在往期笔记中,通过小型系统启动日志梳理了 OHOS 的启动流程,并认识到可以从 init 进程拉起的相关服务进程进一步探究 OHOS 世界。本篇笔记则是对 appspawn 进程的初步探究其官方仓为 https://gitee.com/openharmony/startup_appspawn_lite/tree/master源码分析从参考资料可以知道,appspawn 应用孵化器组件其功能如其名,就是用来启动 OHOS app 的。appspawn 的源码其实很少,因此本篇笔记不会很长。具体地,

2022-04-10 15:50:43 1629

原创 浅谈Openharmony系统服务框架Samgr

前言在刚接触 Samgr 时笔者根本不知道其设计初衷是啥(水平较菜,刚接触C语言),尽管官方文档对其进行了介绍,但仍有种听君一席话胜读一席话的感觉。为此,笔者花了一天对其进行探究和学习,将笔记梳理并记录于本文。Samgr概念官方说明官方仓 https://gitee.com/openharmony/distributedschedule_samgr_lite 对其解释如下:由于平台资源有限,且硬件平台多样,因此需要屏蔽不同硬件架构和平台资源的不同、以及运行形态的不同,提供统一化的系统服务开发框架。

2022-04-09 20:06:29 3348 2

原创 从启动日志简单梳理Openharmony启动流程

前言出于对开源鸿蒙的好奇,笔者借助几位大佬的博文,根据小型系统的启动日志,对 Openharmony 运作流程进行了梳理。由于个人编译的是最新版本,其源码部分与参考博文中有较多出入,但实现机制是不变的。参考的博文如下:鸿蒙子系统解读-分布式任务调度篇(上)鸿蒙OS开源代码精要解读之—— 系统服务框架子系统(服务启动)OHOS3.0启动流程分析丨init阶段鸿蒙系统的启动流程v2.0编译运行笔者在 qemu 上运行 qemu-system-small 系统,日志输出如下图问题描述由于笔者

2022-04-09 19:04:32 3568

原创 Linux内核对比学习系列(4)——进程休眠与唤醒

前言进程休眠与唤醒也是内核管理的重要一部分。本是进程调度相关内容,笔者在此单拎出来进行梳理。同样的,主要对比 linux0.12 与 linux2.6 之间的差异。流程梳理自然地,让一个进程休眠,我们只需要将其状态更改为TASK_INTERRUPTIBLE或者TASK_UNINTERRUPTIBLE,接着再执行调度程序 schedule() 即可。由于调度程序只会调度状态为TASK_RUNNING的进程,因此被修改的进程不会被调度,看上去就像"休眠"了。而唤醒则更简单,只需要将待唤醒进程状态改为 TA

2022-04-08 11:02:41 1591

原创 Openharmony鸿蒙内核编译及qemu运行过程问题记录

前言该篇笔记记录鸿蒙内核编译流程,及qemu运行记录。主要根据 https://gitee.com/openharmony/device_qemu 执行。由于整个过程不是很顺利,故做此笔记。(ps:因为笔者的ubuntu环境不纯净,坑较多。纯净的环境照着文档做应该问题不大)采用的策略是,【docker编译】+【ubuntu执行qemu】注意事项纯净的Ubuntu环境,直接根据文档进行配置即可如果文档流程遇到问题,可考虑使用docker编译,具体参考 https://gitee.com/openh

2022-04-07 19:16:58 3595 1

原创 Linux内核浅析之CFS完全公平调度

前言在《Linux内核对比学习系列(3)——进程调度》一文中,笔者对 Linux2.6 版本关于进程调度流程进行了梳理。其中提及,该版本通过调度器类实现调度,提升了内核关于调度算法的拓展性。本文对其中一种调度器类,CFS完全公平调度进行浅析CFS概念试想原先通过时间片控制进程调度会有什么问题场景 1 不考虑优先级A,B,C三个进程,均分60ms,每个进程获得20ms的时间片。60ms能够保证轮执该三个进程场景 2 考虑优先级A,B,C三个进程,根据优先级,获得时间片为100ms,20ms,5m

2022-04-06 10:40:22 1658

原创 Linux内核对比学习系列(3)——进程调度

前言linux0.12与linux2.6在进程调度的实现上有很大的不同,在此进行记录Linux 0.12该版本对于进程调度算法的实现十分简单,具体实现看schedule()即可,该函数为调度入口kernel/sched.cvoid schedule(void){ int i,next,c; struct task_struct ** p; // ----------------省略检查相关代码------------------/* this is the scheduler p

2022-04-05 16:59:06 1930

原创 Linux内核对比学习系列(2)——进程管理

前言本篇文章主要对比一下linux0.12与linux2.6在进程管理上的区别进程描述符进程描述符指的task_struct结构体,用于存储所有与任务(进程)执行相关的内容,接下来从以下两个方面对比两个版本task_struct的不同通过task_struct如何管理任务(进程)task_struct如何存储Linux 0.12在fork过程的copy_process()的实现中,我们可以知道该版本对 task_struct 的管理十分简单通过get_free_page()向主存申请一

2022-04-05 11:44:03 756

原创 Linux内核对比学习系列(1)——中断

前言前段时间根据《linux内核完全注释》一书,从linux0.12入手,逐渐了解了linux最本质的内核实现原理,并将相关笔记记录于《Linux内核学习系列(xx)》。进一步地,最近开始研读《Linux内核设计与实现》,本书以linux2.6.34进行讲解。在学习的过程中,部分实现以及结构发生了较大变动,为了更好地进行对比学习,故考虑将linux2.6.34与linux0.12的异同之处进行记录。另外,不得不吐槽一下内核学习之困难。其难处主要在于难以调试,不好跟踪,接口太多,有时候不知道看哪个实现。不

2022-04-04 17:19:52 1075

原创 Linux内核学习系列(7)——execve与需求加载

前言通过本系列前几篇文章的梳理,我们对linux0.12内核功能有了初步地了解。进一步地,我们看看linux如何在前述基础上执行用户自定义程序。其中的重点为execve与需求加载。样例在进入正题之间,我们可以在linux0.12中编译并执行下面一段程序#include <unistd.h>#include <stdlib.h>int main(){ execl("/root/hello","hello",NULL); exit(0);}其中/root/hell

2022-04-01 19:34:43 842

原创 Linux内核学习系列(6)——文件系统

前言《Linux内核完全注释》中对文件系统进行了详细地解析,从文件系统组织结构到代码实现都很好理解。本篇对该块内容进行简单梳理,并通过跟踪open系统调用理解文件系统与读写文件的关系文件系统基本概念通俗地说,文件系统用来辅佐内核与设备进行交互,如读写文件,显示字符等操作。就好比作为一个图书馆,需要设计目录,分区来方便用户查询或放置图书。以往块设备打开并写入一个文件这一过程为例,我们看看内核应该怎么完成块设备分块。首先,块设备对于CPU而言也是一块地址空间,如划分内存物理页一样,应该对块设备存储空

2022-03-30 16:18:25 1635

原创 Linux内核学习系列(5)——内存管理

前言本章记录一下个人对linux0.12内存管理的理解。主要涉及物理内存页分配与回收,页表复制等具体操作。同样的,笔记仍然由上而下地进行知识点梳理,而不是单纯介绍函数及其作用。内存管理linux内存管理是对什么进行管理内核中的内存管理,主要管理的是物理内存地址空间。而用户所谓的内存管理,指的是对其当前进程线性地址空间的管理为什么要进行内存管理内核中的内存管理,是为了在分页机制基础上,实现虚拟存储用户中的内存管理,是为了合理利用线性地址空间什么时候需要内核内存管理内存管理是贯彻整个

2022-03-28 11:18:23 2381

原创 Linux内核学习系列(4)——内存映射(地址转换)

前言在上一篇fork的学习中,涉及到get_free_page函数,其与内存管理有关。在进行内存管理的源码学习过程中,发现关于内存映射,或者说是地址转换理解存在问题,经过梳理后,将笔记记录如下,也作为后续内存管理学习的知识铺垫。地址含义在内核运转过程中,涉及三种地址的转换。其中包括,逻辑地址、线性地址和物理内存地址。其定义如下(ps: 注意了,都是针对保护模式而言)逻辑地址逻辑地址(Logical Address)是指由程序产生的与段相关的偏移地址部分。在 Intel 保护模式下即是指程序执行代

2022-03-27 13:03:41 1581

原创 Linux内核学习系列(3)——任务(进程)创建

前言上一篇通过时间片轮转进一步了解了中断机制及其应用,并开始接触任务切换、TSS等相关内容。为了对任务管理(或者说进程管理)有个大体的认识,本章主要探究一下Linux内核中,任务创建相关内容。任务创建学过C语言的都知道,在当前进程中执行fork(),能够创建一个新进程。那么Linux内核中,第一个任务0(进程)又是如何被创建的呢?很自然地可以想到,任务0的创建与fork()创建过程定有不同。对此,本章主要对于这两种方式进行说明。forkfork()的原理其实很简单,主要就是将父进程的内容复制一份到

2022-03-26 14:26:18 2530

原创 Linux内核学习系列(2)——时间片轮转与任务切换

前言上一篇文章,通过系统调用引出了中断以及分段机制和保护模式相关内容,并进行了简单说明。本篇将在中断基础上,从时间片轮转和任务切换的角度切入,进一步地探究linux内核。时间片轮转时间片轮转是操作系统实现并发的方式,简单地说,操作系统为每个进程分配一定的时间片,当前进程的时间片用尽,则切换到下一个就绪进程进行执行。实现时间片轮转的方式很容易想到,根据上一篇举例的时钟中断,我们可以设计实现如下设置定时芯片隔N毫秒发出时钟中断设计时钟中断的执行程序完成以下步骤获取当前任务结构将当前任务时间片-

2022-03-25 22:31:59 2727

原创 Linux内核学习系列(1)——系统调用

前言由于工作需要,个人从java栈转为了c语言栈,并需要深入学习linux内核。本系列记录一些个人学习笔记。由于Linux内核涉及内容以及知识点很多,一开始接触十分痛苦,通过反复阅读《Linux内核完全注释》一书才逐渐能够看懂源码。在理解的过程中,个人发现自上而下地探索内核,才是最适合自己的学习内核的方式。因此,本系列主要从自上而下的角度,进行笔记记录。整个系列配图及概念描述将直接引用《Linux内核完全注释》中的内容。系统调用本系列第一篇从系统调用开始,因为在学习的过程中,个人发现系统调用能够将各个

2022-03-25 15:51:29 2525

原创 分段机制与分页机制

前言根据《Linux内核完全注释》做的笔记目的是理清下述关键词的含义及相互联系,最终彻底理解分段机制与分页机制分段机制段描述符段选择符段寄存器GDT 全局描述符表LDT 局部描述符表分页机制页表TLB 转换查找缓冲区逻辑地址线性地址物理地址概念简述段描述符记录段信息,包括权限、基地址等段选择符理解为索引,从描述符表中定位到唯一的段描述符因此,段寄存器只需要存段选择符,就能根据段寄存器找到段描述符从而获得段基址段基址寄存器存放段基址如 L

2022-03-21 15:30:31 528

原创 LiteOs Studio调试LiteOs无法进行

问题描述笔者根据 https://bbs.huaweicloud.com/blogs/222197 进行LiteOs的学习,烧录一切正常。但调试的过程中,程序无法正常运行,输出如下错误而正常的烧录运行结果如下问题解决考虑到是多线程一起调试造成的os死锁,考虑将线程数减为1试了很多方法,如下方法有效打开lunch.json添加如下图红框所示内容此时,调试控制台中执行的命令为完成了对默认 -smp 4这一参数的覆盖此时调试便是单线程,能够顺利执行...

2022-01-07 12:42:00 800

原创 2021个人秋招经验分享(算法转研发)

本人情况,普通211本硕,今年四月找算法实习四处碰壁后下决心转研发,无实习无项目经历,截止目前已经收到字节,大华,贝壳的意向,其他的还有阿里(待意向),海康(待意向),华为(待主管面),被pass的也有很多,拼多多(hr面挂),腾讯(2面挂),以及数不清的中小厂二面挂,如有赞,滴滴等打算将这五个月认识到的问题以及求职的方法做一个总结,并分享给大家,希望能帮助到和我一样想要转岗的人关于转岗原因,每个人都有每个人的想法,我主要分享一下为了转岗我做了哪些事情首先,是准备阶段。考虑到我没有实习没有项目,在项

2021-09-15 15:40:31 766 11

原创 ThreadLocal原理浅析

前言记录一下ThreadLocal的底层实现使用Web开发中,我们常常会配置一个拦截器对请求进行拦截,并判断对应session是否已经登录,若登录将用户信息放入ThreadLocal中,便于当前线程在后续执行Controller方法时能够继续使用实现拦截器@Componentpublic class AuthInterceptor implements HandlerInterceptor{ // 新建 ThreadLocal public static ThreadLocal&lt

2021-08-26 16:55:09 89

原创 jdkSPI机制浅析

前言面试常会问到javaSPI与dubboSPI区别,关于dubbo的SPI已经做过浅析,现在对javaSPI做梳理问题javaSPI缺点?一次性加载,如何一次性加载?加载完如何获得想要的实现类javaSPI加载哪个路径javaSPI主要解决什么问题使用使用上,通过ServiceLoader.load(Interface.class)就能够加载目录为META-INF/services下关于Interface.class定义的实现类,通过遍历迭代器的方式遍历每个实现类,再根据需要对实现类进行

2021-08-19 09:52:21 128

原创 Netty源码浅析

前言到目前为止,已经看了不少框架的源码了,但如何对源码做笔记并写博客,不同框架适合的做笔记方式也不同对于Netty框架而言,我认为以对比学习+回答问题的方式来浅析源码会好一些,因为Netty本质上也就是基于NIO的一个非阻塞网络编程框架,那么本篇笔记就对比一个简单的NIO非阻塞网络编程,看看Netty是如何实现的流程解析看Netty源码主要就是去看其如何实现Reactor模式的,关于Reactor的演变,在《初识Netty》中我做了对比,不进行赘述本节主要比较一下NIO的传统非阻塞方式与Netty

2021-06-28 20:43:05 516 1

原创 Netty之多任务版Guarded Object模式

前言Netty作为RPC框架的常用底层实现,Consumer每次发送请求给Provider,如何第一时间获取到服务器返回的结果呢?netty本身只实现了连接建立与监听的异步机制,要想实现这一点,得使用FutureDemo介绍搞了个小Demo,测试了一下多任务Guarded Object模式在netty网络编程的应用,并测试一下Channel与Boostrap以及EvenGroup的关系Provider.javapackage com.hx;import io.netty.bootstrap.S

2021-06-23 22:15:39 146 2

原创 Dubbo源码浅析之SPI机制

前言Dubbo通过ExtensionLoader实现了SPI机制,给整个框架提供了扩展性,看源码过程中发现用了很多次synchronized以及ConcurrentHashMap,粗略思考了原因,记录下这篇笔记使用简单说一下使用Serialization serialization = ExtensionLoader.getExtensionLoader(Serialization.class).getExtension("kryo");上述代码加载了下述路径下对应文件中由kryo指向的com.

2021-06-23 11:19:55 156 1

原创 java随笔之JVM内存模型与类加载

前言结合类加载与对象创建来梳理一下JVM内存模型的功能JVM内存模型图片来自 (https://www.cnblogs.com/zhou-yuan/p/14252272.html)随着JDK的升级,方法区最后被淘汰,常见面试题主要以JDK1.7的模型为主淘汰原因是对方法区的GC不好控制,内存也不好管理,容易OOM功能以JDK1.7为例绿色为线程共享,蓝色为线程独占程序计数器:记录线程执行到哪里,便于线程被唤醒后能够继续接着执行虚拟机栈:执行java方法的栈区,其由多个栈帧组成,每调一

2021-06-18 10:42:23 83

原创 java源码浅析之HashMap(1.7)

前言为了与1.8的HashMap进行比较,阅读并记录一下1.7的HashMap,但由于已经是过期的产物,就不看得太细源码初始化 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 static final int MAXIMUM_CAPACITY = 1 << 30; static final float DEFAULT_LOAD_FACTOR = 0.75f; p

2021-06-17 18:35:00 128

原创 java源码浅析之HashMap(1.8)

前言了解一下HashMap的底层实现,强调一下是1.8的源码初始化三种构造方式 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 static final int MAXIMUM_CAPACITY = 1 << 30; static final float DEFAULT_LOAD_FACTOR = 0.75f; int threshold; publi

2021-06-17 11:03:02 130 1

原创 java源码浅析之ArrayList扩容机制

前言准备面试,从源码上看一下ArrayList的扩容机制扩容机制底层结构就是一个Object数组,那么意味着对其初始化一个固定长度后,就不能改变了,要想改变,只能复制到新的数组中初始化三种方式,只需要了解前两种 private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transie

2021-06-16 22:09:00 114

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除