【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

这篇具有很好参考价值的文章主要介绍了【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


基于本人观看学习 哈工大李治军老师主讲的操作系统课程 所做的笔记,仅进行交流分享

特此鸣谢李治军老师,操作系统的神作!

如果本篇笔记帮助到了你,还请点赞 关注 支持一下 ♡>𖥦<)!!

主页专栏有更多,如有疑问欢迎大家指正讨论,共同进步!

给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ

第二章 进程与线程 (上)


更多操作系统笔记:【哈工大李治军老师】操作系统笔记专栏汇总

如果有需要markdown或者PDF可以私信我,可以在此基础上自己添加补充笔记(●’◡’●)



课程链接:

b站: 【哈工大】操作系统 李治军(全32讲)

大学MOOC:大学慕课—操作系统—主讲:哈工大李治军



一、CPU管理的直观想法

操作系统在管理CPU时引入了一个多进程图像,通过多进程图像操作系统管理CPU

多进程图像是操作系统的核心图像。

1.CPU的工作原理

  1. 取指:CPU 从内存中读取指令,并解析指令中的操作码和操作数等信息。
  2. 执行:根据指令中的操作码和操作数,CPU 进行相应的逻辑或算术运算,并将结果保存到寄存器或内存中。
  3. 写回:如果结果需要保存到内存中,则将结果写入内存,或者将结果保存到寄存器中供后续指令使用。
  4. 重复:不断重复上述步骤,直到程序执行结束。
  1. 取指:CPU 从内存中读取指令,并解析指令中包含的操作码和操作数等信息。

  2. 执行:根据指令中的操作码和操作数,CPU 进行相应的逻辑或算术运算,并将结果保存到寄存器或内存中。

  3. 写回:如果结果需要保存到内存中,则将结果写入内存;否则,将结果保存到寄存器中供后续指令使用。

  4. 重复:不断重复上述步骤,直到程序执行结束。

    【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

设置PC初始值,操作系统会自动取指执行

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

有IO指令和没有IO指令执行时间之比:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

可以看出IO执行特别慢,CPU利用率非常低

如何解决这个问题?

在等待IO过程中,CPU切出去执行其他程序,让CPU充分忙碌起来,提高了利用率

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

2.多道程序,交替执行

举例单道程序和多道程序CPU利用率对比:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

这种多道程序同时出发,交替执行就是并发

CPU应该工作的样子:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

3.如何做到并发?

对于切换程序执行,因为ax,bx改变了,只修改寄存器PC不能找到原来程序执行到哪里。

在切出程序后,原来的程序执行到哪里,执行的样子 需要记录下来,切回时才能继续执行

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

由此看出 运行的程序和静态程序不一样:运行的程序需要记录运行后的时刻

4.进程

进程:刻画运行中的程序 一个运行中的程序就是一个进程

进程有开始、有结束,程序没有

进程会走走停停,走停对程序无意义

进程需要记录ax,bx,…,程序不用

记录这些信息的数据结构就是:PCB

多个程序同时运行就是多进程,CPU进行管理。

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

PCB中记录了操作系统所需的,用于描述进程的当前情况以及控制进程运行的全部信息。

操作系统根据PCB来对并发执行的进程进行控制和管理。

二、多进程图像

1.多进程图像是操作系统的核心图像

从上层用户的角度看:共有三个进程PID:1 PID:2 PID:3

操作系统根据PCB合理推进执行多个进程

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

启动了的程序就是进程,所以是多个进程推进操作系统只需要把这些进程记录好、要按照合理的次序推进(分配资源、进行调度)这就是多进程图像。

我们每要解决一个任务,计算机都会创建一个进程来执行这个任务。 多进程图像是操作系统的核心图像

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

windows任务管理器中可以查看多个进程同时执行,操作系统通过管理进程实现对计算机的管理

2.多进程如何组织?

通过PCB形成一系列数据结构,操作系统推进进程的执行

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

3.进程状态图:

多进程的组织:PCB+状态+队列

运行->等待; 运行->就绪; 就绪->运行…

进程状态图能给出进程生存期的清晰描述,是认识操作系统进程管理的一个窗口。

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

挂起:挂起是指操作系统将某个正在运行的进程暂时停止执行,并将其状态保存到外部存储器或内存中,直到以后再次恢复该进程执行。是一种重要的管理和优化技术,它可以减少系统资源的浪费,提高系统的响应速度和可用性。

在进程挂起期间,进程的状态信息被保存到外部存储器或内存中,当操作系统需要重新启动该进程时,可以从保存的状态信息中还原该进程的上下文,继续执行。

