操作系统基础(1)
操作系统基础
0. 操作系统历史
1. 手工操作阶段
用户把程序写在纸带上(其实就是在纸带上打孔) -> 输入到计算机中 -> 计算机处理这个程序 -> 把输出结果又放在纸带中(其实还是打孔) -> 向用户展示用户
缺点:用户独占全机,速度慢导致全局资源利用率极低
2. 批处理阶段
2.1 单道批处理阶段
多个用户把程序写在纸带上 -> 通过外围机录入到磁带中 -> 监督程序会控制程序的输入 -> 计算机处理这些个程序( 每次只允许计算机内存中存在一道用户程序 ) -> 监督程序控制程序的输出 -> 向用户展示
特征:自动性,单道性,封闭性
缺点:系统资源利用率虽然有提升,但还是不充分,外设与 CPU 交替忙碌和空闲,CPU 有大量时间在等待 I/O 运行完成
2.2 多道批处理阶段(操作系统正式诞生)
每次往计算机内存中输入多道程序,并引入了中断技术,由操作系统负责管理这些程序的并发运行
过程:和单道批处理系统差不多,只不过是可以向计算机内存中调入多道程序,让他们在计算机内存中并发运行
优点:多道程序并发运行,共享计算机资源,资源利用率大幅提升
缺点:用户响应时间长,没有人机交互功能( 用户提交任务后只能等待计算机完成处理,中间不能控制自己任务的执行 )
3. 分时操作系统
计算机以时间片为单位轮流为各个 用户/任务 服务(时间片轮转),各个用户可通过终端与计算机进行交互( 时间片就是一个很短的时间,比如50ms )
优点:用户请求可以被即时响应,解决了人机交互问题,允许多个用户使用一台电脑,并且用户对计算机的操作相互独立,感受不到别人的存在
缺点:不能优先处理一些紧急任务,操作系统对各个 用户/任务 都是完全公平的,循环地为每一个 用户/任务 服务一个时间片,不区分任务的紧急性
响应时间:用户数 * 时间片
时间片:切换时间 + 处理时间
4. 实时操作系统
在实时操作系统的控制下,计算机系统接收到外部信号后能及时进行处理,并且要在严格的时限内处理完事件
特点:及时性,可靠性
优点:能优先响应一些紧急任务,某些紧急任务不需要时间片排队
实时操作系统也分为两类:
- 硬实时系统(必须在绝对严格的规定时间内完成处理,如导弹控制系统、自动驾驶系统)
- 软实时系统(能偶尔违反时间规定,如12306火车订票系统,有时候票的余量是不准确的,但是也不妨碍系统的正常运行)
5. 其它操作系统
- 网络操作系统:网络操作系统伴随着计算机网络的发展而诞生,能把网络中的各个计算机有机地结合起来,实现数据传输等功能,实现网络中各种资源的共享(如文件共享)以及各台计算机之间的通信
- 分布式操作系统:主要特点是分布性和并行性,系统之中的各台计算机地位相同,任何工作都可以分布在这些计算机上,他们并行、协同完成这个任务
- 个人操作系统: 如 Windows XP,windows 7,10,macOS,方便个人使用
- 嵌入式操作系统:具有良好的可移植性
- 微机操作系统:在微型计算机上安装的操作系统
1. 计算机组成
现代计算机设备主要有五个部分组成:运算器、控制器、存储器、输入设备、输出设备
CPU 是中央处理器,是控制和运算器,是一台计算机的运算核心和控制核心;存储器的主要功能是存储程序和各种数据;输入输出设备我们一般统称 I/O( Input/Output ),用于连接计算机内外部,而这些组件的连接则需要总线和主板来完成
1. CPU
cpu = 运算器 + 控制器
中央处理器 (Central Processing Unit, CPU),CPU 为一个具有特定功能的芯片, 里头含有微指令集,如果你想要让主机进行什么特异的功能,就得要参考这颗 CPU 是否有相关内置的微指令集才可以
由于 CPU 的工作主要在于管理与运算,因此在 CPU 内又可分为两个主要的单元,分别是: 算数逻辑单元与控制单元
算数逻辑单元主要负责程序运算与逻辑判断
控制单元则主要在协调各周边元件与各单元间的工作
1.1 运算器
负责运算从内存读取到寄存器中的数据,可以理解为一个数据加工厂,就是对寄存器中的数据做运算,这些运算包含基本的算术和逻辑运算
1.1.1 算数逻辑单元(ALU)
这个是运算器中重要的一个组成部分,主要负责的就是对数据的处理,从而实现对数据的算术和逻辑运算
基本功能为:加减乘除(四则运算)、与或非异(逻辑操作)以及移位,求补等操作
1.1.2 累计寄存器(AC)
通常叫做累计器,是一个通用寄存器,当 ALU 处理完算术运算或者逻辑运算后,会得到一个结果,这个结果就会保存在 AC 中
1.1.3 数据缓冲寄存器(DR)
这个 DR 也是存放数据的,存放的主要是从内存读取过来的数据,会暂时存放在这个 DR 中,这里就相当于内存和 CPU 之间做数据传送的一个中转站,因为 CPU 和内存的速度差别很大,中间引入一个 DR,一定程度上起到了速度上缓冲作用
1.1.4 状态条件寄存器(PSW)
主要负责保存由算术指令和逻辑指令运行或者测试的结果建立的各种条件码内容,一般分为状态标志和控制标志
1.2 控制器
可以控制整个 CPU 的工作,对于控制器,一般能够保证程序正确的执行以及能够处理异常事件
1.2.1 指令寄存器(IR)
主要是来保存指令的,CPU 要执行一条指令,就需要先把指令从内存上读取,然后放到我们上面说的数据缓冲寄存器中,之后再从 DR 放到 IR 中,然后会有一个叫做指令译码器的东西根据 IR 的内容生成很多的微操作指令,从而去控制其他部件完成相应的功能
1.2.2 程序计数器(PC)
它保存着下一条要执行的指令,一个程序要执行时,会把这个程序的起始地址存放到这个 PC 中,如果执行结束,CPU 会自动修改,使得 PC 存放下一条执行的指令
1.2.3 地址寄存器(AR)
它保存着 CPU 访问内存上的哪一块内存的内存地址,因为 CPU 和内存速度相差比较大,所以 CPU 要保存着正在访问的那块内存的地址,直到内存的读写操作完成,不然找不到之前访问的是哪个内存单元
1.2.4 指令译码器(ID)
指令分析,看看这个指令做什么的
1.3 寄存器
其实 CPU 大部分都是寄存器,上面说了 CPU 分量大部分:运算器和控制器,其实无论是运算器还是控制器,都离不开寄存器,可以说,CPU 就是寄存器的天下,我们使用 JAVA Python 这些都是高级语言,编写的程序最终都会转化成机器语言,之后就会进去 CPU 运算,实质就是通过 CPU 内部的寄存器来做相应的处理的
寄存器根据功能来分类,大致分为如下几种:
种类 | 功能 |
---|---|
累加寄存器(accumulator register) | 存储执行运算的数据和运算后的数据 |
标志寄存器(flag register) | 存储运算处理后的 CPU 状态 |
程序计数器(program counter) | 存储下一条指令所在的内存地址 |
基址寄存器(base register) | 存储数据内存的起始地址 |
变址寄存器(index register) | 存储基址寄存器的相对地址 |
通用寄存器(general purpose register) | 存储任意数据 |
指令寄存器(instruction register) | 存储指令,CPU 内部使用,无法通过程序对该寄存器进行读写操作 |
栈寄存器(stack register) | 存储栈区域的起始地址 |
另外,可以把 CPU 理解为寄存器的合体,然而,程序计数器、累加寄存器、标志寄存器、指令寄存器和栈寄存器只能有一个,其他寄存器一般会有多个
1.3.1 程序计数器
为什么要单独说这个呢,因为这个决定了程序的执行流程
程序计数器数值的变化 | 地址 | 内存中的内容 |
---|---|---|
0100 | 0100 | 指令:将 0105 地址的数值保存在累加寄存器中 |
0101 | 0101 | 指令:将 0106 地址的数值保存在通用寄存器中 |
0102 | 0102 | 指令:将累加寄存器的值和通用寄存器的值相加 |
0103 | 0103 | 指令:将累加寄存器的值显示在显示器上 |
0104 | 0104 | 指令:结束程序(返回操作系统) |
0105 | 数据:123 | |
0106 | 数据:456 |
首先就是一个指令,表明要把 a 这个数值做相加操作,需要先把 a 保存到累加寄存器,而这个指令被保存在内存中的一个内存单元中,这个内存地址比如说就是 0100,而此时程序计数器就是保存的这个 0100 内存地址,要执行程序的时候,CPU 去看看程序计数器保存的第一个地址,发现是 0100,然后去内存这个 0100 的位置,看看保存的是啥,一看是一个指令,分析指令,原来是要把 0105 地址中的值保存到累加寄存器中,以此类推,从上到下执行
1.4 CPU 指令执行过程
- 取指令:从内存中取出指令
- 指令译码:翻译指令
- 执行指令
- 访问取数:0100
- 结果写回
1.5 CPU 指令集
分为精简指令集( RISC )和复杂指令集( CISC )
精简指令集( Reduced Instruction Set Computer, RISC ):这种 CPU 的设计中,微指令集较为精简,每个指令的执行时间都很短,完成的动作也很单纯,指令的执行性能较佳; 但是若要做复杂的事情,就要由多个指令来完成
复杂指令集( Complex Instruction Set Computer, CISC ):与 RISC 不同的,CISC 在微指令集的每个小指令可以执行一些较低阶的硬件操作,指令数目多而且复杂, 每条指令的长度并不相同,因为指令执行较为复杂所以每条指令花费的时间较长, 但每条个别指令可以处理的工作较为丰富,常见的 CISC 微指令集 CPU 主要有 AMD、Intel、VIA等的 x86 架构的 CPU,由于 AMD、Intel、VIA 所开发出来的x86架构CPU被大量使用于个人电脑(Personal computer)用途上面, 因此,个人电脑常被称为x86架构的电脑
1.6 CPU位数
为何称为 x86 架构呢? 这是因为最早的那颗 Inte l 发展出来的 CPU 代号称为 8086,后来依此架构又开发出 80286, 80386...,因此这种架构的 CPU 就被称为 x86 架构了。在 2003 年以前由 Intel 所开发的 x86 架构 CPU 由 8 位升级到 16、32 位,后来 AMD 依此架构修改新一代的 CPU为 64 位, 为了区别两者的差异,因此 64 位的个人电脑 CPU 又被统称为 x86_64 的架构,所谓的位指的是 CPU 一次数据读取的最大量,64 位 CPU 代表 CPU 一次可以读写 64bits 这么多的数据,32 位 CPU 则是 CPU 一次只能读取 32 位的意思。 因为CPU读取数据量有限制,因此能够从内存中读写的数据也就有所限制,所以,一般 32 位的 CPU 所能读写的最大数据量,大概就是 2^32,4GB左右,64 位就是 2^64
1.7 CPU 缓存
CPU 缓存位于 CPU 与内存之间的临时存储器,它的容量比内存小但交换速度快
CPU 缓存的出现主要是为了解决 CPU 运算速度与内存读写速度不匹配的矛盾,因为 CPU 运算速度要比内存读写速度快很多,这样会使 CPU 花费很长时间等待数据到来或把数据写入内存,在Cache 中的数据是内存中的一小部分,但这一小部分是短时间内 CPU 即将访问的,当 CPU 调用大量数据时,就可避开内存直接从 Cache 中调用,从而加快读取速度,由此可见,在 CPU 中加入 Cache 是一种高效的解决方案,这样整个内存储器( Cache + 内存 )就变成了既有 Cache 的高速度,又有内存的大容量的存储系统了。
按照数据读取顺序和与 CPU 结合的紧密程度,CPU 缓存可以分为一级缓存,二级缓存,部分高端 CPU 还具有三级缓存,每一级缓存中所储存的全部数据都是下一级缓存的一部分,这三种缓存的技术难度和制造成本是相对递减的,所以其容量也是相对递增的,当 CPU 要读取一个数据时,首先从一级缓存中查找,如果没有找到再从二级缓存中查找,如果还是没有就从三级缓存或内存中查找,一般来说,每级缓存的命中率大概都在 80% 左右,也就是说全部数据量的 80% 都可以在一级缓存中找到,只剩下 20% 的总数据量才需要从二级缓存、三级缓存或内存中读取,由此可见一级缓存是整个 CPU 缓存架构中最为重要的部分
1.8 频率
例如 3.6GZh 的 CPU 就表示这颗 CPU 在一秒内可以运行 3.6x109 次工作(每次工作都可以进行少数指令集)
但是不是所有 CPU 都以评率来判断运行效率的
1.8.1 外频和倍频
外频:指的是 CPU 与外部单元进行数据传输时的速度
倍频:指的是 CPU 内部用来加速工作性能的一个倍数
两者相乘才是 CPU 的频率速度,如:内频为 3.0GHz,外频 333MHz,因此倍频就是 9 倍
超频一般都是把外频改高
2. 存储器
存储器分内存储器和外存储器,外存主要是 U 盘、硬盘等,一般意义上我们会讲外存归类于 I/O 设备。所以这里的存储器我们仅仅指的是内存
内存是计算机中重要的部件之一,它是与 CPU 进行沟通的桥梁,是 CPU 能直接寻址的存储空间。我们平常使用的程序,如 Windows 操作系统、打字软件、游戏软件等,一般都是安装在硬盘等外存上的,但仅此是不能使用其功能的,必须把它们调入内存中运行,才能真正使用其功能,我们平时输入一段文字,或玩一个游戏,其实都是在内存中进行的。就好比在一个书房里,存放书籍的书架和书柜相当于电脑的外存,而我们工作的办公桌就是内存。通常我们把要永久保存的、大量的数据存储在外存上,而把一些临时的或少量的数据和程序放在内存上
内存一般采用半导体存储单元,包括随机存储器(RAM),只读存储器(ROM),以及高速缓存(CACHE)
ROM 表示只读存储器(Read Only Memory),在制造 ROM 的时候,信息(数据或程序)就被存入并永久保存,这些信息只能读出,一般不能写入,即使机器停电,这些数据也不会丢失,ROM一般用于存放计算机的基本程序和数据,如 BIOS ROM
RAM 表示随机存储器(Random Access Memory)表示既可以从中读取数据,也可以写入数据,当机器电源关闭时,存于其中的数据就会丢失。我们通常购买或升级的内存条就是用作电脑的内存,内存条(SIMM)就是将 RAM 集成块集中在一起的一小块电路板,它插在计算机中的内存插槽上,以减少 RAM 集成块占用的空间
3. I/O
I/O(input/output),即 输入/输出 端口,每个设备都会有一个专用的 I/O 地址,用来处理自己的输入输出信息,硬盘就是通过 I/O 接口,把数据送到内存中供 CPU 处理的。
4. BUS
总线(Bus)是由导线组成的传输线束,它是 CPU、内存、输入、输出设备传递信息的公用通道,主机的各个部件通过总线相连接,外部设备通过相应的接口电路再与总线相连接,从而形成了计算机硬件系统
4.1 按照功能分类
4.1.1 数据总线
用于传送数据信息,数据总线是双向三态形式的总线,即他既可以把 CPU 的数据传送到存储器或 I/O 接口等其它部件,也可以将其它部件的数据传送到 CPU,需要指出的是,数据的含义是广义的,它可以是真正的数据,也可以是指令代码或状态信息,有时甚至是一个控制信息,因此,在实际工作中,数据总线上传送的并不一定仅仅是真正意义上的数据,常见的数据总线有PCI 等
计算机中的记忆元件由触发器组成,而触发器只有两个状态。即“0”态和“1”态,所以每条信号线上只能传送一个触发器的信息。如果要在一条信号线上连接多个触发器,而每个触发器可以根据需要与信号线连通或断开,当连通时可以传送“0”或“1”,断开时对信号线上的信息不产生影响,就需要一个特殊的电路加以控制,此电路即为三态输出电路,又称为三态门
4.1.2 地址总线
是专门用来传送地址的,由于地址只能从 CPU 传向外部存储器或 I/O 端口,所以地址总线总是单向三态的,这与数据总线不同,地址总线的位数决定了 CPU 可直接寻址的内存空间大小,比如 8 位微机的地址总线为 16 位,则其最大可寻址空间为 2^16=64KB,16 位微型机的地址总线为 20 位,其可寻址空间为 2^20=1MB,一般来说,若地址总线为 n 位,则可寻址空间为 2^n 字节
为什么32位操作系统最大只能支持 4G 内存?
32 位代表地址总线位宽,总线的位宽指的是总线能同时传送的二进制数据的位数,而地址总线的位宽决定了 CPU 可直接寻址的内存空间大小,由于二进制每个位寻址只有 0、1 两种,所以 32 位操作系统的寻址空间就是 2^32=4GB
4.1.3 控制总线
用来传送控制信号和时序信号,控制信号中,有的是微处理器送往存储器和 I/O 接口电路的,如 读/写 信号,片选信号、中断响应信号等;也有是其它部件反馈给 CPU 的,比如:中断申请信号、复位信号、总线请求信号、设备就绪信号等,因此,控制总线的传送方向由具体控制信号而定,一般是双向的,控制总线的位数要根据系统的实际控制需要而定
4.2 按照层次分类
4.2.1 内部总线
在 CPU 内部,寄存器之间和算术逻辑部件 ALU 与控制部件之间传输数据所用的总线称为片内总线(即芯片内部的总线),如 I2C 总线、SPI 总线、SCI 总线等
4.2.2 系统总线
又称内总线或板级总线,是微机中各插件板与系统板之间的总线,用于插件板一级的互联,因为该总线是用来连接微机各功能部件而构成一个完整微机系统的,所以称之为系统总线,人们平常所说的微机总线就是指系统总线,如 ISA 总线、PCI 总线等
4.2.3 外部总线
是计算机和外部设备之间的总线,如IDE总线、USB总线、SCSI总线等
4.3 按照传输方式分类
4.3.1 串行总线
所有信号复用一对信号线,串行总线通信速率比较低,但在数据通信量不是很大的微处理器电路中,显得更加便捷、灵活。如USB总线、SPI总线、I2C总线等
4.3.2 并行总线
每个信号都有自己的信号线,并行总线通信速度快,实时性好,但由于占用的口线多,成本上会有所增加。如 ISA 总线、PCI 总线等
2. 计算机系统层次
- 第一级是微程序级,这级的机器语言是微指令集,程序员用微指令编写的微程序,一般是直接由硬件直接执行的
- 第二级是传统机器级,这级的机器语言是该机的指令集,程序员用机器指令编写的程序可以由微程序进行解释
- 第三级是操作系统级,从操作系统的基本功能来看,一方面它要直接管理传统机器中的软硬件资源,另一方面它又是传统机器的延伸
- 第四级是汇编语言级,这级的机器语言是汇编语言,完成汇编语言翻译的程序叫做汇编程序
- 第五级是高级语言级,这集的机器语言就是各种高级语言,通常用编译程序来完成高级语言翻译的工作
不同层级的程序设计都是由不同的语言来完成了,越低级的语言越接近机器,越高级的语言越接近人类,所以我们大多数应用程序员都工作在高级语言级。而操作系统可以看做是一个中间层,起到机器语言和人类自然语言承上启下的作用,所以操作系统非常重要
3. 操作系统
操作系统是系统软件,其设计的目标:方便性,可靠性,操作系统是对计算机资源进行管理的软件
在一定时间内、在一定条件下无故障地执行指定功能的能力或可能性。可通过可靠度、失效率、平均无故障间隔等来评价产品的可靠性。
3.1 操作系统分类
- 批处理操作系统:单道、多道
- 分时操作系统:Unix
- 实时操作系统:MsgOS
- 网络操作系统
- 分布式操作系统
- 微机操作系统
- 嵌入式操作系统
3.2 操作系统的四个特征
- 并发
指多个事件在同一时间间隔内发生,宏观上表现的是同时发生,微观上交替发生
并行:多个事件同一时刻同时发生
- 共享
系统中的资源可供内存中多个并发的进程同时使用
共享分类:互斥共享:一个时间段内只允许一个进程访问(打印机)
同时共享:一个时间段内允许多个程序同时访问(访问硬盘)
并发和共享互为存在条件
- 虚拟
物理上真实存在的实体映射为多个不存在的逻辑上的若干对应物
空分复用技术:虚拟存储技术
时分复用技术:虚拟处理技术(单核 CPU 处理多任务,微观上处理机在各个微小的时间段内交替着为各个进程服务)
- 异步
在多道程序环境下,允许多个程序并发,但由于资源有限,进程的执行并不是一贯到底,而是走走停停,以不可预知的进度向前推行
没有并发和共享,虚拟和异步就谈不上,还有并发和共享是最基本的特征,且互为存在条件
3.3 操作系统组成
3.3.1 内核(kernel)
内核,是一组程序模块,是操作系统的核心,从不同的角度来看,内核担任的角色不同,从纯技术角度来看,内核只是软件和硬件的一个中间层,它把从软件发来的请求发送给硬件,完成寻址等操作,还充当了底层驱动。从应用程序角度来看,内核是对硬件的一个高层次的抽象,应用程序与硬件没有联系,只与内核有联系,内核是应用程序知道的最底层。从多个并发的进程的角度来看,内核是一个资源管理器,它完成对进程的切换,调度,共享计算机资源(CPU,内存,磁盘,网络等)。还可以把内核看成一个库,通过系统调用向内核发送各种请求
内核的功能:
- 中断处理:中断处理是内核中最基本的功能,也是操作系统赖以活动的基础
- 时钟管理:时钟管理是内核的基本功能
- 短程调度:短程调度的职责所在是分配处理器,按照一定的策略管理处理器的转让,以及完成保护和恢复现场工作
- 原语管理:原语是内核中实现特定功能的不可中断过程
3.3.2 系统调用(system calls)
在 CPU 的所有指令中,有一些指令是非常危险的,如果所有的程序都能使用这些指令,一旦错用指令将导致整个系统崩溃,比如:清内存、设置时钟等,所以,CPU 将指令分为特权指令和非特权指令,对于那些危险的特权指令,只允许操作系统的内核使用,普通的应用程序只能使用那些不会造成灾难的非特权指令
但是一个应用程序肯定是特权指令和非特权指令都需要使用的,这时怎么办?
操作系统将程序的运行空间分为内核空间和用户空间(即内核态和用户态),他们分别运行在不同的级别上,逻辑上是相互隔离的,用户进程在通常情况下是不允许访问内核数据的,它也就无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间的函数,Intel 的 CPU 将特权级别分为4个级别:RING0,RING1,RING2,RING3,Linux 使用了 Ring3 级别运行用户态,Ring0 作为内核态,没有使用 Ring1 和 Ring2,Ring3 状态不能访问 Ring0 的地址空间,包括代码和数据,而系统调用正是操作系统向用户程序提供支持的接口,系统调用把应用程序的请求传给内核,调用相应的的内核函数完成所需的处理,将处理结果返回给应用程序。注意,系统调用并非每一个时刻都会发生,只有那些特权指令才需要通过系统调用接口去请求内核。而一些非特权指令并不需要调用内核而直接可以运行在CPU上。
3.3.3 函数库调用(library calls)
系统调用虽然是内核和用户应用程序之间的沟通桥梁,是用户应用程序访问内核的入口点,但通常情况下,系统调用并不直接和程序员打交道,它仅仅是一个通过软中断机制(X86中的int 0x80)向内核提交请求以获取内核服务的接口。应用程序是通过操作系统提供的应用编程接口(API)而不是直接通过系统调用来编程。操作系统API的主要作用是把操作系统的功能完全展示出来,提供给应用程序,基于该操作系统,与文件、内存、时钟、网络、图形、各种外设等互操作的能力。此外,操作系统API通常还提供许多工具类的功能,比如操纵字符串、各种数据类型、时间日期等。
在Linux中,API遵循了在UNIX中最流行的应用编程界面标准——POSIX标准,该标准基于当时现有的UNIX 实践和经验,描述了操作系统的系统调用编程接口(即API),用于保证应用程序可以再源代码一级多种操作系统上移植运行,这些系统调用编程接口主要是通过 C 库(libc)实现的。库函数(library function)是由用户或组织自己开发的,具有一定功能的函数集合,一般具有较好平台移植性,通过库文件(静态库或动态库)向程序员提供功能性调用。程序员无需关心平台差异,由库来屏蔽平台差异性。
C库(libc)提供了POSIX的绝大部分API,同时,内核提供的每个系统调用在C库中都具有相应的封装函数。系统调用与其C库封装函数的名称常常相同,比如,read系统调用在C库中的封装函数即为read函数。实际上,从用户的角度看,系统调用和C库之间的区别并不重要,他们只需通过C库函数完成所需功能。相反,从内核的角度看,需要考虑的则是提供哪些针对确定目的的系统调用,并不需要关注它们如何被使用
希望这篇文章能对你有帮助哦~