主动挂起:是指进程自己向操作系统申请挂起自己的状态,通常是因为当前执行的任务已经完成,但该进程还需要保留下来,以便稍后根据需要进行继续执行。例如暂停操作、文件下载中暂停/继续操作等

被动挂起:是指操作系统强制挂起某个正在执行的进程,因为系统资源已经不足,或者该进程所请求的资源无法获得,此时操作系统会暂时停止该进程执行,以便其他进程可以更好地利用系统资源。

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

4.多进程如何交替?

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

5.多进程如何影响?

多个进程同时在存在于内存会出现下面的问题

进程1中地址100访问到了同时运行的进程2中的地址100:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

解决的办法:

限制对地址100的读写

多进程的地址空间分离: 内存管理的主要内容

为什么说进程管理连带内存管理形成多进程图像?

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

6.多进程如何合作?

队列中的进程放入和取出 多个进程交替执行

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

生产者—消费者实例

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

共享缓冲区:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

两个合作的进程都要修改counter:

counter语义错误:初始counter=5,进行加一减一应该还是五,但经过执行序列得到的counter为4,进程必须合理推进

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

核心在于进程同步:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

读写PCB,OS中最重要的结构,贯穿始终

三、用户级线程

1.资源切换(引出线程概念)

如何将资源不动而切换指令序列?

线程切换分为指令切换 和 资源切换 分治

进程 = 资源 + 指令执行序列

将资源和指令执行分开一个资源 + 多个指令执行序列

既有多个指令在同时触发交替执行,切换也不用那么复杂:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

线程: 保留了并发的优点,避免了进程切换代价

实质就是内存映射表不变而PC指针改变

多个执行序列+一个地址空间是否实用?

一个网页浏览器: 同时触发,交替执行:

一个线程用来从服务器接收数据

一个线程用来显示文本

一个线程用来处理图片(如解压缩)

一个线程用来显示图片

下载一段网页——显示文本——再回来下载图片…

这些线程要共享资源吗?

接收数据放在100处,显示时要读… 写入读出缓冲区

所有的文本、图片都显示在一个屏幕上

多线程表达的切换指令序列对进程切换很有价值。

2.线程切换

启动多个线程,每个线程执行函数

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

核心是Yield…

能切换了就知道切换时需要是个什么样子

Create就是要制造出第一次切换时应该的样子

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

如果继续往下执行跳转到另一个线程,因为两个线程共用了一个栈

每个指令序列中,函数调用应该用自己的栈!!!

从一个栈到两个栈

TCB任务控制块,包含了线程的信息,如寄存器状态、堆栈指针、优先级、资源占用等

TCB 在任务/线程切换时用于保存当前任务/线程的状态信息,以便后续再次恢复执行

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

(jmp 204;应该去掉)

ThreadCreate的核心就是用程序做出三样东西:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

多个程序出现在内存中执行,在程序执行的过程中调用Yield(),首先切换栈然后再弹栈,实现切换到另外一个线程执行,在Yield()…

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

3.用户级线程与核心级线程

如果进程的某个线程进入内核并阻塞

一旦一个线程阻塞,其他线程也将被阻塞,会切换到另一个进程执行

虽然通过用户级线程启动多个序列,但是CPU发生阻塞等待

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

核心级线程ThreadCreate是系统调用,会进入内核

TCB在内核中,内核中一个线程阻塞,将会切换到另一个线程执行

因此内核级线程的并发性更好

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

核心级线程完全由操作系统在内核中决定,比用户级线程复杂

四、内核级线程

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

和用户级相比,核心级线程有什么不同?

ThreadCreate是系统调用,内核管理TCB,内核负责切换线程

如何让切换成型? ——内核栈,TCB

用户栈是否还要用?执行的代码仍然在用户态,还要进行函数调用

一个栈到一套栈:两个栈到两套栈

1.用户栈和内核栈之间的关联

用户栈和内核栈是操作系统中两个重要的栈空间,它们分别用于存储用户态和内核态的执行环境

当一个进程从用户态切换到内核态时,需要使用到内核栈。进程从内核态返回用户态时,需要使用到用户栈

·所有中断(时钟、外设、INT指令)都引起上述切换

·中断(硬件)又一次帮助了操作系统…

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

用户栈和内核栈是操作系统中两个不同的栈区域,它们各自负责存储不同执行环境的栈帧信息

在处理器发生中断时,或者进程主动发起系统调用时,CPU会切换到内核态,并将当前进程的用户态上下文信息保存到它的用户栈中,然后切换到内核栈中继续执行相应的内核代码;当内核完成相应的处理逻辑后,再将内核栈中保存的内容弹出,恢复用户栈中保存的用户态上下文信息,最后切换回用户态继续执行用户进程。

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

2.开始内核中的切换:switch_to

switch to:仍然是通过TCB找到内核栈指针;然后通过ret切到某个内核程序;最后再用CS:PC切到用户程序

switch to也称为上下文切换,当多个任务或线程同时运行时,操作系统需要在它们之间进行快速的切换,实现并发执行

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

“五段论”保证了线程之间的正确切换和执行:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

用户级线程、核心级线程的对比:

用户级线程是由应用程序开发者创建和管理的线程,运行在用户空间,并通过用户程序调用系统调度库来实现线程的调度和同步

每个用户级线程都对应着一个内核级线程,内核级线程以操作系统内核的身份运行并提供线程与系统资源之间的映射

用户级线程实现简单,调度和同步开销小,缺点是不能利用多处理器环境提高性能

核心级线程是由操作系统内核创建和管理的线程,直接由内核调度器进行调度

与用户级线程相比,内核级线程有更好的并发性和CPU利用率,但比较复杂。

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

五、内核级线程实现

内核级线程需要采用复杂的数据结构来维护线程上下文切换和状态信息,并且需要在内核态进行线程切换

核心是让操作系统内核直接管理线程调度,对于上层应用程序来说,无需关心线程的创建、调度和同步等

先从用户栈到内核栈,再从内核栈到用户栈;核心级线程的两套栈,核心是内核栈。

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

1.int 0x80 fork( )

fork() 系统调用可以让程序生成一个新的独立进程,并在原进程和新进程中进行不同的操作

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

2.切换五段论中的中断入口和出口

中断入口: _system_call将用户态信息压栈

任务的运行状态如果不在就绪状态就执行调度,如果在就绪状态但counter等于0,也执行调度程序

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

中断出口:

sys_fork 函数调用了 fork() 系统调用,它会通过 eax 寄存器返回新创建进程的进程 ID,在此之前会先将该寄存器的值入栈保存

接着通过 cmp 判断是否需要进行进程调度,并将返回地址入栈后通过 jmp 指令跳转到 schedule 函数执行进程切换操作

在进程切换结束后,通过 pop 指令将原 eax 寄存器的值恢复回来fork() 调用的返回值

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

3.switch to

任务状态段Tss(Task State Segment)表示任务状态段的表项,用于获取相应进程的任务结构体的起始地址

跳转到所切换到的进程的任务结构体中保存的代码起始地址,从而实现进程上下文切换的操作:

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

Linux 0.11用tss切换,但也可以用栈切换,因为tss中的信息可以写到内核栈中

4.ThreadCreate

fork()使用 push 指令将寄存器 %gs 和 %eax 的值分别压入栈中

然后并调用_copy_process 函数创建新进程

·在 _copy_process函数中,使用给定的参数构造一个新进程的状态

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

这些参数全部传入 _copy_process函数设置上下文信息

然后函数返回到 sys_fork 函数并使用 add 指令来从栈中删除参数,最终 ret 返回

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

copy_process的细节:创建栈

调用 get_free_page 函数来动态申请一页大小的内存空间,并转换为一个指向PCB的指针 p

创建TCB——创建内核栈和用户栈——关联栈和TCB 子进程和父进程共享用户栈

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

copy_process的细节:执行前准备

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

5.如何执行我们想要的代码?

fork() 调用成功时,返回值为子进程的 PID 在父进程中。在子进程中,fork() 返回 0。

exec() 是一个系统调用,会执行 system_call

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

ex.a_entry是可执行程序入口地址,产生可执行文件时写入

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

内核级线程总结

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

六、操作系统的“树”(知识串讲总结)

前面知识的串联

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】

【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】


更多操作系统笔记:【哈工大李治军老师】操作系统笔记专栏汇总

第二章 下:操作系统第二章(下)学习笔记

文章来源地址https://www.toymoban.com/news/detail-431993.html

大家的点赞、收藏、关注将是我更新的最大动力!欢迎留言或私信建议或问题。 大家的支持和反馈对我来说意义重大,我会继续不断努力提供有价值的内容!

到了这里,关于【操作系统OS】学习笔记:第二章 进程与线程 (上)【哈工大李治军老师】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 操作系统(第5版罗宁 文艳军编著)第二章课后习题答案

            几种常见的中断包括: 输入/输出(I/O)中断:当计算机需要等待外部设备(如硬盘驱动器、键盘、鼠标)完成它们的任务时,它会发出I/O中断。 时钟中断:时钟中断是一个计时器发出的中断,它定期发生,以确保操作系统和其他软件程序能够在一定的时间内运行

    2024年02月08日
    浏览(47)
  • 计算机操作系统重点概念整理-第二章 进程管理【期末复习|考研复习】

    计算机操作系统复习系列文章传送门: 第一章 计算机系统概述 第二章 进程管理 第三章 进程同步 第四章 内存管理 第五章 文件管理 第六章 输出输出I/O管理 给大家整理了一下计算机操作系统中的重点概念,以供大家期末复习和考研复习的时候使用。 参考资料是王道的计算

    2024年02月08日
    浏览(59)
  • 【正点原子FPGA连载】第二章 安装Ubuntu操作系统 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Linux开发指南

    1)实验平台:正点原子RV1126 Linux开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id=692176265749 3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html 前面虚拟机已经创建成功了,相当于硬件已经准备好了,接下来就是要在虚拟机中安装Ubuntu系统了,首先

    2023年04月26日
    浏览(57)
  • 汽车电子笔记之:AUTOSA架构下的多核OS操作系统

    目录 1、AUTOSAR多核操作系统 1.1、OS Application 1.2、多核OS的软件分区 1.3、任务调度 1.4、核间任务同步 1.5、计数器、报警器、调度表 1.6、自旋锁与共享资源 1.7、核间通信IOC 1.8、OS Object中元素交互 1.9、多核OS的启动与关闭 2、多核OS注意事项 2.1、最小部署单元 2.2、核间通信及影

    2024年02月11日
    浏览(44)
  • 从零学习Linux操作系统 第二十二部分 企业域名解析服务的部署及安全优化

    关于dns的名词解释:dns: domain name service(域名解析服务) 关于客户端: /etc/resolv.conf dns指向文件 A记录 ##ip地址叫做域名的Address 记录 SOA ##授权起始主机 关于服务端 bind 安装包 named 服务名称 /etc/named.conf 主配置文件 /var/named 数据目录 端口 53 关于报错信息: 1.no servers could be reach

    2024年02月22日
    浏览(45)
  • JS深入学习笔记 - 第二章.类和对象

    3.1面向对象 这里顺带提一句学习JAVA时,老师说的面向对象和面向过程的区别: 面向过程:强调做什么事情,具体什么步骤。举个把大象放进冰箱的例子: 打开冰箱门 把大象放进冰箱 关上冰箱门 面向对象: 强调的是做动作的主体(称之为对象) 冰箱 :打开操作 冰箱 :放

    2024年02月08日
    浏览(52)
  • 王道操作系统学习笔记(1)——操作系统基本概念

    本文介绍了操作系统的基本概念,文章中的内容来自B站王道考研操作系统课程,想要完整学习的可以到B站官方看完整版。 操作系统:系统资源的管理者(处理机管理、存储器管理、文件管理、设备管理) 交互式命令(在终端中输命令)和批处理命令(Shell脚本) 并发: 宏

    2024年02月10日
    浏览(52)
  • dx12 龙书第二章学习笔记 -- 矩阵代数

    1.矩阵及其运算 矩阵的运算 :①加②减③标量乘法 ④矩阵乘法: 矩阵乘法要有意义的条件是矩阵A的列数和矩阵B的行数必须相同,所以一般不满足交换律 ⑤转置矩阵: ⑥矩阵行列式:det A 学习行列式的主要目的是:利用它推导出求逆矩阵的公式 方阵A是可逆的,当且仅当det

    2024年02月11日
    浏览(47)
  • 云计算学习笔记——第二章 虚拟化与容器

    1.什么是虚拟化    一种计算机资源管理技术 ,将各种IT资源抽象、转换成平一种形式的技术都是虚拟化技术。 2.作用   通过该技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机,每个计算机可运行不同的操作系统,并且应用程序都可以

    2024年01月22日
    浏览(54)
  • 【UnityShader入门精要学习笔记】第二章(3)章节答疑

    本系列为作者学习UnityShader入门精要而作的笔记,内容将包括: 书本中句子照抄 + 个人批注 项目源码 一堆新手会犯的错误 潜在的太监断更,有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 (PS:章节答疑不是我答,是原作者对一些比较容易产生困惑的地

    2024年02月03日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包